Ah, I see. Now I think I understand.
For a given RecordMetaData’s Protobuf record definition, there is exactly one record type, known as the “union descriptor”, that has one field for each top level record type in the database (see: https://foundationdb.github.io/fdb-record-layer/GettingStarted.html#protobuf-configuration). So, something like:
message RecordTypeOne { }
message RecordTypeTwo { }
message UnionDescriptor {
option (com.apple.foundationdb.record.record).usage = UNION;
RecordTypeOne _RecordTypeOne = 1;
RecordTypeTwo _RecordTypeTwo = 2;
}
In this case, there are two types of records that the user may insert into a record store (RecordTypeOne
and RecordTypeTwo
), and the UnionDescriptor
message is essentially “listing” those types.
The Record Layer then wraps all records in union descriptor messages, essentially treating it as a oneof
. When data are serialized, the user hands a record to the Record Layer, and the Record Layer creates a union descriptor message and sets the appropriate field to the supplied record. Then it serializes that union descriptor message to the database. When it deserializes a record, it always first deserializes it into an instance of the union descriptor, and then it searches to see which field in the union descriptor is set. From there, it knows what type the record is from the union descriptor definition.
So, even though the Record Layer allows the user to save a heterogenous set of records of various types into the database, they are always stored using as a homogenous set of union descriptors, and so the same Protobuf deserializer can be used for every record in the store.
(Note that because of how Protobuf serialization works, this essentially means that every record is assigned a “type number”, which is the field number of that record in the union descriptor message. Then the type number (and, less relevantly, the message length) is then stored with the message data, so an alternative approach would have been to have had each record type definition just take a type number, and then we could have concatenated the type number with the serialized Protobuf data for that type. We didn’t do that, partially because it’s easier to just let Protobuf handle that for us, but we did take advantage of the fact that there’s already a record type number when the recordType()
key expression was created. That expression evaluates to some value that is guaranteed to be unique per record type (for a given meta-data definition), and by default, it’s the union descriptor field number for that record.)
(Also left out of this analysis is that in reality, you are allowed to have multiple fields in the union descriptor of the same type. At serialization time, the same field is consistently chosen based on some internal logic, and at deserialization time, it will look to see if any field is set and take the type from that field. But you can use this logic to do things like create a new field in the union descriptor every time you edit the record type, and then the field that’s used on any given record is essentially a record of what version of the record type was current at the time the record was written.)