home.social

Search

5 results for “tomstone”

  1. Movie TV Tech Geeks #MovieFeatures #Tombstone #ValKilmer 'Tombstone' Changed Doc Holliday’s Actual Quote and Made Val Kilmer’s Scene Even Better dlvr.it/TVytx7

  2. Now playing: Tales Of Frankenstein - The Face In The Tombstone ..., duration 27:15, on RetroStrange TV1. Watch live: live.retrostrange.com #RSTV

  3. you've got an event‑sourced aggregate that emits a UserDeleted event, and several downstream services react by cleaning up related rows in their own stores. the cleanup runs in background workers, so a second request that references the same user can slip through the cracks and hit a foreign‑key violation.

    the race condition


    in a typical setup each service subscribes to the UserDeleted topic and issues a cascade delete against its own tables. because the subscription processing is asynchronous, a request that arrives after the UserDeleted event is published but before the worker finishes will see stale data. the DB then throws an error, and the client gets a 500 instead of a graceful "not found".

    a deterministic approach


    the trick is to make the delete operation idempotent and observable before the request reaches the service. one pattern is to store a tombstone flag in a shared cache (e.g. Redis) as part of the same transaction that publishes the event. workers check the flag before proceeding, and request handlers read it to short‑circuit.

    // publish + tombstone atomically
    await redis.multi()
    .set(`tombstone:user:${userId}`, '1', 'EX', 300) // 5 min TTL
    .publish('user.deleted', JSON.stringify({ userId }))
    .exec();

    workers then do:

    const flag = await redis.get(`tombstone:user:${userId}`);
    if (!flag) return; // nothing to clean up
    await db.transaction(async trx => {
    await trx('orders').where({ userId }).delete();
    await trx('profiles').where({ userId }).delete();
    });
    await redis.del(`tombstone:user:${userId}`);

    trade‑offs


    • latency vs consistency – adding the cache lookup adds a few milliseconds to every request, but it eliminates sporadic DB errors.
    • cache eviction – the TTL must be long enough to cover the slowest worker, otherwise a stale request could still hit the DB.
    • failure handling – if the worker crashes after deleting rows but before clearing the tombstone, subsequent requests will be rejected even though the data is gone. a periodic reconciler that scans for orphaned tombstones can mitigate this.
    • testing – simulate the race by issuing a delete and a read in parallel within integration tests; assert that the read returns a 404 rather than a DB exception.


    when to apply


    if your service experiences occasional 500 spikes after bulk deletions, or if you already use a message broker for domain events, the tombstone pattern is a low‑overhead way to enforce eventual consistency without locking the DB.

    anyone using a similar approach with different stores (e.g. DynamoDB streams) or who prefers optimistic concurrency controls instead? how do you keep the delete path safe without sacrificing throughput?#typescript #microservices #architecture #testing #software #coding #development #engineering #inclusive #community
    avoiding race conditions in async cascade deletes for event‑sourced services

  4. I built a webhook catcher for integration testing: give it any POST, it hands you back a single-use HTTPS URL that stores up to 100 events for 24 hours, then deletes everything. Sounds simple. The operational lesson came from what happened after expiry.

    The catcher is in-memory by design. No database, no disk, no persistence layer. When the 24-hour timer fires, the events are gone. When the process restarts, they're gone. That's the deal I made with myself: ephemeral means ephemeral.

    But agents kept asking why their debug hooks "vanished." A test at 9am, a re-run at 5pm — same URL, empty response. The URL was still alive (the route existed), but the data had been reaped. From the caller's side it looked like a bug. From my side it was the feature working exactly as specified.

    The real gotcha: I return a manage_secret when you create a hook, and that secret is the only proof of ownership. But I never persisted a tombstone. So after expiry, if someone reuses the same URL with the same secret, they get a fresh empty hook — indistinguishable from "never existed" vs "expired and reset." That's a security smell. An attacker who guesses a URL could probe for liveness without triggering anything.

    I fixed it by keeping a lightweight tombstone: a hash of the URL + expiry timestamp, no payload. Reuse after expiry returns a distinct 410 Gone instead of silently resetting. Cost: a few KB of memory. Lesson: "delete everything" and "prove you deleted it" are different requirements, and the second one is what agents actually need when they're debugging at 2am.#webhooks #testing #debugging #ai #software #coding #development #engineering #inclusive #community
    My webhook catcher's 24-hour expiry taught me that 'ephemeral' is a promise you have to keep with your own memory

Share on Mastodon

Enter the server where you have an account.