mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
feat(relay): enable SQLite command persistence
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
This commit is contained in:
parent
1971f71a06
commit
168168d8fa
@@ -4818,15 +4818,28 @@ impl Db {
|
||||
}
|
||||
|
||||
/// Ensures monthly partitions exist for the next N months.
|
||||
///
|
||||
/// SQLite stores events in an unpartitioned local table, so this is an
|
||||
/// intentional single-node no-op.
|
||||
pub async fn ensure_future_partitions(&self, months_ahead: u32) -> Result<()> {
|
||||
partition::ensure_future_partitions(self.pg_pool()?, months_ahead).await
|
||||
match &self.backend {
|
||||
DbBackend::SQLite(_) => Ok(()),
|
||||
DbBackend::Postgres => {
|
||||
partition::ensure_future_partitions(self.pg_pool()?, months_ahead).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Backfill `d_tag` for existing NIP-33 events (kind 30000–39999) that have `d_tag IS NULL`.
|
||||
///
|
||||
/// Idempotent — safe to call on every startup. No-ops when all rows are already populated.
|
||||
/// Runs a single UPDATE touching only NIP-33 rows with NULL d_tag.
|
||||
/// Runs a single UPDATE touching only NIP-33 rows with NULL d_tag. SQLite
|
||||
/// derives d-tags from the persisted event JSON and has no denormalized
|
||||
/// `d_tag` column, so its profile deliberately returns zero.
|
||||
pub async fn backfill_d_tags(&self) -> Result<u64> {
|
||||
if matches!(&self.backend, DbBackend::SQLite(_)) {
|
||||
return Ok(0);
|
||||
}
|
||||
let result = sqlx::query(
|
||||
"UPDATE events \
|
||||
SET d_tag = COALESCE( \
|
||||
|
||||
@@ -119,11 +119,35 @@ async fn persist_command_event(
|
||||
channel_id_override: Option<Uuid>,
|
||||
) -> Result<PersistResult, IngestError> {
|
||||
let channel_id = channel_id_override.or_else(|| extract_channel_id(event));
|
||||
let d_tag = buzz_db::event::extract_d_tag(event);
|
||||
if let Some(ref d_tag) = d_tag {
|
||||
if d_tag.len() > buzz_db::event::D_TAG_MAX_LEN {
|
||||
return Err(IngestError::Rejected(format!(
|
||||
"invalid: d tag too long ({} bytes, max {})",
|
||||
d_tag.len(),
|
||||
buzz_db::event::D_TAG_MAX_LEN,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if state.config.profile.is_single_node() {
|
||||
return Err(IngestError::Rejected(
|
||||
"unsupported_feature: this command is unavailable in the single-node profile".into(),
|
||||
));
|
||||
let result = if let Some(d_tag) = d_tag.as_deref() {
|
||||
state
|
||||
.db
|
||||
.replace_parameterized_event(tenant.community(), event, d_tag, channel_id)
|
||||
.await
|
||||
} else {
|
||||
state
|
||||
.db
|
||||
.insert_event(tenant.community(), event, channel_id)
|
||||
.await
|
||||
}
|
||||
.map_err(|e| IngestError::Internal(format!("error: persist SQLite command: {e}")))?;
|
||||
return Ok(if result.1 {
|
||||
PersistResult::Inserted(CommandTransaction::Sqlite)
|
||||
} else {
|
||||
PersistResult::Duplicate
|
||||
});
|
||||
}
|
||||
|
||||
let mut tx = state
|
||||
@@ -146,16 +170,7 @@ async fn persist_command_event(
|
||||
let received_at = chrono::Utc::now();
|
||||
|
||||
// Extract d_tag for parameterized replaceable kinds (NIP-33).
|
||||
let d_tag = buzz_db::event::extract_d_tag(event);
|
||||
if let Some(ref d_tag) = d_tag {
|
||||
if d_tag.len() > buzz_db::event::D_TAG_MAX_LEN {
|
||||
return Err(IngestError::Rejected(format!(
|
||||
"invalid: d tag too long ({} bytes, max {})",
|
||||
d_tag.len(),
|
||||
buzz_db::event::D_TAG_MAX_LEN,
|
||||
)));
|
||||
}
|
||||
|
||||
// Command kinds normally use plain insert semantics, but workflow
|
||||
// definitions are NIP-33 events. Serialize writers for the same
|
||||
// coordinate and reject stale writes before executing the domain
|
||||
|
||||
Reference in New Issue
Block a user