Server tools to backfill, tail, mirror, and verify PLC logs
50
fork

Configure Feed

Select the types of activity you want to include in your feed.

add example for op polling

phil 9011ebeb 3592014b

+89 -2
+35
examples/poll.rs
··· 1 + use allegedly::{ExportPage, poll_upstream}; 2 + 3 + #[tokio::main] 4 + async fn main() { 5 + // set to `None` to replay from the beginning of the PLC history 6 + let after = Some(chrono::Utc::now()); 7 + 8 + // the PLC server to poll for new ops 9 + let upstream = "https://plc.wtf/export".parse().unwrap(); 10 + 11 + // self-rate-limit (plc.directory's limit interval is 600ms) 12 + let throttle = std::time::Duration::from_millis(300); 13 + 14 + // pages are sent out of the poller via a tokio mpsc channel 15 + let (tx, mut rx) = tokio::sync::mpsc::channel(1); 16 + 17 + // spawn a tokio task to run the poller 18 + tokio::task::spawn(poll_upstream(after, upstream, throttle, tx)); 19 + 20 + // receive pages of plc ops from the poller 21 + while let Some(ExportPage { ops }) = rx.recv().await { 22 + println!("received {} plc ops", ops.len()); 23 + 24 + for op in ops { 25 + // in this example we're alerting when changes are found for one 26 + // specific identity 27 + if op.did == "did:plc:hdhoaan3xa3jiuq4fg4mefid" { 28 + println!( 29 + "Update found for {}! cid={}\n -> operation: {}", 30 + op.did, op.cid, op.operation.get() 31 + ); 32 + } 33 + } 34 + } 35 + }
+54 -2
src/poll.rs
··· 51 51 } 52 52 } 53 53 54 - /// PLC 54 + /// State for removing duplicates ops between PLC export page boundaries 55 55 #[derive(Debug, PartialEq)] 56 56 pub struct PageBoundaryState { 57 + /// The previous page's last timestamp 58 + /// 59 + /// Duplicate ops from /export only occur for the same exact timestamp 57 60 pub last_at: Dt, 61 + /// The previous page's ops at its last timestamp 58 62 keys_at: Vec<OpKey>, // expected to ~always be length one 59 63 } 60 64 61 - /// track keys at final createdAt to deduplicate the start of the next page 62 65 impl PageBoundaryState { 66 + /// Initialize the boundary state with a PLC page 63 67 pub fn new(page: &ExportPage) -> Option<Self> { 64 68 // grab the very last op 65 69 let (last_at, last_key) = page.ops.last().map(|op| (op.created_at, op.into()))?; ··· 75 79 76 80 Some(me) 77 81 } 82 + /// Apply the deduplication and update state 83 + /// 84 + /// The beginning of the page will be modified to remove duplicates from the 85 + /// previous page. 86 + /// 87 + /// The end of the page is inspected to update the deduplicator state for 88 + /// the next page. 78 89 fn apply_to_next(&mut self, page: &mut ExportPage) { 79 90 // walk ops forward, kicking previously-seen ops until created_at advances 80 91 let to_remove: Vec<usize> = page ··· 124 135 } 125 136 } 126 137 138 + /// Get one PLC export page 139 + /// 140 + /// Extracts the final op so it can be used to fetch the following page 127 141 pub async fn get_page(url: Url) -> Result<(ExportPage, Option<LastOp>), GetPageError> { 128 142 log::trace!("Getting page: {url}"); 129 143 ··· 152 166 Ok((ExportPage { ops }, last_op)) 153 167 } 154 168 169 + /// Poll an upstream PLC server for new ops 170 + /// 171 + /// Pages of operations are written to the `dest` channel. 172 + /// 173 + /// ```no_run 174 + /// # #[tokio::main] 175 + /// # async fn main() { 176 + /// use allegedly::{ExportPage, Op, poll_upstream}; 177 + /// 178 + /// // set to `None` to replay from the beginning of the PLC history 179 + /// let after = Some(chrono::Utc::now()); 180 + /// 181 + /// // the PLC server to poll for new ops 182 + /// let upstream = "https://plc.wtf/export".parse().unwrap(); 183 + /// 184 + /// // self-rate-limit (plc.directory's limit interval is 600ms) 185 + /// let throttle = std::time::Duration::from_millis(300); 186 + /// 187 + /// // pages are sent out of the poller via a tokio mpsc channel 188 + /// let (tx, mut rx) = tokio::sync::mpsc::channel(1); 189 + /// 190 + /// // spawn a tokio task to run the poller 191 + /// tokio::task::spawn(poll_upstream(after, upstream, throttle, tx)); 192 + /// 193 + /// // receive pages of plc ops from the poller 194 + /// while let Some(ExportPage { ops }) = rx.recv().await { 195 + /// println!("received {} plc ops", ops.len()); 196 + /// 197 + /// for Op { did, cid, operation, .. } in ops { 198 + /// // in this example we're alerting when changes are found for one 199 + /// // specific identity 200 + /// if did == "did:plc:hdhoaan3xa3jiuq4fg4mefid" { 201 + /// println!("Update found for {did}! cid={cid}\n -> operation: {}", operation.get()); 202 + /// } 203 + /// } 204 + /// } 205 + /// # } 206 + /// ``` 155 207 pub async fn poll_upstream( 156 208 after: Option<Dt>, 157 209 base: Url,