mirror of
https://github.com/Dicklesworthstone/coding_agent_session_search.git
synced 2026-09-22 11:59:25 +08:00
97 lines
4.5 KiB
Diff
97 lines
4.5 KiB
Diff
--- a/src/indexer/mod.rs
|
|
+++ b/src/indexer/mod.rs
|
|
@@ -33307,18 +33307,25 @@
|
|
kind: AgentKind::Cli,
|
|
};
|
|
|
|
- let agent_id = if cache_enabled {
|
|
- cache.get_or_insert_agent(writer, &agent)?
|
|
- } else {
|
|
- writer.ensure_agent(&agent)?
|
|
- };
|
|
+ // These idempotent parent upserts precede the canonical
|
|
+ // chunk transaction. They need their own bounded contention
|
|
+ // retry when a warm writer encounters a new identity (GH473).
|
|
+ let agent_id = with_concurrent_retry(SERIAL_CHUNK_CONTENTION_RETRIES, || {
|
|
+ if cache_enabled {
|
|
+ cache.get_or_insert_agent(writer, &agent)
|
|
+ } else {
|
|
+ writer.ensure_agent(&agent)
|
|
+ }
|
|
+ })?;
|
|
|
|
let workspace_id = if let Some(ws) = &conv.workspace {
|
|
- if cache_enabled {
|
|
- Some(cache.get_or_insert_workspace(writer, ws, None)?)
|
|
- } else {
|
|
- Some(writer.ensure_workspace(ws, None)?)
|
|
- }
|
|
+ Some(with_concurrent_retry(SERIAL_CHUNK_CONTENTION_RETRIES, || {
|
|
+ if cache_enabled {
|
|
+ cache.get_or_insert_workspace(writer, ws, None)
|
|
+ } else {
|
|
+ writer.ensure_workspace(ws, None)
|
|
+ }
|
|
+ })?)
|
|
} else {
|
|
None
|
|
};
|
|
--- a/src/indexer/gh473_tests.rs
|
|
+++ b/src/indexer/gh473_tests.rs
|
|
@@ -371,6 +371,46 @@
|
|
storage.close().unwrap();
|
|
}
|
|
|
|
+ fn warm_writer_parent_registration_retries(workspace_only: bool) {
|
|
+ let tmp = TempDir::new().unwrap();
|
|
+ let path = tmp.path().join("parent-retry.db");
|
|
+ let storage = FrankenStorage::open(&path).unwrap();
|
|
+ let mut conversation = norm_conv(Some("gh473-parent"), vec![norm_msg(0, 100)]);
|
|
+ conversation.workspace = Some(tmp.path().join("workspace"));
|
|
+ let completion = crate::storage::sqlite::SourceIngestLedgerEntry {
|
|
+ key: "source_ingest_v1:gh473-parent".into(),
|
|
+ observation: "complete".into(),
|
|
+ };
|
|
+ persist::with_ephemeral_writer(&storage, false, "GH473 warm setup", |writer| {
|
|
+ if workspace_only {
|
|
+ writer.ensure_agent(&crate::model::types::Agent {
|
|
+ id: None,
|
|
+ slug: conversation.agent_slug.clone(),
|
|
+ name: conversation.agent_slug.clone(),
|
|
+ version: None,
|
|
+ kind: crate::model::types::AgentKind::Cli,
|
|
+ })?;
|
|
+ }
|
|
+ Ok(())
|
|
+ }).unwrap();
|
|
+ assert!(storage.ephemeral_writer_preflight_verified());
|
|
+ let (result, retries) = with_held_writer(&path, true, || {
|
|
+ persist::persist_conversations_batched_inner(
|
|
+ &storage, None, std::slice::from_ref(&conversation),
|
|
+ LexicalPopulationStrategy::DeferredAuthoritativeDbRebuild,
|
|
+ false, false, None, persist::PersistHeartbeat::NONE, Some(&completion),
|
|
+ )
|
|
+ });
|
|
+ let result = result.unwrap();
|
|
+ assert_eq!(retries, 1, "warm setup must retry the uncached parent write");
|
|
+ assert_eq!((result.inserted_conversations, result.inserted_messages), (1, 1));
|
|
+ assert_eq!(scalar(&storage, "SELECT COUNT(*) FROM workspaces"), 1);
|
|
+ assert_eq!(scalar(&storage, "SELECT COUNT(*) FROM conversations"), 1);
|
|
+ assert_eq!(scalar(&storage, "SELECT COUNT(*) FROM messages"), 1);
|
|
+ assert_eq!(storage.source_ingest_ledger_entries().unwrap().get(&completion.key), Some(&completion.observation));
|
|
+ storage.close().unwrap();
|
|
+ }
|
|
+
|
|
#[test]
|
|
fn writer_preflight_contention_regression() {
|
|
const CHILD: &str = "CASS_TEST_GH473_PREFLIGHT_CHILD";
|
|
@@ -383,6 +423,8 @@
|
|
}
|
|
permanent_error_is_not_retried();
|
|
writer_body_errors_are_never_replayed();
|
|
+ warm_writer_parent_registration_retries(false);
|
|
+ warm_writer_parent_registration_retries(true);
|
|
canonical_contention_preserves_completion_and_lexical_state();
|
|
canonical_write_and_replay_with_pinned_reader(expected);
|
|
return;
|