Chapter 2 Type Safety and System Evolution

In some cases, it is desirable for types to evolve without breaking interoperability with deployed components already using those types. For example:

  • A new set of applications to be integrated into an existing system may want to introduce additional fields into a structure, or create extended types using inheritance. These new fields can be safely ignored by already deployed applications, but applications that do understand the new fields can benefit from their presence.
  • A new set of applications to be integrated into an existing system may want to increase the maximum size of some sequence or string in a Type. Existing applications can receive data samples from these new applications as long as the actual number of elements (or length of the strings) in the received data sample does not exceed what the receiving applications expect. If a received data sample exceeds the limits expected by the receiving application, then the sample can be safely ignored (filtered out) by the receiver.

To support use cases such as the above, the type system introduces the concept of appendable and mutable types. A type may be final, appendable, or mutable:

  • Final: The type’s range of possible data values is strictly defined. In particular, it is not possible to add elements to members of a collection or aggregated types while maintaining type assignability.
  • Appendable: Two types, where one contains all of the elements/members of the other plus additional elements/members appended to the end, may remain assignable.
  • Mutable: Two types may differ from one another with the addition, removal, and/or transposition of elements/members while remaining assignable.

For example, suppose you have:

struct A { 
    @id(10) int32 a;
    @id(20) int32 b; 
    @id(30) int32 c; 
} 

and

struct B {
    @id(20) int32 b; 
    @id(10) int32 a; 
    @id(40) int32 x; 
} 

In this case, if a DataWriter writes [1, 2, 3], the DataReader will receive [2, 1, 0] (because 0 is the default value of x, which doesn't exist in A's sample).

The type being written and the type(s) being read may differ—maybe because the writing and reading applications have different needs, or maybe because the system and its data design have evolved across versions. Whatever the reason, the databus must detect the differences and mediate them appropriately. This process has several steps:

  1. Define what degree of difference is acceptable for a given type.
  2. Express your intention for compatibility at run time.
  3. Verify that the data can be safely converted.

At run time, the databus will compare the types it finds with the contracts you specified.