0.3.16-pre.007

This commit is contained in:
2026-09-22 06:22:40 +02:00
parent a97837b832
commit d768737629
22 changed files with 842 additions and 276 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/raw_transaction.rs
// version: 13
// version: 14
pub(crate) mod cursor;
@@ -12,6 +12,7 @@ const GET_TOMBSTONE_SQL: &str = "SELECT signature, slot::text AS slot_text, bloc
const GET_TRANSACTION_SQL: &str = "SELECT transaction_row.signature, transaction_row.slot::text AS slot_text, transaction_row.block_time_unix_millis, transaction_row.format_id, transaction_row.format_version, transaction_row.content_hash, transaction_row.payload, transaction_row.retention_state, archive_row.payload AS archive_payload FROM ksp_raw_transactions AS transaction_row LEFT JOIN ksp_raw_transaction_archive_payloads AS archive_row ON archive_row.signature = transaction_row.signature WHERE transaction_row.signature = $1";
const INSERT_ARCHIVE_PAYLOAD_SQL: &str = "INSERT INTO ksp_raw_transaction_archive_payloads (signature, payload) VALUES ($1, $2)";
const INSERT_OBSERVATION_SQL: &str = "INSERT INTO ksp_raw_transaction_observations (observation_key, transaction_signature, provider, protocol, acquisition_method, origin, received_at_unix_millis, capture_session_id, commitment, endpoint_id, filter_id, observed_at_unix_millis, source_payload_hash, source_payload_size_bytes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) ON CONFLICT (observation_key) DO NOTHING RETURNING observation_key";
const INSERT_TRANSACTION_CONFLICT_SQL: &str = "INSERT INTO ksp_raw_transaction_conflicts (transaction_signature, status, revision, canonical_variant_id, incoming_variant_id, latest_relation, latest_reason_code, created_at_unix_millis, updated_at_unix_millis) VALUES ($1, 'open', 1, $2::TEXT::NUMERIC, $3::TEXT::NUMERIC, $4, $5, $6, $6)";
const INSERT_TRANSACTION_SQL: &str = "INSERT INTO ksp_raw_transactions (signature, slot, block_time_unix_millis, format_id, format_version, content_hash, payload, retention_state) VALUES ($1, $2::TEXT::NUMERIC, $3, $4, $5, $6, $7, 'full') ON CONFLICT (signature) DO NOTHING RETURNING signature";
const INSERT_TRANSACTION_VARIANT_LINK_SQL: &str = "INSERT INTO ksp_raw_transaction_observation_variants (observation_key, transaction_signature, variant_id, linked_at_unix_millis) VALUES ($1, $2, $3::TEXT::NUMERIC, $4) RETURNING observation_key";
const INSERT_TRANSACTION_VARIANT_SELECTOR_SQL: &str = "INSERT INTO ksp_raw_transaction_canonical_selectors (transaction_signature, canonical_variant_id, canonical_revision, updated_at_unix_millis) VALUES ($1, $2::TEXT::NUMERIC, 1, $3)";
@@ -25,6 +26,7 @@ const LIST_TRANSACTIONS_DESC_SQL: &str = "SELECT signature, slot::text AS slot_t
const LOCK_OBSERVATION_SQL: &str = "SELECT observation_key, transaction_signature, provider, protocol, acquisition_method, origin, received_at_unix_millis, capture_session_id, commitment, endpoint_id, filter_id, observed_at_unix_millis, source_payload_hash, source_payload_size_bytes FROM ksp_raw_transaction_observations WHERE observation_key = $1 FOR UPDATE";
const LOCK_RETENTION_TRANSACTION_SQL: &str =
"SELECT block_time_unix_millis, payload, retention_state FROM ksp_raw_transactions WHERE signature = $1 FOR UPDATE";
const LOCK_TRANSACTION_CONFLICT_SQL: &str = "SELECT status, revision::TEXT AS revision_text, canonical_variant_id::TEXT AS canonical_variant_id_text, incoming_variant_id::TEXT AS incoming_variant_id_text, latest_relation, latest_reason_code, created_at_unix_millis, updated_at_unix_millis FROM ksp_raw_transaction_conflicts WHERE transaction_signature = $1 FOR UPDATE";
const LOCK_TRANSACTION_SQL: &str = "SELECT signature, slot::text AS slot_text, block_time_unix_millis, format_id, format_version, content_hash, payload, retention_state, NULL::BYTEA AS archive_payload FROM ksp_raw_transactions WHERE signature = $1 FOR UPDATE";
const RAW_TRANSACTION_CONTENT_CONFLICT_META_FIELDS: [&str; 15] = [
"err",
@@ -59,6 +61,7 @@ const UPDATE_ARCHIVED_TRANSACTION_SQL: &str =
const UPDATE_CANONICAL_TRANSACTION_PROJECTION_SQL: &str = "UPDATE ksp_raw_transactions SET slot = $2::TEXT::NUMERIC, block_time_unix_millis = $3, format_id = $4, format_version = $5, content_hash = $6, payload = $7, retention_state = 'full' WHERE signature = $1";
const UPDATE_PURGED_TRANSACTION_SQL: &str = "UPDATE ksp_raw_transactions SET block_time_unix_millis = NULL, payload = NULL, retention_state = 'purged' WHERE signature = $1 AND retention_state = 'archived'";
const UPDATE_REHYDRATED_TRANSACTION_VARIANT_SQL: &str = "UPDATE ksp_raw_transaction_variants SET block_time_unix_millis = $3, payload = $4, retention_state = 'full' WHERE transaction_signature = $1 AND variant_id = $2::TEXT::NUMERIC";
const UPDATE_TRANSACTION_CONFLICT_SQL: &str = "UPDATE ksp_raw_transaction_conflicts SET status = 'open', revision = $2::TEXT::NUMERIC, canonical_variant_id = $3::TEXT::NUMERIC, incoming_variant_id = $4::TEXT::NUMERIC, latest_relation = $5, latest_reason_code = $6, updated_at_unix_millis = $7 WHERE transaction_signature = $1 AND revision = $8::TEXT::NUMERIC";
const UPDATE_TRANSACTION_VARIANT_SELECTOR_SQL: &str = "UPDATE ksp_raw_transaction_canonical_selectors SET canonical_variant_id = $4::TEXT::NUMERIC, canonical_revision = $5::TEXT::NUMERIC, updated_at_unix_millis = $6 WHERE transaction_signature = $1 AND canonical_variant_id = $2::TEXT::NUMERIC AND canonical_revision = $3::TEXT::NUMERIC";
const UPDATE_TRANSACTION_VARIANT_TO_FULL_SQL: &str = "UPDATE ksp_raw_transaction_variants SET payload = $8, retention_state = 'full' WHERE transaction_signature = $1 AND variant_id = $2::TEXT::NUMERIC AND slot = $3::TEXT::NUMERIC AND block_time_unix_millis IS NOT DISTINCT FROM $4 AND format_id = $5 AND format_version = $6 AND content_hash = $7 AND ((retention_state = 'archived' AND payload IS NULL) OR (retention_state = 'full' AND payload = $8))";
@@ -701,42 +704,86 @@ pub(crate) async fn persist_raw_transaction_acquisition(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let (entity_outcome, observed_variant_id, canonical_promotion) = if inserted {
(ksp_store_api::RawEntityWriteOutcome::Inserted, canonical_variant_id, std::option::Option::None)
let (entity_outcome, observed_variant_id, transaction_variant_outcome, canonical_promotion, quarantined_conflict) = if inserted {
(
ksp_store_api::RawEntityWriteOutcome::Inserted,
canonical_variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::InsertedCanonical,
std::option::Option::None,
std::option::Option::None,
)
} else {
let comparison_result = compare_existing_transaction(network, locked.clone(), &raw_transaction);
let comparison = match comparison_result {
let comparison = match compare_existing_transaction(network, locked.clone(), &raw_transaction) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
if error.kind() == crate::PostgresBackendErrorKind::Conflict && error.phase() == "raw_acquisition_content_conflict" {
log_raw_transaction_content_conflict_provenance(&sql_transaction, network, &observation).await;
}
return std::result::Result::Err(error);
},
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
match comparison {
ExistingTransactionMatch::Active => (ksp_store_api::RawEntityWriteOutcome::AlreadyPresent, canonical_variant_id, std::option::Option::None),
ExistingTransactionMatch::Active => (
ksp_store_api::RawEntityWriteOutcome::AlreadyPresent,
canonical_variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::ObservedExact,
std::option::Option::None,
std::option::Option::None,
),
ExistingTransactionMatch::ActiveCompatibleLessComplete => {
let variant_result = persist_or_reuse_native_transaction_variant(&sql_transaction, &raw_transaction, linked_at).await;
let variant_id = match variant_result {
let variant_id = match persist_or_reuse_native_transaction_variant(&sql_transaction, &raw_transaction, linked_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
(ksp_store_api::RawEntityWriteOutcome::AlreadyPresent, variant_id, std::option::Option::None)
(
ksp_store_api::RawEntityWriteOutcome::AlreadyPresent,
variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::ObservedCompatibleLessComplete,
std::option::Option::None,
std::option::Option::None,
)
},
ExistingTransactionMatch::ActiveCompatibleMoreComplete => {
let variant_result = persist_or_reuse_native_transaction_variant(&sql_transaction, &raw_transaction, linked_at).await;
let variant_id = match variant_result {
let variant_id = match persist_or_reuse_native_transaction_variant(&sql_transaction, &raw_transaction, linked_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let promotion_result =
promote_more_complete_transaction_variant(&sql_transaction, &locked, &raw_transaction, canonical_variant_id, variant_id, linked_at).await;
let promotion = match promotion_result {
let promotion =
match promote_more_complete_transaction_variant(&sql_transaction, &locked, &raw_transaction, canonical_variant_id, variant_id, linked_at)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
(
ksp_store_api::RawEntityWriteOutcome::AlreadyPresent,
variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::PromotedCompatibleMoreComplete,
std::option::Option::Some(promotion),
std::option::Option::None,
)
},
ExistingTransactionMatch::ActiveConflict(variant_comparison) => {
let variant_id = match persist_or_reuse_native_transaction_variant(&sql_transaction, &raw_transaction, linked_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
(ksp_store_api::RawEntityWriteOutcome::AlreadyPresent, variant_id, std::option::Option::Some(promotion))
let conflict_transition = match open_or_update_raw_transaction_conflict(
&sql_transaction,
raw_transaction.reference(),
canonical_variant_id,
variant_id,
variant_comparison,
linked_at,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
log_raw_transaction_content_conflict_provenance(&sql_transaction, network, &observation).await;
(
ksp_store_api::RawEntityWriteOutcome::AlreadyPresent,
variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::QuarantinedConflict,
std::option::Option::None,
std::option::Option::Some(conflict_transition),
)
},
ExistingTransactionMatch::Purged => {
if mode == ksp_store_api::RawTransactionAcquisitionMode::ForceRehydrate {
@@ -748,15 +795,22 @@ pub(crate) async fn persist_raw_transaction_acquisition(
if let std::result::Result::Err(error) = variant_result {
return std::result::Result::Err(error);
}
(ksp_store_api::RawEntityWriteOutcome::Rehydrated, canonical_variant_id, std::option::Option::None)
(
ksp_store_api::RawEntityWriteOutcome::Rehydrated,
canonical_variant_id,
ksp_store_api::RawTransactionVariantWriteOutcome::Rehydrated,
std::option::Option::None,
std::option::Option::None,
)
} else {
let commit_result = sql_transaction.commit().await;
if commit_result.is_err() {
return std::result::Result::Err(write_failed("raw_acquisition_commit"));
}
return std::result::Result::Ok(ksp_store_api::RawAcquisitionWriteOutcome::new(
return std::result::Result::Ok(ksp_store_api::RawAcquisitionWriteOutcome::with_transaction_variant(
ksp_store_api::RawEntityWriteOutcome::SkippedPurged,
ksp_store_api::RawObservationWriteOutcome::NotRecorded,
ksp_store_api::RawTransactionVariantWriteOutcome::SkippedPurged,
));
}
},
@@ -778,7 +832,18 @@ pub(crate) async fn persist_raw_transaction_acquisition(
if let std::option::Option::Some(transition) = canonical_promotion {
log_raw_transaction_canonical_promotion_committed(network, raw_transaction.slot(), transition);
}
return std::result::Result::Ok(ksp_store_api::RawAcquisitionWriteOutcome::new(entity_outcome, observation_outcome));
if let std::option::Option::Some(transition) = quarantined_conflict {
let stored = decode_raw_transaction_row(network, locked.clone());
if let std::result::Result::Ok(std::option::Option::Some(stored)) = stored {
log_raw_transaction_content_conflict(network, &stored, &raw_transaction);
}
log_raw_transaction_conflict_quarantined_committed(network, raw_transaction.slot(), transition);
}
return std::result::Result::Ok(ksp_store_api::RawAcquisitionWriteOutcome::with_transaction_variant(
entity_outcome,
observation_outcome,
transaction_variant_outcome,
));
}
/// Persists one additional observation for an already known RAW transaction.
@@ -922,11 +987,21 @@ struct RawCanonicalPromotionTransition {
to_variant_id: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct RawTransactionConflictTransition {
canonical_variant_id: u64,
incoming_variant_id: u64,
reason: ksp_store_api::RawTransactionVariantRelationReason,
relation: ksp_store_api::RawTransactionVariantRelation,
revision: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ExistingTransactionMatch {
Active,
ActiveCompatibleLessComplete,
ActiveCompatibleMoreComplete,
ActiveConflict(ksp_store_api::RawTransactionVariantComparison),
Purged,
}
@@ -1259,6 +1334,155 @@ async fn persist_or_reuse_native_transaction_variant(
};
}
async fn open_or_update_raw_transaction_conflict(
sql_transaction: &deadpool_postgres::Transaction<'_>,
reference: &ksp_store_api::RawTransactionReference,
canonical_variant_id: u64,
incoming_variant_id: u64,
comparison: ksp_store_api::RawTransactionVariantComparison,
observed_at_unix_millis: i64,
) -> std::result::Result<RawTransactionConflictTransition, crate::PostgresBackendError> {
if canonical_variant_id == incoming_variant_id {
return std::result::Result::Err(data_invalid("raw_conflict_variant_identity"));
}
if !matches!(comparison.relation(), ksp_store_api::RawTransactionVariantRelation::Conflict | ksp_store_api::RawTransactionVariantRelation::Incomparable) {
return std::result::Result::Err(data_invalid("raw_conflict_relation"));
}
let signature = reference.signature();
let signature_bytes: &[u8] = signature.as_bytes();
let relation_code = comparison.relation().code();
let reason_code = comparison.reason().code();
let row_result = sql_transaction.query_opt(LOCK_TRANSACTION_CONFLICT_SQL, &[&signature_bytes]).await;
let row = match row_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(write_failed("raw_conflict_lock")),
};
let canonical_variant_id_text = canonical_variant_id.to_string();
let incoming_variant_id_text = incoming_variant_id.to_string();
if row.is_none() {
let insert_result = sql_transaction
.execute(
INSERT_TRANSACTION_CONFLICT_SQL,
&[
&signature_bytes,
&canonical_variant_id_text.as_str(),
&incoming_variant_id_text.as_str(),
&relation_code,
&reason_code,
&observed_at_unix_millis,
],
)
.await;
return match insert_result {
std::result::Result::Ok(1) => std::result::Result::Ok(RawTransactionConflictTransition {
canonical_variant_id,
incoming_variant_id,
reason: comparison.reason(),
relation: comparison.relation(),
revision: 1,
}),
std::result::Result::Ok(_) => std::result::Result::Err(data_invalid("raw_conflict_insert_count")),
std::result::Result::Err(_) => std::result::Result::Err(write_failed("raw_conflict_insert")),
};
}
let row = match row {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(data_invalid("raw_conflict_lock_state")),
};
let status = match row.try_get::<_, std::string::String>("status") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_status")),
};
if status != ksp_store_api::RawTransactionConflictStatus::Open.code() && status != ksp_store_api::RawTransactionConflictStatus::Resolved.code() {
return std::result::Result::Err(data_invalid("raw_conflict_status"));
}
let revision_text = match row.try_get::<_, std::string::String>("revision_text") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_revision")),
};
let revision = match decode_u64_decimal(revision_text.as_str(), "raw_conflict_revision") {
std::result::Result::Ok(value) if value > 0 => value,
std::result::Result::Ok(_) => return std::result::Result::Err(data_invalid("raw_conflict_revision")),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let existing_canonical_text = match row.try_get::<_, std::string::String>("canonical_variant_id_text") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_canonical_variant_id")),
};
let existing_canonical = match decode_u64_decimal(existing_canonical_text.as_str(), "raw_conflict_canonical_variant_id") {
std::result::Result::Ok(value) if value > 0 => value,
std::result::Result::Ok(_) => return std::result::Result::Err(data_invalid("raw_conflict_canonical_variant_id")),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let existing_incoming_text = match row.try_get::<_, std::string::String>("incoming_variant_id_text") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_incoming_variant_id")),
};
let existing_incoming = match decode_u64_decimal(existing_incoming_text.as_str(), "raw_conflict_incoming_variant_id") {
std::result::Result::Ok(value) if value > 0 => value,
std::result::Result::Ok(_) => return std::result::Result::Err(data_invalid("raw_conflict_incoming_variant_id")),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let existing_relation = match row.try_get::<_, std::string::String>("latest_relation") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_relation")),
};
let existing_reason = match row.try_get::<_, std::string::String>("latest_reason_code") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_reason")),
};
if status == ksp_store_api::RawTransactionConflictStatus::Open.code()
&& existing_canonical == canonical_variant_id
&& existing_incoming == incoming_variant_id
&& existing_relation == relation_code
&& existing_reason == reason_code
{
return std::result::Result::Ok(RawTransactionConflictTransition {
canonical_variant_id,
incoming_variant_id,
reason: comparison.reason(),
relation: comparison.relation(),
revision,
});
}
let updated_at = match row.try_get::<_, i64>("updated_at_unix_millis") {
std::result::Result::Ok(value) => std::cmp::max(value, observed_at_unix_millis),
std::result::Result::Err(_) => return std::result::Result::Err(data_invalid("raw_conflict_updated_at")),
};
let next_revision = match revision.checked_add(1) {
std::option::Option::Some(value) if value != 0 => value,
_ => return std::result::Result::Err(data_invalid("raw_conflict_revision_exhausted")),
};
let next_revision_text = next_revision.to_string();
let update_result = sql_transaction
.execute(
UPDATE_TRANSACTION_CONFLICT_SQL,
&[
&signature_bytes,
&next_revision_text.as_str(),
&canonical_variant_id_text.as_str(),
&incoming_variant_id_text.as_str(),
&relation_code,
&reason_code,
&updated_at,
&revision_text.as_str(),
],
)
.await;
return match update_result {
std::result::Result::Ok(1) => std::result::Result::Ok(RawTransactionConflictTransition {
canonical_variant_id,
incoming_variant_id,
reason: comparison.reason(),
relation: comparison.relation(),
revision: next_revision,
}),
std::result::Result::Ok(0) => std::result::Result::Err(data_invalid("raw_conflict_stale_revision")),
std::result::Result::Ok(_) => std::result::Result::Err(data_invalid("raw_conflict_update_count")),
std::result::Result::Err(_) => std::result::Result::Err(write_failed("raw_conflict_update")),
};
}
async fn promote_more_complete_transaction_variant(
sql_transaction: &deadpool_postgres::Transaction<'_>,
locked: &RawTransactionDbRow,
@@ -1657,8 +1881,7 @@ fn compare_existing_transaction(
},
ksp_store_api::RawTransactionVariantRelation::CompatibleMoreComplete => std::result::Result::Ok(ExistingTransactionMatch::ActiveCompatibleMoreComplete),
ksp_store_api::RawTransactionVariantRelation::Conflict | ksp_store_api::RawTransactionVariantRelation::Incomparable => {
log_raw_transaction_content_conflict(network, &stored, incoming);
std::result::Result::Err(conflict("raw_acquisition_content_conflict"))
std::result::Result::Ok(ExistingTransactionMatch::ActiveConflict(comparison))
},
_ => std::result::Result::Err(data_invalid("raw_acquisition_variant_relation")),
};
@@ -1925,6 +2148,22 @@ fn log_raw_transaction_canonical_promotion_committed(network: &ksp_store_api::Ra
return;
}
fn log_raw_transaction_conflict_quarantined_committed(network: &ksp_store_api::RawNetworkId, slot: u64, transition: RawTransactionConflictTransition) {
ksp_logging_lib::warn!(
target: crate::TRACING_TARGET,
domain = "store.raw_transaction.quarantined_conflict",
network = network.as_str(),
slot = slot,
canonical_variant_id = transition.canonical_variant_id,
incoming_variant_id = transition.incoming_variant_id,
conflict_revision = transition.revision,
relation = transition.relation.code(),
reason = transition.reason.code(),
"PostgreSQL Store committed a durable RAW transaction conflict case"
);
return;
}
fn log_raw_transaction_content_conflict(
network: &ksp_store_api::RawNetworkId,
stored: &ksp_store_api::RawTransaction,
@@ -1964,7 +2203,7 @@ fn log_raw_transaction_content_conflict(
incoming_log_messages_prefix_of_stored = diagnostic.log_messages.incoming_prefix_of_stored,
stored_log_messages_has_truncation_marker = diagnostic.log_messages.stored_has_truncation_marker,
incoming_log_messages_has_truncation_marker = diagnostic.log_messages.incoming_has_truncation_marker,
"PostgreSQL Store rejected divergent canonical RAW transaction"
"PostgreSQL Store classified divergent canonical RAW transaction content"
);
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
// version: 28
// version: 29
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -645,7 +645,7 @@ fn v0_3_15_pre_014_content_conflict_diagnostic_exposes_only_block_slot_and_bound
}
#[test]
fn v0_3_16_pre_005_postgres_uses_shared_fail_closed_variant_comparator_without_promotion() {
fn v0_3_16_pre_007_postgres_uses_shared_fail_closed_variant_comparator_with_durable_conflict() {
let source = include_str!("../src/raw_transaction.rs");
for required in [
"ksp_store_api::compare_raw_transaction_variants",
@@ -655,7 +655,8 @@ fn v0_3_16_pre_005_postgres_uses_shared_fail_closed_variant_comparator_without_p
"RawTransactionVariantRelation::Conflict",
"RawTransactionVariantRelation::Incomparable",
"ExistingTransactionMatch::ActiveCompatibleLessComplete",
"raw_acquisition_content_conflict",
"ExistingTransactionMatch::ActiveConflict",
"RawTransactionVariantWriteOutcome::QuarantinedConflict",
] {
assert!(source.contains(required), "missing shared comparator integration guard: {required}");
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/v003_variant_persistence.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -59,7 +59,7 @@ fn v0_3_16_pre_004_legacy_observation_without_mapping_is_not_fabricated() {
}
#[test]
fn v0_3_16_pre_006_shared_comparator_promotes_only_more_complete_and_keeps_conflict_deferred() {
fn v0_3_16_pre_007_shared_comparator_promotes_more_complete_and_quarantines_conflict() {
let source = include_str!("../src/raw_transaction.rs");
assert!(source.contains("ksp_store_api::compare_raw_transaction_variants"));
assert!(source.contains("ExistingTransactionMatch::ActiveCompatibleLessComplete"));
@@ -70,7 +70,44 @@ fn v0_3_16_pre_006_shared_comparator_promotes_only_more_complete_and_keeps_confl
assert!(source.contains("UPDATE ksp_raw_transactions SET slot = $2::TEXT::NUMERIC"));
assert!(source.contains("PostgreSQL Store committed a proved RAW transaction canonical promotion"));
assert!(!source.contains("log_raw_transaction_compatible_more_complete_log_messages"));
assert!(!source.contains("INSERT INTO ksp_raw_transaction_conflicts"));
assert!(source.contains("INSERT INTO ksp_raw_transaction_conflicts"));
assert!(source.contains("ExistingTransactionMatch::ActiveConflict"));
assert!(source.contains("RawTransactionVariantWriteOutcome::QuarantinedConflict"));
return;
}
#[test]
fn v0_3_16_pre_007_conflict_is_persisted_inside_acquisition_transaction_before_observation_and_commit() {
let source = include_str!("../src/raw_transaction.rs");
let start = source.find("pub(crate) async fn persist_raw_transaction_acquisition(");
let end = source.find("pub(crate) async fn record_raw_transaction_observation(");
let (start, end) = match (start, end) {
(std::option::Option::Some(start), std::option::Option::Some(end)) if start < end => (start, end),
_ => panic!("pre.007 acquisition function markers are missing"),
};
let body = &source[start..end];
let persist_variant = body.find("persist_or_reuse_native_transaction_variant");
let conflict = body.find("open_or_update_raw_transaction_conflict");
let observation = body.find("persist_observation_row");
let commit = body.rfind("sql_transaction.commit().await");
let transition_log = body.find("log_raw_transaction_conflict_quarantined_committed");
assert!(matches!(
(persist_variant, conflict, observation, commit, transition_log),
(Some(persist_variant), Some(conflict), Some(observation), Some(commit), Some(transition_log))
if persist_variant < conflict && conflict < observation && observation < commit && commit < transition_log
));
assert!(body.contains("RawTransactionVariantWriteOutcome::QuarantinedConflict"));
let conflict_start = source.find("async fn open_or_update_raw_transaction_conflict(");
let conflict_end = source.find("async fn persist_observation_row(");
let (conflict_start, conflict_end) = match (conflict_start, conflict_end) {
(std::option::Option::Some(start), std::option::Option::Some(end)) if start < end => (start, end),
_ => panic!("pre.007 conflict helper markers are missing"),
};
let conflict_helper = &source[conflict_start..conflict_end];
assert!(conflict_helper.contains("LOCK_TRANSACTION_CONFLICT_SQL"));
assert!(conflict_helper.contains("INSERT_TRANSACTION_CONFLICT_SQL"));
assert!(conflict_helper.contains("UPDATE_TRANSACTION_CONFLICT_SQL"));
assert!(conflict_helper.contains("checked_add(1)"));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/unit_tests/raw_transaction.rs
// version: 13
// version: 14
fn network() -> ksp_store_api::RawNetworkId {
return match ksp_store_api::RawNetworkId::new("devnet") {
@@ -900,3 +900,16 @@ fn v0_3_16_pre_006_promotion_sql_is_compare_guarded_and_projection_complete() {
assert!(!super::UPDATE_TRANSACTION_VARIANT_TO_FULL_SQL.contains("DELETE"));
return;
}
#[test]
fn v0_3_16_pre_007_conflict_sql_is_locked_revision_guarded_and_reopenable() {
assert!(super::LOCK_TRANSACTION_CONFLICT_SQL.contains("FOR UPDATE"));
assert!(super::INSERT_TRANSACTION_CONFLICT_SQL.contains("status, revision"));
assert!(super::INSERT_TRANSACTION_CONFLICT_SQL.contains("'open', 1"));
assert!(super::UPDATE_TRANSACTION_CONFLICT_SQL.contains("status = 'open'"));
assert!(super::UPDATE_TRANSACTION_CONFLICT_SQL.contains("revision = $2::TEXT::NUMERIC"));
assert!(super::UPDATE_TRANSACTION_CONFLICT_SQL.contains("AND revision = $8::TEXT::NUMERIC"));
assert!(super::UPDATE_TRANSACTION_CONFLICT_SQL.contains("canonical_variant_id = $3::TEXT::NUMERIC"));
assert!(super::UPDATE_TRANSACTION_CONFLICT_SQL.contains("incoming_variant_id = $4::TEXT::NUMERIC"));
return;
}