Client API Reference
Client SDK Architecture
Smart routing, near cache, pipelining, and auto-retry built in.
This reference covers the LoomCache Java client SDK. The SDK lives in loom-client and targets Java 17+; the root
class is LoomClient.
Generated Java API documentation for the client modules is available at the Javadoc index.
Supported common types and hidden ABI
Section titled “Supported common types and hidden ABI”The Java public modifier is not the LoomCache support boundary. Some classes in loom-common are public in bytecode so
the client, server, compatibility tests, and tooling can share wire helpers. The public Javadoc route is a curated
application-facing subset, not every public bytecode type. Application code should import common-module types only when
this page documents them as supported or they are required by a supported client method signature.
Stable common support types include documented exceptions, configuration/TLS types, serializer SPI types, Compact
serialization types, SerializablePredicate factory methods for server-filtered listeners and continuous-query cache
seeding, OffloadableProcessor only as a marker on explicitly allowlisted entry-processor implementations, ScanResult
for public map/set scan results, and ScanPage as a stable cursor/key page value when a supported signature or adapter
explicitly uses it. Public client scan APIs return ScanResult. OffloadableProcessor is a scheduling hint for eligible
local execution paths, not a guarantee of thread placement or permission to bypass the production processor allowlist.
Other com.loomcache.common.protocol classes are wire codecs, payload records, opcodes, or handshake diagnostics. They
may remain public for cross-module compatibility, but they are not application APIs. Do not construct protocol frames,
depend on numeric message identifiers, call hidden codec helpers, or implement custom wire predicates. For
SerializablePredicate, supported callers use all, keyEquals, valueEquals, keyStartsWith, and valueContains;
the nested implementation records, constants, encode(), and decode(byte[]) are hidden wire support.
Coordinates
Section titled “Coordinates”Maven Central coordinates are available only after a tagged release is published. Use the published LoomCache version that matches your cluster. Contributors validating a source checkout should install that checkout locally before running application or documentation validation.
<dependency> <groupId>com.loomcache</groupId> <artifactId>loom-client</artifactId> <version>${loomcache.version}</version></dependency>implementation("com.loomcache:loom-client:$loomcacheVersion")Plain client applications need only loom-client. Spring Boot applications can depend on loom-spring-boot for
auto-configuration; see Spring Boot integration.
Contributors validating the documentation site from a source checkout can regenerate and copy those Javadocs into
the public /api/javadoc/ route with:
cd public-documentationnpm run buildConstruction
Section titled “Construction”Construct the client through LoomClient.builder():
LoomClient client = LoomClient.builder() .addSeed("loomcache-1.eu-west.prod.lan:5701") .addSeed("loomcache-2.eu-west.prod.lan:5701") .addSeed("loomcache-3.eu-west.prod.lan:5701") .connectionTimeout(Duration.ofSeconds(5)) .requestTimeout(Duration.ofSeconds(15)) .maxRetries(3) .retryBaseDelay(Duration.ofMillis(100)) .tlsConfig(TlsConfig.builder() .enabled(true) .keyStorePath(Path.of("/etc/loomcache/tls/client.p12")) .keyStorePassword(System.getenv("LOOMCACHE_TLS_KEY_STORE_PASSWORD")) .trustStorePath(Path.of("/etc/loomcache/tls/truststore.p12")) .trustStorePassword(System.getenv("LOOMCACHE_TLS_TRUST_STORE_PASSWORD")) .requireClientAuth(true) .revocationCheckingEnabled(true) .revocationSoftFail(false) .build()) .credentialsFactory(new StaticCredentialsFactory(ClientCredentials.certificate())) .nearCacheEnabled(true) .nearCacheTtl(Duration.ofSeconds(30)) .nearCacheMaxSize(10_000) .build();
client.connect();The builder above shows the common production shape. Plaintext TLS-disabled clients and forwarded AUTH roles are
non-production conveniences. LoomClient.Builder exposes more than 40 public methods; the additional ones most likely to be needed in production are listed here:
| Method | Description |
|---|---|
routingMode(ClientRoutingMode) | Controls which cluster member receives each request (ALL_MEMBERS by default). |
reconnectMode(ClientReconnectMode) | Behavior on connection loss (ON by default). |
batchMaxOperations(int) | Maximum number of operations per LoomBatch; must be positive and no more than the supported release limit. |
nearCacheEvictionPolicy(NearCacheEvictionPolicy) | Eviction algorithm for the near cache. |
topicPollingConfig(TopicPollingConfig) | Configures poll interval, backoff, and max backoff for LoomTopic subscriptions. |
credentialsFactory(CredentialsFactory) | Plug-in credential provider; mutually exclusive with auth(username, roles). |
maxRedirects(int) | Maximum leader-redirect hops before the request fails (must be positive). |
leaderElectionWaitTimeout(Duration) | How long the client waits for a new leader after detecting leader loss (default 15 s). |
registerClass(Class<?>, int) | Register a POJO class for Kryo serialization with the given type ID. |
registerClass(Class<T>, int, Serializer<? super T>) | As above, with an explicit Kryo serializer. |
registerClass(Class<T>, int, Serializer<? super T>, String fingerprint) | As above, with a schema-fingerprint guard for cross-client version safety. |
asyncStart(boolean) | When true, connect() returns before the cluster handshake completes (incompatible with FailoverLoomClient). |
maxInFlightRequests(int) | Back-pressure ceiling on concurrent in-flight requests. |
POJO types passed to registerClass must use identical registrations (same class, same type ID, same serializer) on every client and every cluster member; mismatches cause deserialization failures.
Data-structure factory methods
Section titled “Data-structure factory methods”| Factory method | Returns |
|---|---|
client.getMap(name) | LoomMap<K, V> |
client.getReplicatedMap(name) | LoomReplicatedMap<K, V> (production-gated compatibility handle) |
client.getQueue(name) | LoomQueue<E> |
client.getSet(name) | LoomSet<E> |
client.getMultiMap(name) | LoomMultiMap<K, V> |
client.getList(name) | LoomList<E> |
client.getPriorityQueue(name) | LoomPriorityQueue<E> |
client.getRingbuffer(name) | LoomRingbuffer<E> |
client.getRingbuffer(name, capacity) | LoomRingbuffer<E> with explicit capacity |
client.getReliableTopic(name) | LoomReliableTopic<T> |
client.getReliableTopic(name, capacity, TopicOverloadPolicy) | LoomReliableTopic<T> with custom capacity and overload policy |
client.getTopic(name) | LoomTopic<String> |
client.getTopic(name, Class<T>) | LoomTopic<T> with explicit message type |
client.getPNCounter(name) | LoomPNCounter |
client.getGSet(name) | LoomGSet<E> |
client.getORSet(name) | LoomORSet<E> |
client.getLWWRegister(name) | LoomLWWRegister<V> |
client.getIdGenerator(name) | LoomIdGenerator (prefetchCount=1, no prefetch validity) |
client.getIdGenerator(name, prefetchCount, prefetchValidityMillis) | LoomIdGenerator with prefetch tuning |
client.getExecutorService(name) | LoomExecutorService (production requires explicit task allowlist) |
The table lists public factory methods; production support is governed by the per-feature notes below and the generated Javadocs.
LoomClient also retains lower-level public operation methods such as string-name map, queue, and set calls for
compatibility adapters, diagnostics, and advanced integrations that already own serialization and naming. New application
code should prefer the typed LoomMap, LoomQueue, LoomSet, and related handles above; lower-level methods are not a
broader support promise than the typed handle contract documented here.
LoomMap<K, V> — client.getMap(name)
Section titled “LoomMap<K, V> — client.getMap(name)”LoomMap<K,V> generics are a Java client convenience, not Hazelcast-style implicit object mode. Scalar and binary keys/values use type-tagged wire strings. Registered POJO keys/values use a Kryo object envelope and require identical registrations on clients and members; unregistered POJOs fail before writes are sent.
| Category | Methods |
|---|---|
| Read | get(K), containsKey(K), size(), getAll(Collection<K>), getEntryView(K) |
| Write | put(K, V), putTransient(K, V), putIfAbsent(K, V), putAll(Map), putAll(Map, idempotencyToken), delete(K) → boolean, getAndRemove(K), evict(K), removeAll(Collection<K>), removeAll(LoomQuery), clear() |
| TTL writes | putWithTtl(K, V, Duration), putWithTtl(K, V, long, TimeUnit), plus putWithTTL(...) aliases — returns @Nullable V previous value; a ttl of zero means the entry never expires (like put), not immediate expiry |
| Atomic CAS | replace(K, V, V) → boolean, compareAndDelete(K, V) → boolean, computeIfAbsent(K, Function<K, V>) |
| Entry proc. | executeOnKey(K, EntryProcessor<K, V, R>), executeOnKeys(Set<K>, EntryProcessor<K, V, R>), executeOnEntries(...); the processor must be a named top-level or static-nested class (lambdas/anonymous/local are rejected); production requires explicit processor allowlist |
| Query | keySet(LoomQuery), entrySet(LoomQuery), values(LoomQuery), keyset page helpers, project, count, sum, avg, min, max |
| Listeners | addEntryListener(MapListener, boolean includeValue) → String registrationId; key-scoped and server-predicate overloads; removeEntryListener(String) → boolean |
| Iterate | scan(cursor), scan(cursor, pattern), scan(cursor, pattern, count); scanner(), scanner(pattern), scanner(pattern, pageSize) |
| External store | loadAll(boolean replaceExisting), loadAll(Set<K> keys, boolean replaceExisting) for maps with a clustered, leader-owned MapStore |
| Per-call | get(K, Duration), put(K, V, Duration), delete(K, Duration), containsKey(K, Duration) |
| Async | getAsync, putAsync, putAsyncAndGetPrevious, deleteAsync, containsKeyAsync, sizeAsync |
| Indexes | addIndex(IndexConfig) throws before sending while SQL index maintenance is disabled; addIndexAsync(IndexConfig) returns a failed CompletableFuture |
| Stats | getStats() → LoomMapStats(remoteFetches, totalOps, avgLatencyMs) (with remoteFetchRatePercent()) |
putAll(Map, idempotencyToken) accepts a nullable application token; blank tokens are rejected. The token is scoped by
the authenticated requester or client endpoint, so a restarted client can reuse it for the same caller. Multi-group
batches auto-generate a per-batch token. Partial failure throws PartialPutFailureException.
putTransient(K,V) is a replicated in-memory write that bypasses external MapStore write-through; use it only when
the value must remain out of the backing store. loadAll(...) triggers a leader-owned MapStore load, replicates loaded
entries through Raft without writing them back to the store, no-ops when no MapStore is configured, and is not supported
for partitioned/sharded maps. getEntryView(K) returns the current in-memory value plus metadata and does not trigger
read-through loading.
LoomQueue<E> — client.getQueue(name)
Section titled “LoomQueue<E> — client.getQueue(name)”offer(E),poll(),poll(Duration),take(),peek(),size(),offerAll(Collection),poll(int count),drain(),drainTo(target[, maxElements]), plus async variants (offerAsync,pollAsync,peekAsync,sizeAsync,offerAllAsync,pollAsync(int),drainAsync,drainToAsync).
LoomSet<E> — client.getSet(name)
Section titled “LoomSet<E> — client.getSet(name)”add,remove,contains,size,clear,scan/scanner, and async variants.
LoomTopic<T> — client.getTopic(name) / client.getTopic(name, Class<T>)
Section titled “LoomTopic<T> — client.getTopic(name) / client.getTopic(name, Class<T>)”LoomTopic uses a server-side polling model. Subscriptions receive messages via a background poll loop.
publish(T)— publish a message to all subscribers.subscribe(Consumer<T>)→intsubscriptionId — register a listener; starts the poll scheduler on the first subscription.subscribe(Consumer<T>, Consumer<TopicSequenceLostException>)→intsubscriptionId — register a listener with an explicit sequence-loss handler.unsubscribe(int subscriptionId)→boolean— remove a subscription; shuts down the poll scheduler when no subscriptions remain.closeSubscriptions()— cancel all active subscriptions.activeSubscriptionCount()— number of currently active subscriptions.
Diagnostic counters:
| Method | Description |
|---|---|
getDecodeFailureCount() | Number of messages that could not be deserialized. |
getListenerFailureCount() | Number of listener invocations that threw an exception. |
getPermanentListenerFailureCount() | Messages skipped after listener retry exhaustion; the subscription remains active and advances past the failed sequence. |
getPollFailureCount() | Number of failed poll requests to the server. |
getSequenceLossCount() | Number of sequence-gap events detected (missed messages). |
getLastSequenceLoss() → @Nullable TopicSequenceLostException | Most recent sequence-loss event, or null. |
getCurrentPollBackoffMs() | Current poll backoff in milliseconds (increases after consecutive failures). |
Transactions — client.newTransaction()
Section titled “Transactions — client.newTransaction()”LoomTransaction implements AutoCloseable. Closing without committing triggers rollback().
try (LoomTransaction tx = client.newTransaction()) { tx.put("users", "alice", "Alice Loom"); tx.delete("sessions", "expired-key"); tx.commit();}// With an explicit timeouttry (LoomTransaction tx = client.newTransaction(Duration.ofSeconds(10))) { tx.put("orders", orderId, payload); tx.commit();}Supported transaction operations:
| Method | Description |
|---|---|
put(mapName, key, value) | Buffer a map put. |
delete(mapName, key) | Buffer a map delete. |
putIfAbsent(mapName, key, value) | Buffer a conditional put (applied only if key is absent at commit time). |
setAdd(setName, member) | Buffer a set add. |
setRemove(setName, member) | Buffer a set remove. |
queueOffer(queueName, element) | Buffer a queue offer. |
getForUpdate(mapName, key) → @Nullable String | Read a key under a server-side transaction lock and open the backing transaction session. |
commit() | Commit the transaction; throws on conflict or timeout. |
rollback() | Roll back the transaction (no-op if already committed). |
client.newTransaction() uses the default transaction timeout. client.newTransaction(Duration) sets an explicit timeout (minimum 1 ms). A negative duration throws LoomException; passing null uses the default timeout.
Cross-group replicated transactions currently support map put and delete only. putIfAbsent, set, and queue
transaction operations are limited to the single-group path.
CP primitives — client.consistencySubsystem()
Section titled “CP primitives — client.consistencySubsystem()”client.consistencySubsystem() returns a LoomConsistencySubsystem, which exposes the production-supported CP data
structures:
LoomConsistencySubsystem cp = client.consistencySubsystem();LoomAtomicLong counter = cp.getAtomicLong("hits");counter.incrementAndGet();counter.compareAndSet(10, 20);
LoomLinearizableLock lock = cp.getLock("resource"); // client manages the production CP session internallyLoomLinearizableSemaphore sem = cp.getSemaphore("permits");LoomAtomicLong— fully Raft-replicatedget,set, arithmetic, and compare-and-set operations.LoomLinearizableLock— Raft-replicated lock acquire/release. Withloomcache.profile=productionthe server-side apply handler accepts lock mutations only when the caller holds an active Raft-managed CP session; the Java client creates, heartbeats, and closes that session internally.LoomLinearizableSemaphore— Raft-replicated permit acquire/release. The Java client attaches its managed CP session for acquire/release. A sessionless semaphore mutation fails closed in production; available-permit reads are always allowed.- Other CP extension methods are not production-supported; production profiles reject their direct requests. Use the
embedded
ConsistencySubsystemonly for non-production embedded scenarios.
Batch operations — client.batch()
Section titled “Batch operations — client.batch()”client.batch() .map("users").put("alice", "Alice") .map("users").put("bob", "Bob") .map("users").delete("eve") .execute();Operations are scoped through .map(name), .set(name), or .queue(name). By default the batch is not atomic. In sharded mode execute() uses the current partition table to pre-split non-atomic keyed map put, delete, and putIfAbsent operations by owner group. Call .atomic() for all-or-nothing handling: single-owner batches run under the server’s mutation lock, and cross-owner-group sharded atomic batches currently support keyed map put and delete through server 2PC. Cross-owner atomic batches containing putIfAbsent, set, or queue operations fail closed because condition-bearing 2PC is disabled. .replicated() implies atomic. Non-splittable mixed-owner batches fail closed. Set/queue batches are supported only on the non-sharded/single-Raft-group path; in sharded mode any batch containing set or queue operations fails closed.
To capture partial results from owner-group splitting, use executeAndReport(), which returns a LoomBatch.Result:
LoomBatch.Result result = client.batch() .map("a").put("k1", "v1") .map("b").put("k2", "v2") .executeAndReport();
if (!result.isFullyCommitted()) { int committed = result.committedSubBatches(); int total = result.totalSubBatches(); result.failure().ifPresent(ex -> log.error("partial failure after {}/{}", committed, total, ex));}LoomBatch.Result fields: committedSubBatches(), totalSubBatches(), failure() → Optional<RuntimeException>, isFullyCommitted() → boolean.
Run SQL through client.sql(String) and receive a LoomSqlResult. The client routes the request with the _sql_auto
sentinel; the server parses the FROM clause and extracts the target map:
// User must have an explicit supported serializer/encoding before production use.LoomSqlResult result = client.sql("SELECT key, name, age FROM users WHERE age > 30");See SQL engine for supported syntax. CREATE INDEX <name> ON <mapping> (<cols>) TYPE SORTED|HASH|BITMAP is recognized by the parser but rejected at runtime in this release; LoomMap.addIndex(IndexConfig)
throws before sending a request, while LoomMap.addIndexAsync(IndexConfig) completes the returned CompletableFuture
exceptionally.
Async API
Section titled “Async API”Each data-structure class exposes async-returning methods directly on the class — for example LoomMap.getAsync(K),
LoomQueue.offerAsync(E), and LoomMap.executeOnKey(K, EntryProcessor). These methods are the public async API. In
production, LoomMap.executeOnKey still requires the explicit deny-all-default entry-processor allowlist.
LoomClient.async() returns an AsyncLoomClient for integration code that needs the lower-level async facade; method names like mapPut(String, String, String) operate on untyped data-structure names and serialized values. Application code should prefer the typed *Async methods on data-structure objects.
Map listeners
Section titled “Map listeners”registerMapListener(String mapName, MapChangeListener)/deregisterMapListener(...)— raw map change events.
Cluster event listeners
Section titled “Cluster event listeners”LoomClient supports cluster-lifecycle listeners registered before or after connect():
addLifecycleListener(LifecycleListener)— notified onSTARTING,STARTED,SHUTTING_DOWN,SHUTDOWN,CLIENT_CONNECTED,CLIENT_DISCONNECTED, andCLIENT_CHANGED_CLUSTER(the last emitted byFailoverLoomClientwhen it transparently switches to a different seed group).addMembershipListener(MembershipListener)— notified when cluster members join or leave.addDistributedObjectListener(DistributedObjectListener)— notified when data structures are created or destroyed on the cluster.
High-availability utilities
Section titled “High-availability utilities”FailoverLoomClient— wraps multipleLoomClientinstances; on connection loss it transparently retries against the next seed group. Constructed viaFailoverLoomClient.connect(ClientFailoverConfig). Incompatible withasyncStart=true.LoomQueryCache<K, V>— a client-side materialized view of a server map; implementsAutoCloseable. Located incom.loomcache.client.query.Pipelining— sends a list of operations without waiting for individual responses, then collects results. Reduces round-trip overhead for bulk independent reads.ConnectionPoolMetrics— diagnostics counters andMetricsSnapshotvalues used by client telemetry. Supported application use is read-only observation when surfaced by diagnostics; callers should not instantiate it or mutate counters as an application-level connection-pool control surface.
Local caches
Section titled “Local caches”Under com.loomcache.client.cache:
NearCache— sized LRU with TTL, updated by server-push invalidations.ReadThroughCache,AsyncReadThroughCache— cache-loader fronted with single-flight coalescing.WriteBehindCache— in-process buffer with asynchronous flush to the cluster.CacheLoader,AsyncCacheLoader— SPIs you implement.
Retry & routing
Section titled “Retry & routing”RetryPolicy— exponential backoff with jitter for known-safe retryable failures. Unknown operation outcomes are not automatically retried; reconcile state or retry only with application-level idempotency or an explicit reusable token where an API provides one. Explicit application tokens are scoped by the authenticated requester or client endpoint, not by the randomLoomClientprocess instance.- Smart routing — partition-aware retries with leader fallback.
- Client-side leader cache — caches the last known leader; cleared explicitly or on authenticated redirect.
- Router-level mutation idempotency keys protect the client’s own retry loop; a fresh application call gets a fresh key unless the API exposes an explicit reusable application token.
Protocol compatibility
Section titled “Protocol compatibility”- The version handshake is fail-closed: peers must use the same artifact version (
2.1.0), this build’s supported protocol value, and the same Kryo registration digest. The server rejects unknown negotiated capabilities; the client exposes only recognized capability flags. Handshake payloads reject unknown extension TLVs instead of skipping them. This release has no mixed-version rolling window. - Protocol error responses can carry typed payloads or legacy/free-form UTF-8 details. The client maps known typed/error-token payloads to specific
LoomExceptionsubclasses and otherwise raisesLoomExceptionwith the server detail. Supported retry handling should catchLoomExceptionand inspectretryClassification()/errorCode(); concrete transport backpressure subclass names are not the supported catch surface.
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.