Recover index state and stale log heads after partial repairs

This commit is contained in:
Carl Niklas Rydberg 2026-02-08 08:55:39 +01:00
parent 6ab25361be
commit 624bd29bf9
2 changed files with 57 additions and 29 deletions

View file

@ -11,6 +11,7 @@
#include "amduat/enc/asl_core_index.h" #include "amduat/enc/asl_core_index.h"
#include "amduat/enc/asl_log.h" #include "amduat/enc/asl_log.h"
#include "amduat/hash/asl1.h" #include "amduat/hash/asl1.h"
#include "amduat/util/log.h"
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
@ -3821,10 +3822,19 @@ static bool amduat_asl_store_index_fs_current_state_impl(
if (amduat_asl_store_index_fs_find_latest_snapshot_id(fs, &snapshot_id) && if (amduat_asl_store_index_fs_find_latest_snapshot_id(fs, &snapshot_id) &&
amduat_asl_store_index_fs_load_snapshot_manifest( amduat_asl_store_index_fs_load_snapshot_manifest(
fs->root_path, snapshot_id, &manifest, manifest_hash)) { fs->root_path, snapshot_id, &manifest, manifest_hash)) {
if (manifest.anchor_logseq <= last_logseq) {
out_state->snapshot_id = manifest.snapshot_id; out_state->snapshot_id = manifest.snapshot_id;
if (manifest.anchor_logseq > out_state->log_position) { if (manifest.anchor_logseq > out_state->log_position) {
out_state->log_position = manifest.anchor_logseq; out_state->log_position = manifest.anchor_logseq;
} }
} else {
amduat_log(
AMDUAT_LOG_WARN,
"index snapshot anchor beyond log tail; ignoring snapshot (snapshot=%" PRIu64 " anchor=%" PRIu64 " log_tail=%" PRIu64 ")",
(uint64_t)manifest.snapshot_id,
(uint64_t)manifest.anchor_logseq,
(uint64_t)last_logseq);
}
amduat_asl_snapshot_manifest_free(&manifest); amduat_asl_snapshot_manifest_free(&manifest);
} else if (record_count != 0u && } else if (record_count != 0u &&
amduat_asl_store_index_fs_find_latest_anchor(records, amduat_asl_store_index_fs_find_latest_anchor(records,

View file

@ -536,6 +536,7 @@ amduat_asl_store_error_t amduat_asl_log_append(
for (uint32_t attempt = 0u; attempt < AMDUAT_ASL_LOG_MAX_RETRIES; ++attempt) { for (uint32_t attempt = 0u; attempt < AMDUAT_ASL_LOG_MAX_RETRIES; ++attempt) {
bool head_exists = false; bool head_exists = false;
bool head_ref_missing = false;
amduat_reference_t head_ref; amduat_reference_t head_ref;
amduat_artifact_t head_artifact; amduat_artifact_t head_artifact;
amduat_asl_log_chunk_t head_chunk; amduat_asl_log_chunk_t head_chunk;
@ -560,6 +561,10 @@ amduat_asl_store_error_t amduat_asl_log_append(
store_err = amduat_asl_store_get(log_store->store, head_ref, store_err = amduat_asl_store_get(log_store->store, head_ref,
&head_artifact); &head_artifact);
if (store_err != AMDUAT_ASL_STORE_OK) { if (store_err != AMDUAT_ASL_STORE_OK) {
if (store_err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
/* Recover from a stale head pointer by rebasing to a fresh chunk. */
head_ref_missing = true;
} else {
char *head_ref_hex = NULL; char *head_ref_hex = NULL;
(void)amduat_asl_ref_encode_hex(head_ref, &head_ref_hex); (void)amduat_asl_ref_encode_hex(head_ref, &head_ref_hex);
amduat_log(AMDUAT_LOG_ERROR, amduat_log(AMDUAT_LOG_ERROR,
@ -574,6 +579,8 @@ amduat_asl_store_error_t amduat_asl_log_append(
free(pointer_name); free(pointer_name);
return store_err; return store_err;
} }
}
if (!head_ref_missing) {
if (!head_artifact.has_type_tag || if (!head_artifact.has_type_tag ||
head_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1) { head_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1) {
amduat_reference_free(&head_ref); amduat_reference_free(&head_ref);
@ -604,6 +611,17 @@ amduat_asl_store_error_t amduat_asl_log_append(
} }
base_offset = head_chunk.base_offset + head_chunk.entry_count; base_offset = head_chunk.base_offset + head_chunk.entry_count;
amduat_asl_log_chunk_free(&head_chunk); amduat_asl_log_chunk_free(&head_chunk);
} else {
char *head_ref_hex = NULL;
(void)amduat_asl_ref_encode_hex(head_ref, &head_ref_hex);
amduat_log(AMDUAT_LOG_WARN,
"asl_log_append: stale head pointer missing chunk; rebasing log head (log=%s pointer=%s attempt=%u head_ref=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
head_ref_hex != NULL ? head_ref_hex : "(hex-encode-failed)");
free(head_ref_hex);
}
} }
{ {
@ -618,7 +636,7 @@ amduat_asl_store_error_t amduat_asl_log_append(
new_chunk.has_timestamp = has_timestamp; new_chunk.has_timestamp = has_timestamp;
new_chunk.has_actor = has_actor; new_chunk.has_actor = has_actor;
new_chunk.entries = (amduat_asl_log_entry_t *)entries; new_chunk.entries = (amduat_asl_log_entry_t *)entries;
if (head_exists) { if (head_exists && !head_ref_missing) {
new_chunk.has_prev = true; new_chunk.has_prev = true;
new_chunk.prev_ref = head_ref; new_chunk.prev_ref = head_ref;
} }