datomic 2025-09-10

In my experience it's common to either automatically apply schema changes at peer startup, or to do some kind of startup-time check to make sure that the database has the version of the DB that the peer expects, as a guard against forgetting to apply schema changes alongside code changes. I'm thinking about how to add similar guardrails around classpath transaction functions. I'd like my peers to refuse to start (or at least warn) if the transactor is not using the expected version of the transaction function jar. How are others approaching this? The first thing that comes to mind is to have a transaction function that writes the version to a well-known location. At startup a peer could transact an invocation of that function and then query to get the version. But I'm wondering if I'm missing some simpler approach.

Simpler approach maybe to not do breakage, meaning if you need to change the behavior of a transaction function give it a new name.

maybe you could keep classpath functions distinct (foo-1, foo-2, foo-3 etc), but use database function wrappers as your transaction interface, and update that to truly deploy

jar has foo-1; you add foo-2 to "change" it

you deploy the jar, bounce the txor

then update some database function {:db/ident :foo :db/fn {:code '(apply foo-2 args))} to actually start using that new function in your transaction functions

peers don't need to know or care that the fn changed (unless they run that fn locally in d/with or such)

I'm not sure transacting the new db-fn will actually verify the classpath fn is present--you may have to actually call it, so may not end up any better than what you propose

At startup a peer could transact an invocation of that function and then query to get the version.
A little simpler: you could d/cancel from the function with the version in the anomaly map; then you don't add a new transaction every time you check

1

ah, I like that, I hadn't thought of d/cancel