Improve ASL store index lifecycle

This commit is contained in:
Carl Niklas Rydberg 2026-01-18 05:54:31 +01:00
parent 0a118b9841
commit dde532d68f
6 changed files with 2049 additions and 10 deletions

View file

@ -18,15 +18,30 @@ typedef struct {
uint64_t idle_time_ns; uint64_t idle_time_ns;
} amduat_asl_store_index_fs_snapshot_policy_t; } amduat_asl_store_index_fs_snapshot_policy_t;
typedef struct {
uint64_t max_segment_records;
uint64_t max_segment_bytes;
uint64_t small_artifact_threshold;
bool allow_deferred_visibility;
} amduat_asl_store_index_fs_segment_policy_t;
typedef struct {
uint32_t segment_domain_id;
uint8_t record_visibility;
} amduat_asl_store_index_fs_visibility_policy_t;
typedef struct { typedef struct {
amduat_asl_store_config_t config; amduat_asl_store_config_t config;
amduat_asl_store_index_fs_snapshot_policy_t snapshot_policy; amduat_asl_store_index_fs_snapshot_policy_t snapshot_policy;
amduat_asl_store_index_fs_segment_policy_t segment_policy;
amduat_asl_store_index_fs_visibility_policy_t visibility_policy;
uint16_t shard_count; uint16_t shard_count;
char root_path[AMDUAT_ASL_STORE_INDEX_FS_ROOT_MAX]; char root_path[AMDUAT_ASL_STORE_INDEX_FS_ROOT_MAX];
uint64_t pending_snapshot_bytes; uint64_t pending_snapshot_bytes;
uint64_t last_ingest_time_ns; uint64_t last_ingest_time_ns;
amduat_asl_snapshot_id_t next_snapshot_id; amduat_asl_snapshot_id_t next_snapshot_id;
bool snapshot_state_initialized; bool snapshot_state_initialized;
void *open_segments;
} amduat_asl_store_index_fs_t; } amduat_asl_store_index_fs_t;
bool amduat_asl_store_index_fs_init(amduat_asl_store_index_fs_t *fs, bool amduat_asl_store_index_fs_init(amduat_asl_store_index_fs_t *fs,
@ -37,6 +52,14 @@ void amduat_asl_store_index_fs_set_snapshot_policy(
amduat_asl_store_index_fs_t *fs, amduat_asl_store_index_fs_t *fs,
amduat_asl_store_index_fs_snapshot_policy_t policy); amduat_asl_store_index_fs_snapshot_policy_t policy);
void amduat_asl_store_index_fs_set_segment_policy(
amduat_asl_store_index_fs_t *fs,
amduat_asl_store_index_fs_segment_policy_t policy);
void amduat_asl_store_index_fs_set_visibility_policy(
amduat_asl_store_index_fs_t *fs,
amduat_asl_store_index_fs_visibility_policy_t policy);
void amduat_asl_store_index_fs_set_shard_count( void amduat_asl_store_index_fs_set_shard_count(
amduat_asl_store_index_fs_t *fs, amduat_asl_store_index_fs_t *fs,
uint16_t shard_count); uint16_t shard_count);
@ -47,6 +70,14 @@ amduat_asl_store_error_t amduat_asl_store_index_fs_snapshot_create(
amduat_asl_log_position_t *out_logseq, amduat_asl_log_position_t *out_logseq,
uint8_t out_root_hash[32]); uint8_t out_root_hash[32]);
amduat_asl_store_error_t amduat_asl_store_index_fs_flush(
amduat_asl_store_index_fs_t *fs,
amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t amduat_asl_store_index_fs_gc(
amduat_asl_store_index_fs_t *fs,
const amduat_asl_index_state_t *state);
amduat_asl_store_ops_t amduat_asl_store_index_fs_ops(void); amduat_asl_store_ops_t amduat_asl_store_index_fs_ops(void);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -2,6 +2,7 @@
#define AMDUAT_ASL_STORE_H #define AMDUAT_ASL_STORE_H
#include "amduat/asl/core.h" #include "amduat/asl/core.h"
#include "amduat/enc/asl_log.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
@ -48,6 +49,20 @@ typedef struct {
amduat_reference_t ref, amduat_reference_t ref,
amduat_asl_index_state_t state, amduat_asl_index_state_t state,
amduat_artifact_t *out_artifact); amduat_artifact_t *out_artifact);
amduat_asl_store_error_t (*tombstone)(void *ctx,
amduat_reference_t ref,
uint32_t scope,
uint32_t reason_code,
amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t (*tombstone_lift)(
void *ctx,
amduat_reference_t ref,
amduat_asl_log_position_t tombstone_logseq,
amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t (*log_scan)(
void *ctx,
amduat_asl_log_record_t **out_records,
size_t *out_count);
bool (*current_state)(void *ctx, amduat_asl_index_state_t *out_state); bool (*current_state)(void *ctx, amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t (*validate_config)( amduat_asl_store_error_t (*validate_config)(
void *ctx, void *ctx,
@ -62,6 +77,9 @@ static inline void amduat_asl_store_ops_init(amduat_asl_store_ops_t *ops) {
ops->get = NULL; ops->get = NULL;
ops->put_indexed = NULL; ops->put_indexed = NULL;
ops->get_indexed = NULL; ops->get_indexed = NULL;
ops->tombstone = NULL;
ops->tombstone_lift = NULL;
ops->log_scan = NULL;
ops->current_state = NULL; ops->current_state = NULL;
ops->validate_config = NULL; ops->validate_config = NULL;
} }
@ -97,6 +115,24 @@ amduat_asl_store_error_t amduat_asl_store_get_indexed(
amduat_asl_index_state_t state, amduat_asl_index_state_t state,
amduat_artifact_t *out_artifact); amduat_artifact_t *out_artifact);
amduat_asl_store_error_t amduat_asl_store_tombstone(
amduat_asl_store_t *store,
amduat_reference_t ref,
uint32_t scope,
uint32_t reason_code,
amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t amduat_asl_store_tombstone_lift(
amduat_asl_store_t *store,
amduat_reference_t ref,
amduat_asl_log_position_t tombstone_logseq,
amduat_asl_index_state_t *out_state);
amduat_asl_store_error_t amduat_asl_log_scan(
amduat_asl_store_t *store,
amduat_asl_log_record_t **out_records,
size_t *out_count);
bool amduat_asl_index_current_state(amduat_asl_store_t *store, bool amduat_asl_index_current_state(amduat_asl_store_t *store,
amduat_asl_index_state_t *out_state); amduat_asl_index_state_t *out_state);

File diff suppressed because it is too large Load diff

View file

@ -106,6 +106,66 @@ amduat_asl_store_error_t amduat_asl_store_get_indexed(
return store->ops.get_indexed(store->ctx, ref, state, out_artifact); return store->ops.get_indexed(store->ctx, ref, state, out_artifact);
} }
amduat_asl_store_error_t amduat_asl_store_tombstone(
amduat_asl_store_t *store,
amduat_reference_t ref,
uint32_t scope,
uint32_t reason_code,
amduat_asl_index_state_t *out_state) {
amduat_asl_store_error_t cfg_err;
if (store == NULL || store->ops.tombstone == NULL) {
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
}
if (ref.hash_id != store->config.hash_id) {
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
}
cfg_err = amduat_asl_store_validate_config(store);
if (cfg_err != AMDUAT_ASL_STORE_OK) {
return cfg_err;
}
return store->ops.tombstone(store->ctx, ref, scope, reason_code, out_state);
}
amduat_asl_store_error_t amduat_asl_store_tombstone_lift(
amduat_asl_store_t *store,
amduat_reference_t ref,
amduat_asl_log_position_t tombstone_logseq,
amduat_asl_index_state_t *out_state) {
amduat_asl_store_error_t cfg_err;
if (store == NULL || store->ops.tombstone_lift == NULL) {
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
}
if (ref.hash_id != store->config.hash_id) {
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
}
cfg_err = amduat_asl_store_validate_config(store);
if (cfg_err != AMDUAT_ASL_STORE_OK) {
return cfg_err;
}
return store->ops.tombstone_lift(store->ctx,
ref,
tombstone_logseq,
out_state);
}
amduat_asl_store_error_t amduat_asl_log_scan(
amduat_asl_store_t *store,
amduat_asl_log_record_t **out_records,
size_t *out_count) {
amduat_asl_store_error_t cfg_err;
if (store == NULL || store->ops.log_scan == NULL) {
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
}
cfg_err = amduat_asl_store_validate_config(store);
if (cfg_err != AMDUAT_ASL_STORE_OK) {
return cfg_err;
}
return store->ops.log_scan(store->ctx, out_records, out_count);
}
bool amduat_asl_index_current_state(amduat_asl_store_t *store, bool amduat_asl_index_current_state(amduat_asl_store_t *store,
amduat_asl_index_state_t *out_state) { amduat_asl_index_state_t *out_state) {
amduat_asl_store_error_t cfg_err; amduat_asl_store_error_t cfg_err;

View file

@ -5,6 +5,7 @@
#include "amduat/asl/store.h" #include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core.h"
#include "amduat/enc/asl_core_index.h" #include "amduat/enc/asl_core_index.h"
#include "amduat/enc/asl_log.h"
#include "amduat/format/ref.h" #include "amduat/format/ref.h"
#include "amduat/hash/asl1.h" #include "amduat/hash/asl1.h"
@ -250,6 +251,13 @@ static uint64_t fnv1a64(const uint8_t *data, size_t len, uint64_t seed) {
return hash; return hash;
} }
static uint64_t load_u64_le(const uint8_t *data) {
return (uint64_t)data[0] | ((uint64_t)data[1] << 8) |
((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) |
((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) |
((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56);
}
static uint16_t ref_shard(amduat_reference_t ref, uint16_t shard_count) { static uint16_t ref_shard(amduat_reference_t ref, uint16_t shard_count) {
uint64_t hash; uint64_t hash;
uint8_t hash_id_bytes[2]; uint8_t hash_id_bytes[2];
@ -739,6 +747,343 @@ cleanup:
return exit_code; return exit_code;
} }
static int test_segment_batch_packing(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_index_fs_segment_policy_t segment_policy;
amduat_asl_store_index_fs_visibility_policy_t visibility_policy;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state_a;
amduat_asl_index_state_t state_b;
amduat_artifact_t artifact_a;
amduat_artifact_t artifact_b;
amduat_artifact_t loaded;
amduat_reference_t ref_a;
amduat_reference_t ref_b;
uint8_t payload_a[3] = {0x01, 0x02, 0x03};
uint8_t payload_b[4] = {0x10, 0x11, 0x12, 0x13};
char *root;
amduat_asl_log_record_t *records = NULL;
size_t record_count = 0u;
uint64_t segment_id = 0u;
char *segment_path = NULL;
uint8_t *segment_bytes = NULL;
size_t segment_len = 0u;
amduat_asl_core_index_segment_t segment;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
segment_policy.max_segment_records = 2u;
segment_policy.max_segment_bytes = 0u;
segment_policy.small_artifact_threshold = 1024u;
segment_policy.allow_deferred_visibility = true;
amduat_asl_store_index_fs_set_segment_policy(&fs, segment_policy);
visibility_policy.segment_domain_id = 7u;
visibility_policy.record_visibility = 1u;
amduat_asl_store_index_fs_set_visibility_policy(&fs, visibility_policy);
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
artifact_a = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a)));
artifact_b = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b)));
ref_a = amduat_reference(0u, amduat_octets(NULL, 0u));
ref_b = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact_a, &ref_a, &state_a);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed a failed: %d\n", err);
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref_a, state_a, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr, "expected a to be pending before seal: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_store_put_indexed(&store, artifact_b, &ref_b, &state_b);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed b failed: %d\n", err);
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref_a, state_b, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed a after seal failed: %d\n", err);
goto cleanup;
}
amduat_artifact_free(&loaded);
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref_b, state_b, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed b after seal failed: %d\n", err);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_log_scan(&store, &records, &record_count);
if (err != AMDUAT_ASL_STORE_OK || record_count != 1u ||
records[0].record_type != AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL) {
fprintf(stderr, "log scan expected one segment seal\n");
goto cleanup;
}
if (records[0].payload.len < 8u) {
fprintf(stderr, "segment seal payload too small\n");
goto cleanup;
}
segment_id = load_u64_le(records[0].payload.data);
memset(&segment, 0, sizeof(segment));
if (!build_segment_path(root, segment_id, &segment_path)) {
fprintf(stderr, "segment path build failed\n");
goto cleanup;
}
if (!read_file(segment_path, &segment_bytes, &segment_len)) {
fprintf(stderr, "segment read failed\n");
goto cleanup;
}
if (!amduat_enc_asl_core_index_decode_v1(
amduat_octets(segment_bytes, segment_len), &segment)) {
fprintf(stderr, "segment decode failed\n");
goto cleanup;
}
if (segment.record_count != 2u || segment.extent_count != 2u) {
fprintf(stderr, "segment record/extent count mismatch\n");
goto cleanup;
}
if (segment.header.segment_visibility != 1u ||
segment.header.segment_domain_id != 7u) {
fprintf(stderr, "segment visibility metadata mismatch\n");
goto cleanup;
}
if (segment.records[0].visibility != 1u ||
segment.records[1].visibility != 1u ||
segment.records[0].domain_id != 7u ||
segment.records[1].domain_id != 7u) {
fprintf(stderr, "record visibility metadata mismatch\n");
goto cleanup;
}
exit_code = 0;
cleanup:
amduat_enc_asl_core_index_free(&segment);
free(segment_path);
free(segment_bytes);
amduat_enc_asl_log_free(records, record_count);
amduat_reference_free(&ref_a);
amduat_reference_free(&ref_b);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_tombstone_round_trip(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_asl_index_state_t tombstone_state;
amduat_asl_index_state_t lift_state;
amduat_artifact_t artifact;
amduat_artifact_t loaded;
amduat_reference_t ref;
uint8_t payload[4];
char *root;
amduat_asl_log_record_t *records = NULL;
size_t record_count = 0u;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload, 0x2a, sizeof(payload));
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed: %d\n", err);
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed failed: %d\n", err);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_store_tombstone(&store, ref, 7u, 9u, &tombstone_state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "tombstone failed: %d\n", err);
goto cleanup;
}
if (tombstone_state.log_position <= state.log_position) {
fprintf(stderr, "tombstone did not advance log position\n");
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr, "get_indexed after tombstone expected not found: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_store_tombstone_lift(&store,
ref,
tombstone_state.log_position,
&lift_state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "tombstone_lift failed: %d\n", err);
goto cleanup;
}
if (lift_state.log_position <= tombstone_state.log_position) {
fprintf(stderr, "tombstone_lift did not advance log position\n");
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed after tombstone lift failed: %d\n", err);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_log_scan(&store, &records, &record_count);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "log scan failed: %d\n", err);
goto cleanup;
}
if (record_count < 3u ||
records[record_count - 1u].record_type !=
AMDUAT_ASL_LOG_RECORD_TOMBSTONE_LIFT) {
fprintf(stderr, "log scan missing tombstone lift record\n");
goto cleanup;
}
exit_code = 0;
cleanup:
amduat_enc_asl_log_free(records, record_count);
amduat_reference_free(&ref);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_gc_keeps_visible(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_artifact_t artifact;
amduat_artifact_t loaded;
amduat_reference_t ref;
uint8_t payload[5];
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload, 0x77, sizeof(payload));
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_gc(&fs, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "gc failed: %d\n", err);
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed after gc failed: %d\n", err);
goto cleanup;
}
amduat_artifact_free(&loaded);
exit_code = 0;
cleanup:
amduat_reference_free(&ref);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_large_round_trip_perf(void) { static int test_large_round_trip_perf(void) {
enum { k_artifact_count_default = 10000, k_payload_max = 64 }; enum { k_artifact_count_default = 10000, k_payload_max = 64 };
amduat_asl_store_config_t config; amduat_asl_store_config_t config;
@ -1278,6 +1623,15 @@ int main(void) {
if (test_snapshot_truncation() != 0) { if (test_snapshot_truncation() != 0) {
return 1; return 1;
} }
if (test_segment_batch_packing() != 0) {
return 1;
}
if (test_tombstone_round_trip() != 0) {
return 1;
}
if (test_gc_keeps_visible() != 0) {
return 1;
}
if (test_large_round_trip_perf() != 0) { if (test_large_round_trip_perf() != 0) {
return 1; return 1;
} }

View file

@ -40,6 +40,26 @@ static int test_indexed_ops_unsupported(void) {
return 1; return 1;
} }
err = amduat_asl_store_tombstone(&store, ref, 0u, 0u, &state);
if (err != AMDUAT_ASL_STORE_ERR_UNSUPPORTED) {
fprintf(stderr, "tombstone missing ops should be unsupported: %d\n", err);
return 1;
}
err = amduat_asl_store_tombstone_lift(&store, ref, 1u, &state);
if (err != AMDUAT_ASL_STORE_ERR_UNSUPPORTED) {
fprintf(stderr,
"tombstone_lift missing ops should be unsupported: %d\n",
err);
return 1;
}
err = amduat_asl_log_scan(&store, NULL, NULL);
if (err != AMDUAT_ASL_STORE_ERR_UNSUPPORTED) {
fprintf(stderr, "log_scan missing ops should be unsupported: %d\n", err);
return 1;
}
return 0; return 0;
} }