Your application writes the file to its database, and the relationship to SpiceDB. Both need to happen. There is no transaction that spans them, so there is always a moment where one has landed and the other has not. Crash the handler at each point below and watch what each system is left holding.
pick a point and crash the server there
no crash yet
Viable in the right context. If a user would notice and could fix it, or the content is not sensitive, the simplicity may be worth more than the guarantee.
Compare the two systems on a schedule against a source of truth, then remediate or flag the differences.
For data that fits SpiceDB's model — a relationship is simply present or absent — drop the database column entirely. One write, no second system, no problem.
The API call becomes an event rather than a procedure. Separate consumers write to the database and to SpiceDB, and the runtime guarantees nothing is dropped.
Write familiar procedural code and let the framework handle errors, rollbacks and coordination across services.
Write the intended SpiceDB change to an outbox table inside the same database transaction. A separate process reads the log and applies it. One transaction, so no dual write.
This is not a SpiceDB problem. Writing to an application database and a cache, or to two services, or to two databases, has exactly the same shape. Any system of sufficient complexity meets some version of it.
Reordering does not fix it. Try the SpiceDB-first option above. The dangerous window moves, it does not close — there is still a boundary where the process can die between the two writes. Adding transactional semantics to SpiceDB would move it again rather than remove it.
Based on authzed.com/blog/the-dual-write-problem, which goes further into each approach.