Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## Unreleased

- Align the use-case claims with solid-objects-js. "Is it worth installing
here?" listed long-lived workflows without a limit, while `docs/fit.md`
called a rate limiter an anti-pattern and the JS README sold per-key rate
limits. Both projects now say the same thing: a low-rate quota that a
reminder refills fits, because each check is one durable ordered message; a
limiter that every request touches does not; and a workflow fits when one
entity owns the mutable state and its mailbox holds the step order. A durable
execution engine that replays named steps from a step log remains a different
tool.
- Name Solid Objects Pro in `docs/fit.md` for the high-QPS cases the guide
rejects, and map its three capabilities onto them: grouped operations,
ephemeral operations, and reactive projections. The README already pointed
there; the fit guide stopped at "anti-pattern".
- Title the README "Solid Objects Ruby", matching "Solid Objects JS" in the
Node package, and give both the same two badges. The CI badge now pins
`?branch=main`, and a RubyGems version badge sits beside it. The gem name,
the module, and the published metadata do not change.

## 0.14.2 - 2026-08-25

- Rewrite the first screen around the objection a reader actually has. The
Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Solid Objects
# Solid Objects Ruby

[![CI](https://github.com/cardmagic/solid-objects-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/cardmagic/solid-objects-ruby/actions/workflows/ci.yml)
[![CI](https://github.com/cardmagic/solid-objects-ruby/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/cardmagic/solid-objects-ruby/actions/workflows/ci.yml)
[![gem](https://img.shields.io/gem/v/solid_objects)](https://rubygems.org/gems/solid_objects)

**Self-hosted, distributed Durable Objects in Rails without a daemon using your existing SQL database.**

Expand Down Expand Up @@ -137,15 +138,23 @@ If it all happens inside one request, use a lock.
## Is it worth installing here?

Worth it when several requests, jobs, or processes act on the same cart, chat
room, device twin, game room, or long-lived workflow, and each next action
needs the last committed state. Worth it when that same thing also owns work
that fires later, or a number a live page must show.
room, device twin, game room, long-lived workflow, or refillable quota, and
each next action needs the last committed state. Worth it when that same thing
also owns work that fires later, or a number a live page must show.

Two of those have a limit. A workflow fits when one entity owns the mutable
state and its mailbox holds the step order. A durable execution engine that
replays named steps from a step log is a different tool, because Solid Objects
redelivers an ordered message and retries it. A quota fits when one identity
checks it a few times per minute, because each check is one durable ordered
message with a retained history row. A limiter that every request to that
identity touches does not fit here.

Not worth it for a plain counter, a single-row update inside one transaction, a
stateless job, bulk ingestion or a data-parallel pipeline, CPU-heavy work, a
large JSON document that belongs in normalized rows, high-QPS request reads, or
a global rate-limit counter that every request touches. One hot identity is
serialized on purpose, so making everything one identity makes a queue.
a rate-limit counter that every request touches. One hot identity is serialized
on purpose, so making everything one identity makes a queue.

High-QPS reads and hot identities are where this runtime stops being the right
tool on its own. [Solid Objects Pro](https://solidobjects.pro/) is a commercial
Expand Down
27 changes: 22 additions & 5 deletions docs/fit.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ Solid Objects is a good candidate when most of these are true:
asynchronous features, a Solid Objects runtime process.

Typical fits include checkout state machines, collaborative rooms, device
twins, durable assessments, approval workflows, and user-specific scheduling.
twins, durable assessments, approval workflows, user-specific scheduling, and
low-rate quotas that a reminder refills.

A workflow fits when one entity owns the mutable state and its mailbox holds
the step order. A durable execution engine that replays named steps from a step
log is a different tool: Solid Objects redelivers an ordered message and
retries it, and does not replay a handler from a step log.

## Poor fit and anti-patterns

Expand All @@ -46,10 +52,21 @@ when any of these dominate:
- State that is clearer as a normal record with database constraints and direct
service methods.

A rate limiter is usually a poor actor: it is hot, request-critical, and often
expires rather than requiring permanent message history. An impressions
pipeline is also a poor actor: its value is high-throughput append and
aggregation, not serialized mutable state.
A request-path rate limiter is usually a poor actor: it is hot,
request-critical, and often expires rather than requiring permanent message
history. A low-rate quota is the case that does fit, such as five password
resets an hour for one account, where a reminder refills the bucket and each
check is one durable ordered message. An impressions pipeline is also a poor
actor: its value is high-throughput append and aggregation, not serialized
mutable state.

[Solid Objects Pro](https://solidobjects.pro/) is the commercial scaling layer
for the high-QPS cases in this section. Grouped operations coalesce concurrent
calls into one bulk insert. Ephemeral operations hold a loss-tolerant call in
process memory and write no journal row, which is the mode for an abuse limiter,
a presence signal, or a view count. Reactive projections materialize a read
model from the durable broadcast outbox, so request-path reads stop competing
with mailbox work.

## Cost model

Expand Down
Loading