See the best posts from any Bluesky account
0
fork

Configure Feed

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

add tombstoneUserSnapshots to ClickHouseStore

Bulk-tombstones all non-deleted post snapshots for an author via a
single INSERT ... SELECT FROM ... FINAL, using positional column
mapping to avoid ClickHouse's alias-shadowing issue in WHERE clauses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+48
+48
packages/clickhouse/src/store.ts
··· 306 306 ) 307 307 } 308 308 } 309 + 310 + /** 311 + * Bulk-tombstones all currently non-deleted post snapshots for an author. 312 + * 313 + * Uses a single INSERT ... SELECT to copy every non-deleted row for the 314 + * author with is_deleted=1 and a fresh snapshot_taken_at. The 315 + * ReplacingMergeTree engine will, on the next merge, pick the row with 316 + * the largest snapshot_taken_at per (post_author_did, post_uri), and the 317 + * is_deleted=1 rows win because they have a newer snapshot_taken_at. 318 + * 319 + * Called when an account deletion or takedown event arrives for a tracked user. 320 + */ 321 + async tombstoneUserSnapshots(authorDid: string): Promise<void> { 322 + // NOTE: Column aliases (e.g. "now64(6) AS snapshot_taken_at") are intentionally 323 + // omitted from this INSERT SELECT because ClickHouse evaluates SELECT aliases 324 + // before the WHERE clause — so "1 AS is_deleted" in the SELECT would shadow the 325 + // column reference in "WHERE is_deleted = 0", causing the filter to evaluate as 326 + // "1 = 0" (always false) and return zero rows. Using positional column mapping 327 + // (column order instead of aliases) avoids this aliasing trap. 328 + const sql = ` 329 + INSERT INTO post_snapshots 330 + SELECT 331 + post_uri, 332 + post_author_did, 333 + post_text, 334 + post_created_at, 335 + snapshot_likes, 336 + snapshot_reposts, 337 + snapshot_quotes, 338 + now64(6), 339 + toUInt8(1) 340 + FROM post_snapshots FINAL 341 + WHERE post_author_did = {authorDid:String} 342 + AND is_deleted = 0 343 + ` 344 + 345 + try { 346 + await this.client.command({ 347 + query: sql, 348 + query_params: { authorDid }, 349 + }) 350 + } catch (err) { 351 + throw new Error( 352 + `ClickHouseStore.tombstoneUserSnapshots failed for author ${authorDid}`, 353 + { cause: err }, 354 + ) 355 + } 356 + } 309 357 }