It's not clear what the right thing to do is if the database connection fails
during the middle of a transaction. In general, one can't talk to the database
after that, so I'm assuming the transaction is aborted (or eventually aborted)
and rolled back, and everything has to be retried.
I'm not currently tracking state inside a Transaction, which means that I don't
freak out if you call commit() then call rollback(), I just ignore the latter
call, and it's also possible to do something stupid like:
Transaction tx = Transaction.start(ctx);
try {
_somRepo.doOp(..);
} catch (DatabaseException de) {
// no problem!, keep on keepin' on
}
_someRepo.doAnotherOp(...);
tx.commit();
If the first repo op fails due to a database connection failure, the
transaction will get a new database connection for the second op and do the
last half of the op in a new connection.
I guess I'll have to do some state tracking, because stupid as the above code
is, I'm sure some circumstance will arise where it seems like a reasonable
thing to do, and hilarity will ensue. Blah.
The _marshallers table must be guarded as computed marshallers can be created
at any time, and marshaller initialization itself must be guarded for the same
reason.
Thanks to Ray for pointing this out.
This causes the elements of the list to match the original order they were
added to the cons list (which stores elements "backwards").
This order matters for joins, as Ray discovered.
Most JDBC drivers have supported this for ages. No longer do we need the
hackery of turning around and doing another query to obtain generated key
values.
It would be extremely rare, but if two nodes were trying to simultaneously
store() the same record with the same Key, one could fail with a DKE.
Attempt a second 'update' if the insert fails.
We've chosen not to conflate Java transience with Depot non-persistence. It's a
little wonky to have to choose a particular memory model behavior just to have
a field not persisted in your record.
@Transform is first sought on the field itself, which overrides any other
handling. Next it is sought on the class that represents the field (i.e. if the
field is type Foo we look for an @Transform annotation on class Foo). We avoid
doing this somewhat expensive search if the field has a type that we know
cannot have an @Transform annotation. If we've still found no @Transform
annotation (which is unfortunately the overwhelmingly common case), we then
create a stock marshaller.
Otherwise, if a data migration is registered and run after the initialization
process, we'll end up warning that this record has not yet been initialized.
In theory, you should register your data migrations prior to calling initRepos,
but I don't want to be too draconian about that. Unlike schema migrations, you
*could* conceivably mean to start your app up and let it run before running
data migrations.
This allows two separate services which share a database to assign "control" of
the schema to one of the services. The non-controlling service can be
configured to simply fail if new code is deployed to said service before new
code is deployed to the controlling service, rather than have the
non-controlling service migrate the schema out from under the controlling
service.
version is set to what we expect it to be.
Otherwise if one node reads an old value for the schema version, it will
be able to grab the migration lock as long as another node has already
completed the migration.