Skip to content

Serialization Policy

LoomCache uses Kryo as the default object serializer and requires every serialized runtime class to be registered. Compact serialization, GenericRecord, and the global serializer SPI are available for schema-oriented or custom formats, but default map values, snapshots, and protocol payloads still depend on a stable serializer contract.

LoomMap<K,V> generics are a Java client convenience, not Hazelcast-style implicit object mode. Scalar and byte[] map keys/values use LoomCache’s stable internal value encoding. Registered application POJO keys/values carry their registered-class identity with the serialized payload; unregistered POJOs fail before a write is sent. Server-side SQL and index extraction do not introspect registered-object payloads in this release. Decoding validates the registered-class identity carried with the payload against the local registration table, so a class-ID or serializer-identity change fails rather than being interpreted as another class.

The supported serialization API is the documented registration/configuration surface: KryoSerializerApi, StableSerializerFingerprint, LoomSerializer, LoomSerializerProvider, Compact API types, and the PortableObjectEnvelope helper described below. Other public classes in com.loomcache.common.protocol and hidden members of serializer classes are bytecode-level compatibility support for LoomCache modules, not extension APIs. Applications should not construct protocol frames, call hidden codec helpers, depend on reserved Kryo IDs below the application floor, or parse internal wire payloads that are not documented on this page.

  • Register application classes before clients write data or members load snapshots/WAL records.
  • Use the same class, numeric ID, and optional custom serializer on every client and member that can read or write the payload.
  • Treat IDs as durable schema identifiers. Never recycle an ID for a different class.
  • Keep a version-controlled registry and allocate IDs monotonically.
  • Register classes explicitly in a fixed startup path; do not depend on scan order.
  • Roll out new registrations everywhere before writing values that need them. Adding an unrelated registration does not make older object envelopes unreadable after the new registry is deployed; changing the payload class ID or serializer identity does.
  • Prove each production value type through client round-trip, WAL replay, snapshot restore, restart, and same-artifact upgrade drills before relying on it for critical data.

KryoSerializer reserves the low numeric ID range for LoomCache base/JDK registrations and enforces an application floor of 50; registerClass rejects any id below that floor. LoomCache also keeps private bands for server/runtime payloads and distributed JCache wrappers. Treat 1000-9999 as the recommended explicit application-ID band for ordinary deployments; server and JCache boot paths reject duplicate IDs when they install their private registrations.

  1. Null sentinel.
  2. Application class-specific serializer from registerClass(clazz, id, serializer).
  3. Application class registration from registerClass(clazz, id).
  4. LoomCache base registrations.
  5. Fail closed for unknown classes.

Application serializers cannot override LoomCache base registrations. Duplicate class IDs and duplicate class registrations are rejected.

Registered POJO keys/values, entry processors, executor tasks, and other typed object payloads use PortableObjectEnvelope when the receiver must validate the declared application class before asking Kryo to decode the payload. Ordinary applications should use LoomClient.registerClass(...) and the typed client APIs instead of building envelopes directly, but the envelope is a documented low-level wire contract for integrations that intentionally inspect or produce registered-object payloads.

Version 1 envelope bytes are stable and ordered as follows:

  1. ASCII magic LCPE.
  2. Unsigned one-byte version, currently 1.
  3. Java modified-UTF class name written with DataOutputStream.writeUTF; the UTF-8 class-name length must be at most PortableObjectEnvelope.MAX_CLASS_NAME_BYTES (512).
  4. Four-byte signed payload length.
  5. Opaque Kryo payload bytes for that registered class.

Decoding returns no envelope when the magic is absent. A known magic with an unsupported version, malformed class name, negative or overrun payload length, trailing bytes, or invalid payload shape fails closed. The envelope does not relax Kryo registration rules: the declared class name and the encoded class ID/serializer identity must still match the local registry.

Register application classes on the client builder before connecting:

LoomClient client = LoomClient.builder()
.addSeed("127.0.0.1:5701")
.registerClass(Customer.class, 1000)
.registerClass(Order.class, 1001, new OrderKryoSerializer())
.build();
client.connect();

LoomClient.registerClass(...) is also available after construction, but only before connect() — once the client is running (or closed) it throws IllegalStateException("registerClass must be called before connect"). A new registration bumps the serializer’s registration generation and is re-applied lazily to each pooled Kryo instance; it cannot repair data already written with missing or conflicting registrations.

Standalone clients and members can install default no-custom-serializer registrations through -Dloomcache.serialization.kryo-registrations=com.example.Customer=1000,com.example.Order=1001 or the matching LOOMCACHE_SERIALIZATION_KRYO_REGISTRATIONS environment variable.

Server members must install the same registry before they accept clients because the protocol handshake compares the Kryo registration digest. This applies to registered-object LoomMap payloads, embedded members, standalone server bootstraps, entry processors, executor payloads, non-production CP extension payloads, snapshots, WAL replay, and any custom MapStore values installed through clustered server leader-owned persistence. Packaged generic JDBC MapStore and the Spring default-map JPA write-through bridge remain outside production persistence; the packaged Spring bridge is not installed in production.

For Spring deployments, centralize the registry in configuration code shared by all services that start a LoomClient or embedded server. For container deployments, bake the registration module into the server image and roll every member before enabling writes that use new classes.

Use Compact serialization when schema evolution matters more than raw Kryo class IDs. Compact payloads carry a type name, field metadata, and a stable fingerprint so readers can tolerate extra fields and provide defaults for missing fields. GenericRecord lets tooling read or create Compact payloads without loading the application class.

The Compact public wire contract is semantic rather than byte-offset based. Treat Compact bytes as opaque and use CompactSerializationService to read or write them. The supported schema contract is the type name, ordered field names, field kinds (STRING, INT32, INT64, BOOLEAN, FLOAT64, BYTES), nullability of each encoded value, and the derived fingerprint. Changing a type name or field kind is incompatible. Adding a field is compatible only when readers provide a default for missing fields; removing a field is compatible only when readers no longer require it. Reordering fields changes the fingerprint and should be treated as a schema change during rollout. Non-finite FLOAT64 values are rejected.

Compact still has a schema contract: type names, field names, field kinds, and custom Compact serializers must be deployed consistently across every client and member that can read or write the payload. The internal Compact binary layout, magic values, length fields, and private helper methods are not extension points.

GlobalSerializerRegistry loads Kryo by default, or the highest-priority LoomSerializerProvider from ServiceLoader. Applications may replace the process-wide serializer directly for tests or custom deployments. A custom global serializer becomes part of the wire and snapshot contract.

This page is the public serialization checklist for the release.

LoomCache is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Hazelcast, Inc. or by any other company whose products are named in this documentation. “Hazelcast” is a trademark of Hazelcast, Inc.; references to it are nominative and describe only migration and comparison. All other product and company names are trademarks of their respective owners and are used for identification purposes only.