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.
Public serialization boundary
Section titled “Public serialization boundary”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.
Kryo registration rules
Section titled “Kryo registration rules”- 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.
Resolution order
Section titled “Resolution order”- Null sentinel.
- Application class-specific serializer from
registerClass(clazz, id, serializer). - Application class registration from
registerClass(clazz, id). - LoomCache base registrations.
- Fail closed for unknown classes.
Application serializers cannot override LoomCache base registrations. Duplicate class IDs and duplicate class registrations are rejected.
Portable object envelope
Section titled “Portable object envelope”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:
- ASCII magic
LCPE. - Unsigned one-byte version, currently
1. - Java modified-UTF class name written with
DataOutputStream.writeUTF; the UTF-8 class-name length must be at mostPortableObjectEnvelope.MAX_CLASS_NAME_BYTES(512). - Four-byte signed payload length.
- 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.
Client setup
Section titled “Client setup”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 setup
Section titled “Server setup”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.
Compact and GenericRecord
Section titled “Compact and GenericRecord”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.
Global serializer SPI
Section titled “Global serializer SPI”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.