{"record":{"id":"e192313ad2cf0ac3","repo":"LemmyNet/lemmy","slug":"err-getting-activity-e-e19231","errorCode":null,"errorMessage":"err getting activity: {e:?}","messagePattern":"err getting activity: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/apub/send/src/util.rs","lineNumber":193,"sourceCode":"}\n\ntype CachedActivityInfo = Option<Arc<SentActivity>>;\n/// activities are immutable so cache does not need to have TTL\n/// May return None if the corresponding id does not exist or is a received activity.\n/// Holes in serials are expected behaviour in postgresql\n/// todo: cache size should probably be configurable / dependent on desired memory usage\npub(crate) async fn get_activity_cached(\n  pool: &mut DbPool<'_>,\n  activity_id: ActivityId,\n) -> Result<CachedActivityInfo> {\n  static ACTIVITIES: LazyLock<Cache<ActivityId, CachedActivityInfo>> =\n    LazyLock::new(|| Cache::builder().max_capacity(10000).build());\n  ACTIVITIES\n    .try_get_with(activity_id, async {\n      Ok(Some(Arc::new(SentActivity::read(pool, activity_id).await?)))\n    })\n    .await\n    .map_err(|e: Arc<LemmyError>| anyhow::anyhow!(\"err getting activity: {e:?}\"))\n}\n\n/// return the most current activity id (with 1 second cache)\npub(crate) async fn get_latest_activity_id(pool: &mut DbPool<'_>) -> Result<Option<ActivityId>> {\n  static CACHE: LazyLock<Cache<(), Option<ActivityId>>> = LazyLock::new(|| {\n    Cache::builder()\n      .time_to_live(*CACHE_DURATION_LATEST_ID)\n      .build()\n  });\n  CACHE\n    .try_get_with((), async {\n      use lemmy_db_schema_file::schema::sent_activity::dsl::{id, sent_activity};\n      let conn = &mut get_conn(pool).await?;\n      let latest_id: Option<ActivityId> = sent_activity\n        .select(diesel::dsl::max(id))\n        .get_result(conn)\n        .await?;\n      anyhow::Result::<_, anyhow::Error>::Ok(latest_id)","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/LemmyNet/lemmy/blob/439734dd638a2c06a2f907beab7dcf4646e88f86/crates/apub/send/src/util.rs#L175-L211","documentation":"get_activity_cached loads a SentActivity by id through a moka cache (try_get_with); when the underlying read fails, the LemmyError (wrapped in Arc) is converted into this anyhow error. Holes in activity serial ids are normal in PostgreSQL, so a missing id surfaces as this error too.","triggerScenarios":"spawn_send_if_needed calls get_activity_cached with an activity_id that does not exist in sent_activities (serial gap) or the DB read fails (connection/query error).","commonSituations":"PostgreSQL sequence gaps after rollbacks/crashes cause the queue to reference ids that were never committed; transient DB outages during federation; read replicas lagging.","solutions":["Confirm whether the id is a normal PostgreSQL sequence hole — treat as benign and skip","Check DB health/logs for the underlying query failure","Retry once the database is reachable; the cache will re-fetch","If gaps recur and block the queue, adjust the loop to skip missing ids instead of failing"],"exampleFix":"// before\nlet activity = get_activity_cached(pool, activity_id).await?; // aborts on gap\n// after\nlet activity = match get_activity_cached(pool, activity_id).await {\n  Ok(a) => a,\n  Err(e) => { warn!(\"skipping activity {activity_id}: {e:?}\"); continue; }\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match get_activity_cached(pool, id).await {\n  Ok(a) => process(a),\n  Err(e) => { warn!(\"activity {id} unavailable (maybe serial gap): {e:?}\"); skip(id); }\n}","preventionTips":["Treat missing serial ids as expected PostgreSQL behavior and skip them","Cache failures are not permanent — retry on transient DB errors","Monitor sent_activities for abnormal gap growth"],"tags":["database","cache","federation","activity-id"],"backgroundTag":"record-not-found","analyzedSha":"439734dd638a2c06a2f907beab7dcf4646e88f86","analyzedAt":"2026-09-06T11:28:35.035Z","contentChangedAt":"2026-09-06T11:28:35.035Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}