Fix GET request returning corrupted data for multi-chunk blobs
Four bugs caused data mismatches between PUT and GET operations: 1. chunk_store_exists() used file_open (O_CREAT) to check if a chunk file exists, which created empty files as a side effect. Servers would then read from these empty files and return uninitialized heap memory as chunk data. Fixed by using file_exists (access()) instead. 2. Server's process_fetch_chunk checked `ret < 0` after chunk_store_read, missing the EOF case (ret == 0) from empty files. Changed to `ret <= 0`. 3. begin_transfers() == 0 was used as a completion check in both STEP_FETCH_CHUNK and STEP_STORE_CHUNK, but returning 0 only means "no new transfers started", not "all done". With concurrent chunk transfers, this caused premature completion, leaving in-flight transfers to hit UNREACHABLE. Fixed by using all_chunk_transfers_completed() instead. 4. choose_store_locations_for_chunk() returned [0, 1] for ALL chunks regardless of index, while GET fetches chunk i from servers (i+j) % num_servers. This made some chunks unreachable. Fixed by using (chunk_idx + j) % num_servers for store locations. Also added SO_REUSEADDR to the server listen socket so restarting the cluster doesn't fail with "Couldn't setup TCP listener", and added a note on mock_access about the simulation mismatch. https://claude.ai/code/session_01JniNxxDP4NbsDXNGmGNG7H
This commit is contained in:
+11
-11
@@ -165,6 +165,9 @@ void toastyfs_free(ToastyFS *tfs)
|
||||
free(tfs->put_data);
|
||||
}
|
||||
|
||||
static int find_completed_transfer_for_hash(ToastyFS *tfs, SHA256 hash);
|
||||
static bool all_chunk_transfers_completed(ToastyFS *tfs);
|
||||
|
||||
static bool
|
||||
transfer_for_hash_already_started(ToastyFS *tfs, SHA256 hash)
|
||||
{
|
||||
@@ -400,12 +403,11 @@ static int process_message(ToastyFS *tfs,
|
||||
mark_waiting_transfers_for_hash_as_aborted(tfs, resp.hash);
|
||||
client_log(tfs, "RECV FETCH_RESP", "size=%u", resp.size);
|
||||
|
||||
if (begin_transfers(tfs) == 0) {
|
||||
begin_transfers(tfs);
|
||||
if (all_chunk_transfers_completed(tfs)) {
|
||||
tfs->error = TOASTYFS_ERROR_VOID;
|
||||
tfs->step = STEP_GET_DONE;
|
||||
client_log_simple(tfs, "ALL CHUNKS FETCHED");
|
||||
} else {
|
||||
tfs->step = STEP_FETCH_CHUNK;
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -434,7 +436,8 @@ static int process_message(ToastyFS *tfs,
|
||||
tfs->transfers[transfer_idx].state = TRANSFER_COMPLETED;
|
||||
mark_waiting_transfers_for_hash_as_aborted(tfs, ack.hash);
|
||||
|
||||
if (begin_transfers(tfs) == 0) {
|
||||
begin_transfers(tfs);
|
||||
if (all_chunk_transfers_completed(tfs)) {
|
||||
|
||||
CommitPutMessage msg = {
|
||||
.base = {
|
||||
@@ -462,9 +465,6 @@ static int process_message(ToastyFS *tfs,
|
||||
tfs->step = STEP_COMMIT;
|
||||
client_log(tfs, "SEND COMMIT_PUT", "key=%s chunks=%d req=%lu",
|
||||
tfs->key, tfs->num_chunks, tfs->request_id);
|
||||
|
||||
} else {
|
||||
tfs->step = STEP_STORE_CHUNK;
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -748,10 +748,10 @@ int toastyfs_register_events(ToastyFS *tfs, void **ctxs, struct pollfd *pdata, i
|
||||
}
|
||||
|
||||
static void
|
||||
choose_store_locations_for_chunk(ToastyFS *tfs, int *locations)
|
||||
choose_store_locations_for_chunk(ToastyFS *tfs, int chunk_idx, int *locations)
|
||||
{
|
||||
for (int i = 0; i < REPLICATION_FACTOR; i++) {
|
||||
locations[i] = i % tfs->num_servers;
|
||||
for (int j = 0; j < REPLICATION_FACTOR; j++) {
|
||||
locations[j] = (chunk_idx + j) % tfs->num_servers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +802,7 @@ int toastyfs_async_put(ToastyFS *tfs, char *key, int key_len,
|
||||
SHA256 hash = compute_chunk_hash(data_copy + offset, size);
|
||||
|
||||
int locations[REPLICATION_FACTOR];
|
||||
choose_store_locations_for_chunk(tfs, locations);
|
||||
choose_store_locations_for_chunk(tfs, i, locations);
|
||||
|
||||
for (int j = 0; j < REPLICATION_FACTOR; j++)
|
||||
add_transfer(tfs, hash, locations[j], data_copy + offset, size);
|
||||
|
||||
Reference in New Issue
Block a user