pg queue/tx/notify dedupe + doc truth-telling: job_from_row has one owner (queue.rs, tx.rs imports it) and get_job_tx reuses live_columns()/dead_columns() instead of inlining the 18-column lists — the contract-pinned Job shape now has exactly one decode owner (ADR-012 §2); sweep_expired's doc states the moved + retention-deleted sum in the in_tx multi-statement frame (the sum the SQLite twin returns — the doc was the only liar); notify.rs's closed-store listen error routes through the shared database_error helper; bridge_capacity() is a const with its rationale doc carried. No behavior change — identical SQL strings, error shapes, and capacity. (task pg-fix-dedupe-cleanup, review 002 minor notes + Finding 6 queue bullet)

This commit is contained in:
glm-5.3-flash committed 2026-10-10 05:04:09 +00:00
1 parent 12d0497b1c
commit 40625090f6
4 files changed
+61 -77

No files matched your search

+14 -61
View File
@@ -84,10 +84,11 @@ use serde_json::Value;
use tokio::runtime::Handle;
use alkstore::{
BoxedFuture, EnqueueOpts, Error, Job, JobState, Result, StreamEvent, TxHandle,
validate_local_name, validate_shared_name,
BoxedFuture, EnqueueOpts, Error, Job, Result, StreamEvent, TxHandle, validate_local_name,
validate_shared_name,
};
use crate::queue::{dead_columns, job_from_row, live_columns};
use crate::resolution::{
ResolvedEnqueue, outbox_backing_queue_name, outbox_default_stamps, plain_queue_default_stamps,
resolve_enqueue_opts, stamps_with_override,
@@ -471,13 +472,9 @@ impl TxHandle for PgTxHandle {
let row = client
.query_opt(
&format!(
"SELECT id, queue, 'dead', payload, priority, run_at, attempts,
max_attempts, worker_id, claimed_at, claim_expires_at,
created_at, expires_at, visibility_timeout_s,
backoff_base_s, dead_letter_retention_s,
last_error, died_at
FROM {} WHERE id = $1 AND queue = $2",
self.table(tables::DEAD)
"SELECT {dead} FROM {deadt} WHERE id = $1 AND queue = $2",
dead = dead_columns(),
deadt = self.table(tables::DEAD)
),
&[&job_id, &queue],
)
@@ -489,13 +486,9 @@ impl TxHandle for PgTxHandle {
let row = client
.query_opt(
&format!(
"SELECT id, queue, state, payload, priority, run_at, attempts,
max_attempts, worker_id, claimed_at, claim_expires_at,
created_at, expires_at, visibility_timeout_s,
backoff_base_s, dead_letter_retention_s,
NULL::text, NULL::bigint
FROM {} WHERE id = $1 AND queue = $2",
self.table(tables::JOB)
"SELECT {live} FROM {jobt} WHERE id = $1 AND queue = $2",
live = live_columns(),
jobt = self.table(tables::JOB)
),
&[&job_id, &queue],
)
@@ -678,48 +671,8 @@ impl Drop for PgTxHandle {
}
}
/// Decode a job row (the uniform 18-column shape — the dead-table
/// select carries `last_error`/`died_at`; the live one binds NULLs)
/// into the contract value (`Job::from_row` — the `#[doc(hidden)]`
/// constructor, ADR-021 §2's field list of record). Malformed rows map
/// to `Error::Codec` (the decode-side posture guard).
fn job_from_row(row: &tokio_postgres::Row) -> Result<Job> {
let state = match row.get::<_, Option<&str>>(2) {
Some("pending") => JobState::Pending,
Some("processing") => JobState::Processing,
Some("dead") => JobState::Dead,
other => {
return Err(Error::Codec(format!(
"job row state must be pending|processing|dead, got {other:?}"
)));
}
};
let payload: Option<Vec<u8>> = row
.try_get(3)
.map_err(|e| Error::Codec(format!("job row payload must be bytea: {e}")))?;
Ok(Job::from_row(
row.get(0),
row.get(1),
state,
payload.unwrap_or_default(),
row.get(4),
row.get(5),
row.get(6),
row.get(7),
row.get(8),
row.get(9),
row.get(10),
row.get(11),
row.get(12),
row.get(13),
row.get(14),
row.get(15),
row.get(16),
row.get(17),
))
}
// The stream-page decode (`stream_events_from_rows`) lives in
// `stream.rs` — the one decode owner the tx reads and the auto-commit
// reads share (the `stream` field mapping and the `Codec` posture
// guard live once; ADR-012 §2's one-owner rule).
// The row decodes ride one owner each: the stream-page decode
// (`stream_events_from_rows`) lives in `stream.rs`, the job-row
// decode (`job_from_row` + the select column lists) in `queue.rs` —
// the one decode owners the tx reads and the auto-commit reads share
// (ADR-012 §2's one-owner rule).