···2 Db, Dt, ExportPage, FjallDb, FolderSource, HttpSource, backfill, backfill_to_fjall,
3 backfill_to_pg,
4 bin::{GlobalArgs, bin_init},
5- fjall_to_pages, full_pages, logo, pages_to_fjall, pages_to_pg, pages_to_stdout, poll_upstream,
6};
7use clap::Parser;
8use reqwest::Url;
···139 log::trace!("opening source fjall db at {fjall_path:?}...");
140 let db = FjallDb::open(&fjall_path)?;
141 log::trace!("opened source fjall db");
142- tasks.spawn(fjall_to_pages(db, bulk_tx, until));
143 } else if let Some(dir) = dir {
144 if http != DEFAULT_HTTP.parse()? {
145 anyhow::bail!(
···2 Db, Dt, ExportPage, FjallDb, FolderSource, HttpSource, backfill, backfill_to_fjall,
3 backfill_to_pg,
4 bin::{GlobalArgs, bin_init},
5+ full_pages, logo, pages_to_fjall, pages_to_pg, pages_to_stdout, poll_upstream,
6};
7use clap::Parser;
8use reqwest::Url;
···139 log::trace!("opening source fjall db at {fjall_path:?}...");
140 let db = FjallDb::open(&fjall_path)?;
141 log::trace!("opened source fjall db");
142+ tasks.spawn(backfill(db, bulk_tx, source_workers.unwrap_or(4), until));
143 } else if let Some(dir) = dir {
144 if http != DEFAULT_HTTP.parse()? {
145 anyhow::bail!(
+1-1
src/lib.rs
···19pub use cached_value::{CachedValue, Fetcher};
20pub use client::{CLIENT, UA};
21pub use mirror::{ExperimentalConf, ListenConf, serve, serve_fjall};
22-pub use plc_fjall::{FjallDb, backfill_to_fjall, fjall_to_pages, pages_to_fjall};
23pub use plc_pg::{Db, backfill_to_pg, pages_to_pg};
24pub use poll::{PageBoundaryState, get_page, poll_upstream};
25pub use ratelimit::{CreatePlcOpLimiter, GovernorMiddleware, IpLimiters};
···19pub use cached_value::{CachedValue, Fetcher};
20pub use client::{CLIENT, UA};
21pub use mirror::{ExperimentalConf, ListenConf, serve, serve_fjall};
22+pub use plc_fjall::{FjallDb, backfill_to_fjall, pages_to_fjall};
23pub use plc_pg::{Db, backfill_to_pg, pages_to_pg};
24pub use poll::{PageBoundaryState, get_page, poll_upstream};
25pub use ratelimit::{CreatePlcOpLimiter, GovernorMiddleware, IpLimiters};
+2-2
src/mirror/fjall.rs
···275 let db = fjall.clone();
276277 let ops = tokio::task::spawn_blocking(move || {
278- let iter = db.export_ops(after, limit)?;
279- iter.collect::<anyhow::Result<Vec<_>>>()
280 })
281 .await
282 .map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?
···275 let db = fjall.clone();
276277 let ops = tokio::task::spawn_blocking(move || {
278+ let iter = db.export_ops(after.unwrap_or(Dt::UNIX_EPOCH)..)?;
279+ iter.take(limit).collect::<anyhow::Result<Vec<_>>>()
280 })
281 .await
282 .map_err(|e| Error::from_string(e.to_string(), StatusCode::INTERNAL_SERVER_ERROR))?
+67-70
src/plc_fjall.rs
···01use crate::{Dt, ExportPage, Op as CommonOp, PageBoundaryState};
2use anyhow::Context;
3use data_encoding::{BASE32_NOPAD, BASE64URL_NOPAD};
···5 Database, Keyspace, KeyspaceCreateOptions, OwnedWriteBatch, PersistMode,
6 config::BlockSizePolicy,
7};
08use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::path::Path;
12use std::sync::Arc;
13use std::time::Instant;
014use tokio::sync::{mpsc, oneshot};
1516const SEP: u8 = 0;
···10221023 pub fn export_ops(
1024 &self,
1025- after: Option<Dt>,
1026- limit: usize,
1027 ) -> anyhow::Result<impl Iterator<Item = anyhow::Result<Op>> + '_> {
1028- let iter = if let Some(after) = after {
1029- let start = (after.timestamp_micros() as u64).to_be_bytes();
1030- self.inner.ops.range(start..)
1031- } else {
1032- self.inner.ops.iter()
001033 };
010341035- Ok(iter.take(limit).map(|item| {
001036 let (key, value) = item
1037 .into_inner()
1038 .map_err(|e| anyhow::anyhow!("fjall read error: {e}"))?;
···1060 })
1061 }))
1062 }
00000000000000000000000000000000000000000000000000001063}
10641065pub async fn backfill_to_fjall(
···1149 t0.elapsed()
1150 );
1151 Ok("pages_to_fjall")
1152-}
1153-1154-pub async fn fjall_to_pages(
1155- db: FjallDb,
1156- dest: mpsc::Sender<ExportPage>,
1157- until: Option<Dt>,
1158-) -> anyhow::Result<&'static str> {
1159- log::info!("starting fjall_to_pages backfill source...");
1160-1161- let t0 = Instant::now();
1162-1163- let dest_clone = dest.clone();
1164- let ops_sent = tokio::task::spawn_blocking(move || -> anyhow::Result<usize> {
1165- let iter = db.export_ops(None, usize::MAX)?;
1166- let mut current_page = Vec::with_capacity(1000);
1167- let mut count = 0;
1168-1169- for op_res in iter {
1170- let op = op_res?;
1171-1172- if let Some(u) = until {
1173- if op.created_at >= u {
1174- break;
1175- }
1176- }
1177-1178- let operation_str = serde_json::to_string(&op.operation)?;
1179- let common_op = crate::Op {
1180- did: op.did,
1181- cid: op.cid,
1182- created_at: op.created_at,
1183- nullified: op.nullified,
1184- operation: serde_json::value::RawValue::from_string(operation_str)?,
1185- };
1186-1187- current_page.push(common_op);
1188- count += 1;
1189-1190- if current_page.len() >= 1000 {
1191- let page = ExportPage {
1192- ops: std::mem::take(&mut current_page),
1193- };
1194- if dest_clone.blocking_send(page).is_err() {
1195- break;
1196- }
1197- }
1198- }
1199-1200- if !current_page.is_empty() {
1201- let page = ExportPage { ops: current_page };
1202- let _ = dest_clone.blocking_send(page);
1203- }
1204-1205- Ok(count)
1206- })
1207- .await??;
1208-1209- log::info!(
1210- "finished sending {ops_sent} ops from fjall in {:?}",
1211- t0.elapsed()
1212- );
1213- Ok("fjall_to_pages")
1214}
12151216#[cfg(test)]