Implementing VersionStamps in bindings

Hm, yeah, this might have been an instance where brevity was chosen instead of clarity. Maybe a 12 byte “versionstamp” should have been an ExtendedVersionstamp or something to indicate it is different from a 10 byte versionstamp? ¯\_(ツ)_/¯

This can be improved somewhat by the fact that it will be the magic string preceded by the versionstamp type code, which might make it less likely to collide (or, well, maybe not). We ultimately decided that a magic string was more error prone than the extra methods were bad ergonomically, so we went with what’s in the codebase now. To handle subspaces, we added methods to pack a tuple with a prefix so that the prefix length gets added in to the offset correctly. It doesn’t quite work for suffixes, but that seems to be less common.

What are your order of operations? The get_versionstamp method is somewhat weird, but you have to call the future before it is committed, and then that future will be ready only after the commit. So something like:

CompletableFuture<byte[]> vsFuture = tr.getVersionstamp();
tr.commit().join();  // blocking call to wait on commit
byte[] versionstamp = vsFuture.join(); // non-blocking call to get versionstamp

(But, like, in C# instead of Java.) I think you can get used_during_commit if you call getVersionstamp after commit rather than before.

Are you only ever running one transaction at a time? You will only see a non-zero “batch version” (we call it) if there are multiple transactions being committed together at a single version, which can only happen if there are concurrent commits. The easiest way is to probably fire off multiple transactions and then wait for all of them.

Just to expand on this a little, with versionstamps in particular, it’s often the case that you will want a forward index and a reverse index, i.e., (keyspace_1, key) -> version and (keyspace_2, version) -> key. There are a couple of reasons for this, but the most obvious is that as you don’t know what key you wrote if you only write the one with a version, then to remove that key, you have to somehow figure out what that version was. So you can either scan the keyspace_2 (slow) or look it up in the other index (fast). The other less obvious reason is that if you get a commit_uknown_result error, then if all you had were the index with versions in keyspace_2, it would be really easy to add multiple entries in keyspace_2 in subsequent retries by mistake. Having keyspace_1 around let’s you either detect that you are in a retry loop and not write it again (essentially letting you know that your commit succeeded) or, if index maintenance is done correctly, lets you clean up keyspace_1 and keyspace_2 within the retry loop. But all of that depends on the versionstamp having the same user version each time for a logical record within the database.