Add MISSING_FILE_GENERATION special value for "file does not exist"
Implements a new special generation counter value (UINT64_MAX) that allows clients to assert that a file should NOT exist when performing operations. Primary use case: Atomic "create-if-not-exists" operations When combined with TOASTY_WRITE_CREATE_IF_MISSING flag: - File doesn't exist → server creates file and writes (SUCCESS) - File exists → gen_match fails → BADGEN error (prevents overwrite) This enables race-condition-free file creation where you want to create a NEW file or fail if it already exists. Key changes: - Added MISSING_FILE_GENERATION constant to file_tree.h - Updated gen_match() to return false when checking existing entities against MISSING_FILE_GENERATION - Modified file_tree_delete_entity() to succeed (no-op) when file is missing and MISSING_FILE_GENERATION is specified (idempotent delete) - Modified file_tree_write() to validate against the new value - Added documentation in metadata_server.c explaining how MISSING_FILE_GENERATION works WITH CREATE_IF_MISSING - Updated protocol documentation in PROTOCOL.txt - Added comprehensive feature documentation in MISSING_FILE_GENERATION.md Additional use cases: - Idempotent delete operations (delete only if not already gone) - Detecting unexpected file creation in validation/testing scenarios The implementation is backward compatible and type-safe. Files are never assigned MISSING_FILE_GENERATION as their actual generation value.
This commit is contained in:
@@ -430,6 +430,7 @@ process_client_read(MetadataServer *state, int conn_idx, ByteView msg)
|
||||
uint64_t actual_bytes;
|
||||
SHA256 hashes[MAX_READ_HASHES];
|
||||
int ret = file_tree_read(&state->file_tree, path, offset, length, &gen, &chunk_size, hashes, MAX_READ_HASHES, &actual_bytes);
|
||||
assert(gen != NO_GENERATION);
|
||||
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -476,6 +477,7 @@ process_client_read(MetadataServer *state, int conn_idx, ByteView msg)
|
||||
MessageWriter writer;
|
||||
message_writer_init(&writer, output, MESSAGE_TYPE_READ_SUCCESS);
|
||||
|
||||
assert(gen != NO_GENERATION);
|
||||
message_write(&writer, &gen, sizeof(gen));
|
||||
|
||||
if (chunk_size > UINT32_MAX) {
|
||||
@@ -668,7 +670,10 @@ process_client_write(MetadataServer *state, int conn_idx, ByteView msg)
|
||||
num_chunks, expect_gen, &new_gen, new_hashes, removed_hashes, &num_removed, truncate_after);
|
||||
|
||||
// If write failed because file doesn't exist and CREATE_IF_MISSING flag is set,
|
||||
// create the file and retry the write
|
||||
// create the file and retry the write.
|
||||
// Note: MISSING_FILE_GENERATION works WITH CREATE_IF_MISSING to implement
|
||||
// atomic "create-only-if-not-exists" semantics: creates if missing (NOENT),
|
||||
// but fails if file already exists (BADGEN from gen_match).
|
||||
if (ret == FILETREE_NOENT && (flags & TOASTY_WRITE_CREATE_IF_MISSING)) {
|
||||
// Create the file with default chunk size of 4096 bytes
|
||||
uint64_t chunk_size = 4096;
|
||||
|
||||
Reference in New Issue
Block a user