···89pub use backfill::backfill;
10pub use client::CLIENT;
11-pub use plc_pg::Db;
12pub use poll::{get_page, poll_upstream};
13pub use weekly::{BundleSource, FolderSource, HttpSource, Week, pages_to_weeks, week_to_pages};
14
···89pub use backfill::backfill;
10pub use client::CLIENT;
11+pub use plc_pg::{Db, write_bulk};
12pub use poll::{get_page, poll_upstream};
13pub use weekly::{BundleSource, FolderSource, HttpSource, Week, pages_to_weeks, week_to_pages};
14
+61-1
src/plc_pg.rs
···1-use tokio_postgres::{Client, Error as PgError, NoTls, connect};
00023/// a little tokio-postgres helper
0004#[derive(Debug, Clone)]
5pub struct Db {
6 pg_uri: String,
···29 Ok(client)
30 }
31}
000000000000000000000000000000000000000000000000000000
···1+use crate::{ExportPage, Op};
2+use tokio_postgres::{Client, types::{Type, Json}, Error as PgError, NoTls, connect, binary_copy::BinaryCopyInWriter};
3+use std::pin::pin;
4+56/// a little tokio-postgres helper
7+///
8+/// it's clone for easiness. it doesn't share any resources underneath after
9+/// cloning at all so it's not meant for
10#[derive(Debug, Clone)]
11pub struct Db {
12 pg_uri: String,
···35 Ok(client)
36 }
37}
38+39+pub async fn write_bulk(
40+ db: Db,
41+ pages: flume::Receiver<ExportPage>,
42+) -> Result<(), PgError> {
43+ let mut client = db.connect().await?;
44+ let tx = client.transaction().await?;
45+46+ tx
47+ .execute(r#"
48+ CREATE TABLE backfill (
49+ did text not null,
50+ cid text not null,
51+ operation jsonb not null,
52+ nullified boolean not null,
53+ createdAt timestamptz not null
54+ )"#, &[])
55+ .await?;
56+57+58+ let types = &[
59+ Type::TEXT,
60+ Type::TEXT,
61+ Type::JSONB,
62+ Type::BOOL,
63+ Type::TIMESTAMPTZ,
64+ ];
65+66+ let sync = tx.copy_in("COPY backfill FROM STDIN BINARY").await?;
67+ let mut writer = pin!(BinaryCopyInWriter::new(sync, types));
68+69+ while let Ok(page) = pages.recv_async().await {
70+ for s in page.ops {
71+ let Ok(op) = serde_json::from_str::<Op>(&s) else {
72+ log::warn!("ignoring unparseable op: {s:?}");
73+ continue;
74+ };
75+ writer.as_mut().write(&[
76+ &op.did,
77+ &op.cid,
78+ &Json(op.operation),
79+ &op.nullified,
80+ &op.created_at,
81+ ]).await?;
82+ }
83+ }
84+85+ let n = writer.as_mut().finish().await?;
86+ log::info!("copied in {n} rows");
87+88+ tx.commit().await?;
89+90+ Ok(())
91+}