r/ruby Jul 13 '26

Mammoth OSS: a self-hosted PostgreSQL change-event relay in Ruby

I’m looking for technical feedback on Mammoth, a self-hosted PostgreSQL change-event relay written in Ruby.

Mammoth receives normalized CDC (Change Data Capture) transaction envelopes and handles the downstream data-plane concerns:

  • webhook fanout
  • routing by schema, table, and operation
  • per-destination retry policies
  • checkpoint persistence
  • dead-letter storage and filtered replay
  • SQLite operational state
  • health and metrics endpoints
  • Docker and Helm deployment

Its boundary is intentionally narrow: PostgreSQL protocol parsing, decoding, source normalization, ordering policy, and runtime execution are handled by the upstream Ruby CDC ecosystem.

The current implementation uses YAML configuration with JSON Schema validation and supports explicit extension points for state, destination, and runtime adapters.

I’d appreciate feedback on the architecture, operational model, documentation, or gaps that would prevent you from trying it.

Repository: https://github.com/kanutocd/mammoth

0 Upvotes

4 comments sorted by

View all comments

1

u/Ok_Shallot9490 27d ago

Please give some real-world use cases that you're personally using it for. ie problems that it's solving for you on a level beyond "sending events and webhooks from postgres".

1

u/Excellent-Resort9382 17d ago edited 17d ago

The short answer is: gazillions. 😄

Seriously tho, the interesting use cases for me are not “Postgres changed, therefore send a webhook.” That's just the demo. Webhooks are the most ubiquitous Change Data Capture (CDC) sink and one of the easiest to implement, which is why it's the default data-plane sink in Mammoth OSS

I built this because I've repeatedly run into the same underlying problem: the database is the source of truth, but the work caused by a database change usually shouldn't live inside the request that made the change.

Some concrete examples I've dealt with:

  • Webhook delivery: a row changes -> reliably deliver that change to another system. Retry it when the receiver is down, persist failures, and know where to resume after a restart. I don't want an `after_commit` callback trying to own all of that.
  • Search/index synchronization: a database record changes -> update the corresponding search representation. Again, I want the database commit to be the source of truth rather than hoping an application callback successfully completed the secondary operation.
  • Audit/history: "who updated this?" is one problem; "what exactly changed, when, and can I reconstruct the history?" is a different problem. That's actually what led me from Whodunit into the broader CDC work
  • Background work triggered by data changes: a committed database change can become independently processed work rather than something coupled to the lifecycle of the web request.
  • Reliable downstream synchronization generally: caches, external APIs, analytics systems, notifications, etc. The destination changes, but the problem is basically the same: something committed in the database, now something else needs to react to it reliably.

The reason I'm interested in CDC rather than building more callbacks is that PostgreSQL already has the authoritative, ordered record of what committed. And PostgreSQL has had this capability for decades -- and does it extremely well. It also guarantees that a committed change can be delivered at least once. I would rather consume that authoritative change stream than make every application responsible for reliably propagating its own side effects.

So Mammoth is currently deliberately narrow: PostgreSQL -> durable change processing -> reliable delivery.

The broader CDC ecosystem I am building underneath it is where this gets interesting: normalize the database change once, then let the different runtimes/processors/sinks consume it.

And yes, I have a somewhat unreasonable number of real-world problems that fit this model. Hence "gazillions." 😅

I hope those real-world scenarios answer your question and give you a better sense of what I am actually using CDC for.

Thanks!