Class LoomClient

java.lang.Object
com.loomcache.client.LoomClient
All Implemented Interfaces:
AutoCloseable

public class LoomClient extends Object implements AutoCloseable
LoomCache Client SDK with smart/unisocket routing, near cache, and production-grade resilience.

Client Routing

Default ClientRoutingMode.ALL_MEMBERS maintains connections to all configured seeds and routes requests across active members. ClientRoutingMode.SINGLE_MEMBER keeps one member connection for environments that restrict outbound sockets. ClientRoutingMode.MULTI_MEMBER keeps smart routing within a configured member group.

Near Cache

Frequently accessed entries may be cached locally when near cache is enabled. GET checks near cache first. Invalidated on PUT/DELETE. Configurable TTL and max size.

Connection Management

  • Connects to all seeds on startup
  • Exponential backoff (100ms → 5s cap) + jitter (±25%) on connection retry
  • Request-response correlation via correlation IDs
  • Thread-safe: synchronized + ReentrantLock where appropriate (JEP 491 safe on Java 25+)

Retry Behavior

Failed requests are retried up to maxRetries times (default 3). Between retries, the client applies exponential backoff: delay = min(100ms × 2^attempt, 5000ms), with ±25% jitter added to prevent thundering herd effects.

During leader election, when no leader is available, the client transparently cycles through all known nodes (round-robin) and retries every 100ms for up to 15 seconds. This wait period does not count against the retry limit.

Redirect Handling (Consistency-by-default)

When the server responds with RESPONSE_REDIRECT, the client:

  • Extracts the leader address from the response
  • Caches it for future requests
  • Retries immediately on the leader (or waits if election is in progress)

Request Timeout

Each operation has an individual timeout (default 120 seconds) that applies to the request-response exchange with the server. Timeouts are not retried; they are treated as fatal errors and trigger exponential backoff on the next retry attempt.

Usage

LoomClient client = LoomClient.builder()
    .addSeed("127.0.0.1:5701")
    .addSeed("127.0.0.1:5702")
    .connectionTimeout(Duration.ofSeconds(5))
    .requestTimeout(Duration.ofSeconds(120))
    .maxRetries(3)
    .nearCacheEnabled(true)
    .nearCacheTtl(Duration.ofSeconds(30))
    .build();
client.connect();

// Map operations
client.mapPut("users", "user1", "john");
String value = client.mapGet("users", "user1");

// Queue operations
client.queuePush("tasks", "process_order_123");
String task = client.queuePop("tasks");

// Set operations
client.setAdd("tags", "trending");
Set<String> tags = client.setMembers("tags");

// Cleanup
client.close();
  • Constructor Details

    • LoomClient

      protected LoomClient(LoomClient.Builder builder)
    • LoomClient

      @Deprecated(since="1.1", forRemoval=true) public LoomClient(List<String> seedAddresses)
      Deprecated, for removal: This API element is subject to removal in a future version.
      Use builder() instead for better control and clarity
      Legacy constructor for backward compatibility.

      Prefer using builder() for more explicit configuration. Uses default timeouts and retry settings: - Connection timeout: 5000ms - Request timeout: 10000ms - Max retries: 3 - Near cache: enabled with 30s TTL and 10000 max entries

      Parameters:
      seedAddresses - the list of seed addresses (must not be empty)
      Throws:
      IllegalArgumentException - if seedAddresses is empty
  • Method Details

    • builder

      public static LoomClient.Builder builder()
    • localhost

      public static LoomClient localhost(int port)
      Create a client connecting to localhost on the specified port. For development/testing.
    • connect

      public void connect()
      Establish connections to seed nodes in the cluster.

      In ClientRoutingMode.ALL_MEMBERS, this method connects to each seed address in order. In ClientRoutingMode.SINGLE_MEMBER, it stops after the first successful seed connection. In ClientRoutingMode.MULTI_MEMBER, it connects only to seeds that match the configured member-group IP/CIDR rules. Failed connections are logged but do not block; at least one successful connection is required for subsequent operations to succeed. Seed addresses must be in the format "host:port".

      If the client was configured with authentication (via builder().auth(username, roles) or builder().credentialsFactory(factory)), an AUTH message is automatically sent to all connected nodes after successful connection.

      This method is idempotent: calling it multiple times has no effect after the first call.

      Throws:
      IllegalArgumentException - if a seed address has invalid format
    • close

      public void close()
      Close the client and release all resources.

      This method:

      • Closes all socket connections to cluster nodes
      • Clears the near cache
      • Completes all pending requests with an IOException
      • Marks the client as closed

      After closing, all subsequent operations will fail. This method is idempotent: calling it multiple times has no side effects.

      For use in try-with-resources:

      try (LoomClient client = LoomClient.builder().addSeed("127.0.0.1:5701").build()) {
          client.connect();
          // use client
      } // automatically closed
      
      Specified by:
      close in interface AutoCloseable
    • refreshPartitionTable

      public boolean refreshPartitionTable()
    • mapGet

      public @Nullable String mapGet(String mapName, String key) throws LoomException
      Get a value from a distributed map.

      This operation is routed via smart routing (key hash) to the primary owner node, avoiding an extra network hop. The near cache is checked first if enabled.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times. During leader elections, the client waits transparently for up to 15 seconds.

      Parameters:
      mapName - the name of the map (must not be null or empty)
      key - the key to retrieve (must not be null or empty)
      Returns:
      the value associated with the key, or null if not found
      Throws:
      LoomException - if the operation fails after all retries, or on timeout
    • mapPut

      public @Nullable String mapPut(String mapName, String key, String value) throws LoomException
      Put a key-value pair into a distributed map.

      This operation is routed to the leader (for write consistency) after caching the leader address from redirects. If no leader is available, the client waits transparently during leader elections.

      The near cache entry for this key is invalidated to prevent serving stale data.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      mapName - the name of the map (must not be null or empty)
      key - the key to set (must not be null or empty)
      value - the value to set (must not be null)
      Returns:
      the previous value associated with the key, or null if the key was not present
      Throws:
      LoomException - if the operation fails after all retries, or on timeout
    • mapPutWithTtl

      public @Nullable String mapPutWithTtl(String mapName, String key, String value, Duration ttl) throws LoomException
      Store a key-value pair with server-owned entry TTL.

      This TTL controls the map entry lifetime. It is intentionally separate from per-request timeout APIs such as mapPutWithTimeout(String, String, String, Duration). A negative TTL means use the map default TTL, if any; zero means no explicit entry expiry.

      Throws:
      LoomException
    • mapPutWithTtl

      public @Nullable String mapPutWithTtl(String mapName, String key, String value, long ttl, TimeUnit timeUnit) throws LoomException
      Store a key-value pair with server-owned entry TTL.
      Parameters:
      ttl - entry TTL in timeUnit; negative means use the map default TTL, if any; zero means no explicit entry expiry
      Throws:
      LoomException
    • mapPutIfAbsent

      public @Nullable String mapPutIfAbsent(String mapName, String key, String value) throws LoomException
      Atomically put a key-value pair only if the key is not already present.

      Server executes MAP_PUT_IF_ABSENT (0x08) atomically via DistributedMap.putIfAbsent() — no race window between check and insert.

      Parameters:
      mapName - the name of the map (must not be null or empty)
      key - the key to set (must not be null or empty)
      value - the value to set (must not be null)
      Returns:
      the existing value if the key was already present, or null if the key was inserted
      Throws:
      LoomException - if the operation fails after all retries, or on timeout
    • mapReplace

      public @Nullable String mapReplace(String mapName, String key, String value) throws LoomException
      Throws:
      LoomException
    • mapReplaceCas

      public boolean mapReplaceCas(String mapName, String key, String oldValue, String newValue) throws LoomException
      Throws:
      LoomException
    • mapCompareDelete

      public boolean mapCompareDelete(String mapName, String key, String expectedValue) throws LoomException
      Throws:
      LoomException
    • mapDelete

      public boolean mapDelete(String mapName, String key) throws LoomException
      Throws:
      LoomException
    • mapExecuteOnKey

      public <R> @Nullable R mapExecuteOnKey(String mapName, String key, EntryProcessor<?,?,R> processor) throws LoomException
      Throws:
      LoomException
    • mapDeleteAndGet

      public @Nullable String mapDeleteAndGet(String mapName, String key) throws LoomException
      Atomically remove a key and return the previously associated value.

      Unlike mapDelete(String, String), which discards the server response payload, this variant surfaces the deleted value directly from the single round-trip MAP_DELETE response. That guarantees destructive-read semantics: the value returned here is exactly what the server removed, with no get-then-delete race against concurrent writers.

      Parameters:
      mapName - target map
      key - key to remove
      Returns:
      the previous value associated with key, or null if the key was not present
      Throws:
      LoomException - if the operation fails
    • mapContainsKey

      public boolean mapContainsKey(String mapName, String key) throws Exception
      Throws:
      Exception
    • mapClear

      public void mapClear(String mapName) throws LoomException
      Throws:
      LoomException
    • mapSize

      public int mapSize(String mapName) throws Exception
      Throws:
      Exception
    • mapScan

      public ScanResult mapScan(String mapName, long cursor, @Nullable String pattern, int count) throws Exception
      Throws:
      Exception
    • mapGetWithTimeout

      public @Nullable String mapGetWithTimeout(String mapName, String key, Duration timeout) throws LoomException
      Get a key-value pair with per-operation timeout override.
      Parameters:
      mapName - the name of the map
      key - the key to look up
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      the value associated with the key, or null if not found
      Throws:
      LoomException - if the operation fails or times out
    • mapPutWithTimeout

      public @Nullable String mapPutWithTimeout(String mapName, String key, String value, Duration timeout) throws LoomException
      Put a key-value pair with per-operation timeout override.
      Parameters:
      mapName - the name of the map
      key - the key to store
      value - the value to associate with the key
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      the previous value, or null if the key was not present
      Throws:
      LoomException - if the operation fails or times out
    • mapDeleteWithTimeout

      public boolean mapDeleteWithTimeout(String mapName, String key, Duration timeout) throws LoomException
      Delete a key-value pair with per-operation timeout override.
      Parameters:
      mapName - the name of the map
      key - the key to remove
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the key was present and removed; false if the key was not found
      Throws:
      LoomException - if the operation fails or times out
    • mapContainsKeyWithTimeout

      public boolean mapContainsKeyWithTimeout(String mapName, String key, Duration timeout) throws LoomException
      Check if a key exists with per-operation timeout override.
      Parameters:
      mapName - the name of the map
      key - the key to check for
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the key exists; false otherwise
      Throws:
      LoomException - if the operation fails or times out
    • queueOffer

      public boolean queueOffer(String queueName, String item) throws Exception
      Enqueue an item at the tail of a distributed FIFO queue.

      The item is appended to the queue and persisted. If the queue does not exist, it is created automatically.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      queueName - the name of the queue (must not be null or empty)
      item - the item to enqueue (must not be null)
      Returns:
      true if the item was successfully enqueued, false otherwise
      Throws:
      Exception - if the operation fails after all retries
    • queuePoll

      public @Nullable String queuePoll(String queueName) throws Exception
      Dequeue an item from the head of a distributed FIFO queue.

      Removes and returns the first item in the queue. If the queue is empty, returns null.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      queueName - the name of the queue (must not be null or empty)
      Returns:
      the first item in the queue, or null if the queue is empty
      Throws:
      Exception - if the operation fails after all retries
    • queuePeek

      public @Nullable String queuePeek(String queueName) throws Exception
      Peek at the head of a distributed queue without removing it.

      Returns the first item in the queue without modifying the queue. If the queue is empty, returns null.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      queueName - the name of the queue (must not be null or empty)
      Returns:
      the first item in the queue, or null if the queue is empty
      Throws:
      Exception - if the operation fails after all retries
    • queueSize

      public int queueSize(String queueName) throws Exception
      Return the number of items currently in a distributed FIFO queue.
      Parameters:
      queueName - the name of the queue (must not be null or empty)
      Returns:
      queue size, or 0 when the queue does not exist
      Throws:
      Exception - if the operation fails after all retries
    • queueOfferAll

      public int queueOfferAll(String queueName, Collection<String> items) throws Exception
      Enqueue multiple items at the tail of a distributed FIFO queue.
      Parameters:
      queueName - the name of the queue (must not be null or empty)
      items - items to enqueue (must not be null and must not contain nulls)
      Returns:
      number of items accepted by the queue
      Throws:
      Exception - if the operation fails after all retries
    • queuePollN

      public List<String> queuePollN(String queueName, int count) throws Exception
      Remove up to count items from the head of a distributed FIFO queue.
      Parameters:
      queueName - the name of the queue (must not be null or empty)
      count - maximum number of items to remove
      Returns:
      removed items in FIFO order; empty when the queue does not exist
      Throws:
      Exception - if the operation fails after all retries
    • queueDrainTo

      public List<String> queueDrainTo(String queueName, int maxElements) throws Exception
      Remove up to maxElements items from the head of a distributed FIFO queue.
      Parameters:
      queueName - the name of the queue (must not be null or empty)
      maxElements - maximum number of items to remove
      Returns:
      removed items in FIFO order; empty when the queue does not exist
      Throws:
      Exception - if the operation fails after all retries
    • queueDrain

      public List<String> queueDrain(String queueName) throws Exception
      Drain the queue into a list.
      Parameters:
      queueName - the name of the queue (must not be null or empty)
      Returns:
      removed items in FIFO order; empty when the queue does not exist
      Throws:
      Exception - if the operation fails after all retries
    • queueOfferWithTimeout

      public boolean queueOfferWithTimeout(String queueName, String item, Duration timeout) throws LoomException
      Enqueue an item with per-operation timeout override.
      Parameters:
      queueName - the name of the queue
      item - the item to enqueue
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the item was successfully enqueued, false otherwise
      Throws:
      LoomException - if the operation fails or times out
    • queuePollWithTimeout

      public @Nullable String queuePollWithTimeout(String queueName, Duration timeout) throws LoomException
      Dequeue an item with per-operation timeout override.
      Parameters:
      queueName - the name of the queue
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      the first item in the queue, or null if the queue is empty
      Throws:
      LoomException - if the operation fails or times out
    • multiMapPut

      public boolean multiMapPut(String mapName, String key, String value) throws Exception
      Throws:
      Exception
    • multiMapGet

      public List<String> multiMapGet(String mapName, String key) throws Exception
      Throws:
      Exception
    • multiMapRemove

      public boolean multiMapRemove(String mapName, String key, String value) throws Exception
      Throws:
      Exception
    • multiMapSize

      public int multiMapSize(String mapName) throws Exception
      Throws:
      Exception
    • multiMapKeys

      public List<String> multiMapKeys(String mapName) throws Exception
      Throws:
      Exception
    • multiMapValues

      public List<String> multiMapValues(String mapName) throws Exception
      Throws:
      Exception
    • multiMapClear

      public void multiMapClear(String mapName) throws Exception
      Throws:
      Exception
    • listAdd

      public boolean listAdd(String listName, String item) throws Exception
      Throws:
      Exception
    • listGet

      public @Nullable String listGet(String listName, int index) throws Exception
      Throws:
      Exception
    • listSet

      public @Nullable String listSet(String listName, int index, String item) throws Exception
      Throws:
      Exception
    • listRemove

      public @Nullable String listRemove(String listName, int index) throws Exception
      Throws:
      Exception
    • listSize

      public int listSize(String listName) throws Exception
      Throws:
      Exception
    • listSubList

      public List<String> listSubList(String listName, int fromIndex, int toIndex) throws Exception
      Throws:
      Exception
    • listClear

      public void listClear(String listName) throws Exception
      Throws:
      Exception
    • priorityQueueOffer

      public boolean priorityQueueOffer(String queueName, String item, int priority) throws Exception
      Throws:
      Exception
    • priorityQueueOffer

      public boolean priorityQueueOffer(String queueName, String item) throws Exception
      Throws:
      Exception
    • priorityQueuePoll

      public @Nullable String priorityQueuePoll(String queueName) throws Exception
      Throws:
      Exception
    • priorityQueuePeek

      public @Nullable String priorityQueuePeek(String queueName) throws Exception
      Throws:
      Exception
    • priorityQueueSize

      public long priorityQueueSize(String queueName) throws Exception
      Throws:
      Exception
    • priorityQueueClear

      public void priorityQueueClear(String queueName) throws Exception
      Throws:
      Exception
    • ringbufferAdd

      public long ringbufferAdd(String ringbufferName, String item, int capacity) throws Exception
      Throws:
      Exception
    • ringbufferReadOne

      public @Nullable String ringbufferReadOne(String ringbufferName, long sequence) throws Exception
      Throws:
      Exception
    • ringbufferReadMany

      public List<String> ringbufferReadMany(String ringbufferName, long startSequence, int maxCount) throws Exception
      Throws:
      Exception
    • ringbufferHeadSequence

      public long ringbufferHeadSequence(String ringbufferName) throws Exception
      Throws:
      Exception
    • ringbufferTailSequence

      public long ringbufferTailSequence(String ringbufferName) throws Exception
      Throws:
      Exception
    • ringbufferCapacity

      public int ringbufferCapacity(String ringbufferName) throws Exception
      Throws:
      Exception
    • reliableTopicPublish

      public long reliableTopicPublish(String topicName, String message, TopicOverloadPolicy policy, int capacity) throws Exception
      Throws:
      Exception
    • reliableTopicReadFrom

      public List<String> reliableTopicReadFrom(String topicName, long fromSequence, int maxCount) throws Exception
      Throws:
      Exception
    • reliableTopicUnsubscribe

      public void reliableTopicUnsubscribe(String topicName) throws Exception
      Throws:
      Exception
    • pnCounterResetSession

      public long pnCounterResetSession(String counterName, String sessionId) throws Exception
      Throws:
      Exception
    • pnCounterGet

      public long pnCounterGet(String counterName, String sessionId, long epoch) throws Exception
      Throws:
      Exception
    • pnCounterIncrementAndGet

      public long pnCounterIncrementAndGet(String counterName, String sessionId, long epoch) throws Exception
      Throws:
      Exception
    • pnCounterDecrementAndGet

      public long pnCounterDecrementAndGet(String counterName, String sessionId, long epoch) throws Exception
      Throws:
      Exception
    • pnCounterAddAndGet

      public long pnCounterAddAndGet(String counterName, long delta, String sessionId, long epoch) throws Exception
      Throws:
      Exception
    • pnCounterSubtractAndGet

      public long pnCounterSubtractAndGet(String counterName, long delta, String sessionId, long epoch) throws Exception
      Throws:
      Exception
    • gSetAdd

      public boolean gSetAdd(String setName, String element) throws Exception
      Throws:
      Exception
    • gSetContains

      public boolean gSetContains(String setName, String element) throws Exception
      Throws:
      Exception
    • gSetSize

      public int gSetSize(String setName) throws Exception
      Throws:
      Exception
    • gSetMembers

      public List<String> gSetMembers(String setName) throws Exception
      Throws:
      Exception
    • orSetAdd

      public boolean orSetAdd(String setName, String element) throws Exception
      Throws:
      Exception
    • orSetRemove

      public boolean orSetRemove(String setName, String element) throws Exception
      Throws:
      Exception
    • orSetContains

      public boolean orSetContains(String setName, String element) throws Exception
      Throws:
      Exception
    • orSetSize

      public int orSetSize(String setName) throws Exception
      Throws:
      Exception
    • orSetMembers

      public List<String> orSetMembers(String setName) throws Exception
      Throws:
      Exception
    • lwwGet

      public @Nullable String lwwGet(String registerName) throws Exception
      Throws:
      Exception
    • lwwSet

      public long lwwSet(String registerName, String serializedValue) throws Exception
      Throws:
      Exception
    • lwwTimestamp

      public long lwwTimestamp(String registerName) throws Exception
      Throws:
      Exception
    • idGeneratorNext

      public long idGeneratorNext(String generatorName) throws Exception
      Throws:
      Exception
    • idGeneratorNextBatch

      public List<Long> idGeneratorNextBatch(String generatorName, int count) throws Exception
      Throws:
      Exception
    • topicPublish

      public void topicPublish(String topicName, String message) throws Exception
      Publish a message to a distributed topic.
      Parameters:
      topicName - the name of the topic
      message - the message to publish
      Throws:
      Exception
    • topicPublishWithTimeout

      public void topicPublishWithTimeout(String topicName, String message, Duration timeout) throws LoomException
      Publish a message to a distributed topic with per-operation timeout override.
      Parameters:
      topicName - the name of the topic
      message - the message to publish
      timeout - per-operation timeout; if null, uses the client's default timeout
      Throws:
      LoomException - if the operation fails or times out
    • topicPublish

      @Deprecated(since="1.0", forRemoval=true) public void topicPublish(String message) throws Exception
      Deprecated, for removal: This API element is subject to removal in a future version.
      Use topicPublish(String, String) with explicit topic name
      Throws:
      Exception
    • topicPoll

      public @Nullable String topicPoll(String topicName, long sequenceId) throws Exception
      Poll for a message from a topic (client-side polling for subscriptions).

      This is a simple polling mechanism used by LoomTopic.subscribe() to fetch messages at regular intervals. For production use, server-push is preferred.

      Parameters:
      topicName - the name of the topic
      sequenceId - the last seen sequence ID (used to avoid duplicates)
      Returns:
      the message if available, null if no new messages
      Throws:
      Exception - on network errors
    • setAdd

      public boolean setAdd(String setName, String element) throws Exception
      Add an element to a distributed set.

      If the element already exists in the set, no change occurs. The operation is routed via smart routing to the node owning this element.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      setName - the name of the set (must not be null or empty)
      element - the element to add (must not be null)
      Returns:
      true if the element was added, false if it was already present
      Throws:
      Exception - if the operation fails after all retries
    • setRemove

      public boolean setRemove(String setName, String element) throws Exception
      Remove an element from a distributed set.

      If the element is not present, no change occurs. The operation is routed via smart routing to the node owning this element.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      setName - the name of the set (must not be null or empty)
      element - the element to remove (must not be null)
      Returns:
      true if the element was removed, false if it was not present
      Throws:
      Exception - if the operation fails after all retries
    • setContains

      public boolean setContains(String setName, String element) throws Exception
      Check if a distributed set contains an element.

      The operation is routed via smart routing to the node owning this element. Results are not cached in the near cache.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      setName - the name of the set (must not be null or empty)
      element - the element to check (must not be null)
      Returns:
      true if the set contains the element, false otherwise
      Throws:
      Exception - if the operation fails after all retries
    • setSize

      public int setSize(String setName) throws Exception
      Get the number of elements in a distributed set.

      Failures are transparently retried with exponential backoff (100ms → 5s) up to maxRetries times.

      Parameters:
      setName - the name of the set (must not be null or empty)
      Returns:
      the number of elements in the set, or 0 if the set does not exist
      Throws:
      Exception - if the operation fails after all retries
    • setClear

      public void setClear(String setName) throws Exception
      Throws:
      Exception
    • setScan

      public ScanResult setScan(String setName, long cursor, @Nullable String pattern, int count) throws Exception
      Throws:
      Exception
    • setScan

      @Deprecated(since="1.0", forRemoval=true) public ScanResult setScan(long cursor) throws Exception
      Deprecated, for removal: This API element is subject to removal in a future version.
      Use setScan(String, long, String, int) with explicit set name
      Throws:
      Exception
    • setAddWithTimeout

      public boolean setAddWithTimeout(String setName, String element, Duration timeout) throws LoomException
      Add an element to a distributed set with per-operation timeout override.
      Parameters:
      setName - the name of the set
      element - the element to add
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the element was added, false if it was already present
      Throws:
      LoomException - if the operation fails or times out
    • setRemoveWithTimeout

      public boolean setRemoveWithTimeout(String setName, String element, Duration timeout) throws LoomException
      Remove an element from a distributed set with per-operation timeout override.
      Parameters:
      setName - the name of the set
      element - the element to remove
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the element was removed, false if it was not present
      Throws:
      LoomException - if the operation fails or times out
    • setContainsWithTimeout

      public boolean setContainsWithTimeout(String setName, String element, Duration timeout) throws LoomException
      Check if a distributed set contains an element with per-operation timeout override.
      Parameters:
      setName - the name of the set
      element - the element to check
      timeout - per-operation timeout; if null, uses client's default
      Returns:
      true if the set contains the element, false otherwise
      Throws:
      LoomException - if the operation fails or times out
    • lockAcquire

      public long lockAcquire(String lockName) throws Exception
      Acquire a linearizable lock, blocking until successful.
      Parameters:
      lockName - the name of the lock
      Returns:
      the fence token for this acquisition
      Throws:
      Exception - if the operation fails
    • lockTryAcquire

      public long lockTryAcquire(String lockName, Duration timeout) throws Exception
      Try to acquire a linearizable lock with timeout.
      Parameters:
      lockName - the name of the lock
      timeout - the timeout duration
      Returns:
      the fence token if acquired; -1 if timed out
      Throws:
      Exception - if the operation fails
    • lockRelease

      public void lockRelease(String lockName) throws Exception
      Release a linearizable lock.
      Parameters:
      lockName - the name of the lock
      Throws:
      Exception - if the operation fails
    • lockGetFence

      @Deprecated(since="1.1", forRemoval=true) public long lockGetFence(String lockName) throws Exception
      Deprecated, for removal: This API element is subject to removal in a future version.
      Use lockAcquire(String) instead. This method misleadingly suggests a read-only fence query but actually acquires the lock.
      Acquire the lock and return the fence token.

      WARNING: This method acquires the lock via CP_LOCK_ACQUIRE. There is no read-only fence query in the current protocol. If you only need to read the fence token without acquiring, this method is NOT suitable -- it will acquire the lock as a side effect. Callers must release the lock when done.

      Parameters:
      lockName - the name of the lock
      Returns:
      the fence token from the acquisition; 0 if not acquired
      Throws:
      Exception - if the operation fails
    • lockAcquireAndGetFence

      public long lockAcquireAndGetFence(String lockName) throws Exception
      Acquire the lock and return the fence token.

      This method sends a CP_LOCK_ACQUIRE request and returns the fence token from the acquisition. The caller holds the lock after this call and must release it when done.

      Parameters:
      lockName - the name of the lock
      Returns:
      the fence token from the acquisition; 0 if not acquired
      Throws:
      Exception - if the operation fails
    • semaphoreAcquire

      public void semaphoreAcquire(String semaphoreName, int permits) throws Exception
      Acquire permits from a semaphore, blocking if necessary.
      Parameters:
      semaphoreName - the name of the semaphore
      permits - the number of permits to acquire
      Throws:
      Exception - if the operation fails
    • semaphoreTryAcquire

      public boolean semaphoreTryAcquire(String semaphoreName, int permits, Duration timeout) throws Exception
      Try to acquire permits from a semaphore with timeout.
      Parameters:
      semaphoreName - the name of the semaphore
      permits - the number of permits to acquire
      timeout - the timeout duration
      Returns:
      true if acquired; false if timed out
      Throws:
      Exception - if the operation fails
    • semaphoreRelease

      public void semaphoreRelease(String semaphoreName, int permits) throws Exception
      Release permits to a semaphore.
      Parameters:
      semaphoreName - the name of the semaphore
      permits - the number of permits to release
      Throws:
      Exception - if the operation fails
    • semaphoreAvailablePermits

      public int semaphoreAvailablePermits(String semaphoreName) throws Exception
      Get the number of available permits in a semaphore.
      Parameters:
      semaphoreName - the name of the semaphore
      Returns:
      the number of available permits
      Throws:
      Exception - if the operation fails
    • latchTrySetCount

      public boolean latchTrySetCount(String latchName, long count) throws Exception
      Throws:
      Exception
    • latchCountDown

      public void latchCountDown(String latchName) throws Exception
      Throws:
      Exception
    • latchAwait

      public void latchAwait(String latchName) throws Exception
      Throws:
      Exception
    • latchAwait

      public boolean latchAwait(String latchName, Duration timeout) throws Exception
      Throws:
      Exception
    • latchGetCount

      public long latchGetCount(String latchName) throws Exception
      Throws:
      Exception
    • atomicReferenceGet

      public <T> @Nullable T atomicReferenceGet(String refName, Class<T> type) throws Exception
      Throws:
      Exception
    • atomicReferenceSet

      public void atomicReferenceSet(String refName, @Nullable Object value) throws Exception
      Throws:
      Exception
    • atomicReferenceCompareAndSet

      public boolean atomicReferenceCompareAndSet(String refName, @Nullable Object expectedValue, @Nullable Object newValue) throws Exception
      Throws:
      Exception
    • atomicLongGet

      public long atomicLongGet(String atomicLongName) throws Exception
      Get the current value of an atomic long.
      Parameters:
      atomicLongName - the name of the atomic long
      Returns:
      the current value
      Throws:
      Exception - if the operation fails
    • atomicLongSet

      public void atomicLongSet(String atomicLongName, long value) throws Exception
      Set the value of an atomic long.
      Parameters:
      atomicLongName - the name of the atomic long
      value - the value to set
      Throws:
      Exception - if the operation fails
    • atomicLongIncrementAndGet

      public long atomicLongIncrementAndGet(String atomicLongName) throws Exception
      Atomically increment an atomic long and return the new value.
      Parameters:
      atomicLongName - the name of the atomic long
      Returns:
      the new value
      Throws:
      Exception - if the operation fails
    • atomicLongDecrementAndGet

      public long atomicLongDecrementAndGet(String atomicLongName) throws Exception
      Atomically decrement an atomic long and return the new value.
      Parameters:
      atomicLongName - the name of the atomic long
      Returns:
      the new value
      Throws:
      Exception - if the operation fails
    • atomicLongGetAndIncrement

      public long atomicLongGetAndIncrement(String atomicLongName) throws Exception
      Atomically get the current value and increment.
      Parameters:
      atomicLongName - the name of the atomic long
      Returns:
      the old value
      Throws:
      Exception - if the operation fails
    • atomicLongGetAndDecrement

      public long atomicLongGetAndDecrement(String atomicLongName) throws Exception
      Atomically get the current value and decrement.
      Parameters:
      atomicLongName - the name of the atomic long
      Returns:
      the old value
      Throws:
      Exception - if the operation fails
    • atomicLongAddAndGet

      public long atomicLongAddAndGet(String atomicLongName, long delta) throws Exception
      Atomically add a delta to an atomic long and return the new value.
      Parameters:
      atomicLongName - the name of the atomic long
      delta - the amount to add
      Returns:
      the new value
      Throws:
      Exception - if the operation fails
    • atomicLongGetAndAdd

      public long atomicLongGetAndAdd(String atomicLongName, long delta) throws Exception
      Atomically get the current value and add a delta.
      Parameters:
      atomicLongName - the name of the atomic long
      delta - the amount to add
      Returns:
      the old value
      Throws:
      Exception - if the operation fails
    • atomicLongCompareAndSet

      public boolean atomicLongCompareAndSet(String atomicLongName, long expectedValue, long newValue) throws Exception
      Atomically compare and set an atomic long.
      Parameters:
      atomicLongName - the name of the atomic long
      expectedValue - the expected current value
      newValue - the new value
      Returns:
      true if updated; false otherwise
      Throws:
      Exception - if the operation fails
    • setNearCacheEnabled

      public void setNearCacheEnabled(boolean enabled)
    • setNearCacheTtlMs

      public void setNearCacheTtlMs(long ttlMs)
      Change the TTL for new near cache entries. Existing entries keep their original TTL.
    • setNearCacheMaxSize

      public void setNearCacheMaxSize(int maxSize)
    • setNearCacheEvictionPolicy

      public void setNearCacheEvictionPolicy(NearCacheEvictionPolicy policy)
    • nearCacheEvictionPolicy

      public NearCacheEvictionPolicy nearCacheEvictionPolicy()
    • getNearCacheSize

      public int getNearCacheSize()
    • clearNearCache

      public void clearNearCache()
    • getNearCacheInvalidationMode

      public NearCacheInvalidationStrategy.InvalidationMode getNearCacheInvalidationMode()
      Get the current near-cache invalidation mode. Useful for metrics/observability to track if invalidation is via server-push or polling.
      Returns:
      the current mode (PUSH or POLL)
    • setNearCacheLocalUpdatePolicy

      public void setNearCacheLocalUpdatePolicy(NearCacheLocalUpdatePolicy policy)
    • nearCacheLocalUpdatePolicy

      public NearCacheLocalUpdatePolicy nearCacheLocalUpdatePolicy()
    • setNearCachePreloaderConfig

      public void setNearCachePreloaderConfig(NearCachePreloaderConfig config)
    • nearCachePreloaderConfig

      public NearCachePreloaderConfig nearCachePreloaderConfig()
    • nearCacheReconciliationConfig

      public NearCacheReconciliationConfig nearCacheReconciliationConfig()
    • setNearCacheReconciliationConfig

      public void setNearCacheReconciliationConfig(NearCacheReconciliationConfig config)
    • setNearCacheSerializeKeys

      public void setNearCacheSerializeKeys(boolean serializeKeys)
    • nearCacheSerializeKeys

      public boolean nearCacheSerializeKeys()
    • addLifecycleListener

      public LoomClient addLifecycleListener(LifecycleListener listener)
    • removeLifecycleListener

      public boolean removeLifecycleListener(LifecycleListener listener)
    • addMembershipListener

      public LoomClient addMembershipListener(MembershipListener listener)
    • removeMembershipListener

      public boolean removeMembershipListener(MembershipListener listener)
    • addDistributedObjectListener

      public LoomClient addDistributedObjectListener(DistributedObjectListener listener)
    • removeDistributedObjectListener

      public boolean removeDistributedObjectListener(DistributedObjectListener listener)
    • getMap

      public <K,V> LoomMap<K,V> getMap(String name)
      Obtain a type-safe handle to a distributed map.

      Note: All keys and values are serialized to String internally via String.valueOf(Object). The generic types provide compile-time convenience but the underlying protocol is String-based.

      Parameters:
      name - the map name (shared across the cluster)
    • getExecutorService

      public LoomExecutorService getExecutorService(String name)
      Obtain a handle to a named distributed executor service.

      Submitted tasks are serialized with this client's Kryo registry. Register the same task/result classes on every server node before using this API.

    • registerClass

      public LoomClient registerClass(Class<?> clazz, int id)
      Register an application class with this client's Kryo serializer.

      The same class and ID must be registered on every server node that may deserialize the payload.

    • registerClass

      public <T> LoomClient registerClass(Class<T> clazz, int id, com.esotericsoftware.kryo.Serializer<? super T> serializer)
      Register an application class with a class-specific Kryo serializer.

      The same class, ID, and serializer must be registered on every server node that may deserialize the payload.

    • getQueue

      public <E> LoomQueue<E> getQueue(String name)
      Obtain a handle to a distributed queue. Same String-serialization caveat as getMap(String).
    • getMultiMap

      public <K,V> LoomMultiMap<K,V> getMultiMap(String name)
      Obtain a handle to a distributed multimap. Same String-serialization caveat as getMap(String).
    • getList

      public <E> LoomList<E> getList(String name)
      Obtain a handle to a distributed list. Same String-serialization caveat as getMap(String).
    • getPriorityQueue

      public <E> LoomPriorityQueue<E> getPriorityQueue(String name)
      Obtain a handle to a distributed priority queue. Same String-serialization caveat as getMap(String).
    • getRingbuffer

      public <E> LoomRingbuffer<E> getRingbuffer(String name)
      Obtain a handle to a distributed ringbuffer. Same String-serialization caveat as getMap(String).
    • getRingbuffer

      public <E> LoomRingbuffer<E> getRingbuffer(String name, int capacity)
      Obtain a handle to a distributed ringbuffer with an explicit creation capacity.
    • getReliableTopic

      public <T> LoomReliableTopic<T> getReliableTopic(String name)
      Obtain a handle to a reliable topic. Same String-serialization caveat as getMap(String).
    • getReliableTopic

      public <T> LoomReliableTopic<T> getReliableTopic(String name, int capacity, TopicOverloadPolicy policy)
      Obtain a handle to a reliable topic with explicit retention capacity and overload policy.
    • getPNCounter

      public LoomPNCounter getPNCounter(String name)
      Obtain a session-bound PN-counter handle.
    • getGSet

      public <E> LoomGSet<E> getGSet(String name)
      Obtain a handle to a grow-only set CRDT. Same String-serialization caveat as getMap(String).
    • getORSet

      public <E> LoomORSet<E> getORSet(String name)
      Obtain a handle to an observed-remove set CRDT. Same String-serialization caveat as getMap(String).
    • getLWWRegister

      public <V> LoomLWWRegister<V> getLWWRegister(String name)
      Obtain a handle to a last-writer-wins register CRDT. Same String-serialization caveat as getMap(String).
    • getIdGenerator

      public LoomIdGenerator getIdGenerator(String name)
      Obtain a distributed Snowflake ID generator without client-side prefetch.
    • getIdGenerator

      public LoomIdGenerator getIdGenerator(String name, int prefetchCount, long prefetchValidityMillis)
      Obtain a distributed Snowflake ID generator with client-side prefetch.
    • getTopic

      public LoomTopic<String> getTopic(String name)
      Obtain a handle to a distributed topic with String messages.

      Warning: this overload returns a String-typed topic because the default wire format is String. For non-String message types you must call getTopic(String, Class) — the previous generic <T> overload silently hard-wired String.class internally and would corrupt non-String payloads.

    • getTopic

      public <T> LoomTopic<T> getTopic(String name, Class<T> messageType)
      Obtain a typed handle to a distributed topic.
    • getSet

      public <E> LoomSet<E> getSet(String name)
      Obtain a handle to a distributed set. Same String-serialization caveat as getMap(String).
    • consistencySubsystem

      public LoomConsistencySubsystem consistencySubsystem()
      Access the Consistency Subsystem for strongly consistent primitives.

      The Consistency Subsystem provides Raft-backed, linearizable primitives including linearizable locks, semaphores, and atomic longs. These have stronger consistency guarantees than the eventually-consistent base data structures.

      Returns:
      a Consistency Subsystem factory
      Since:
      1.4
    • getConsistencySubsystem

      public LoomConsistencySubsystem getConsistencySubsystem()
      JavaBean-style alias for consistencySubsystem().
      Returns:
      a Consistency Subsystem factory
    • sql

      public LoomSqlResult sql(String query)
      Execute a SQL query on the cluster.

      Returns a result containing the query columns and rows. SQL queries are executed on the cluster and results are returned to the client.

      Parameters:
      query - the SQL query string
      Returns:
      a SQL result with columns and rows
      Throws:
      LoomException - if the query fails or the cluster is unavailable
      Since:
      1.4
    • addIndex

      public void addIndex(String mapName, IndexConfig config)
      Adds or replaces index metadata for a distributed map through the SQL DDL path.
      Parameters:
      mapName - the map name to index
      config - the client-side index configuration
      Throws:
      LoomException - if the cluster rejects the DDL or is unavailable
    • batch

      public LoomBatch batch()
      Create a new LoomBatch for batching multiple data-structure operations into a single server-side execution.

      Usage:

      // Fast batch (network optimization, no atomicity)
      client.batch()
          .map("users").put("user:1", userJson)
          .counter("visits").increment()
          .execute();
      
      // Atomic batch (all-or-nothing on this node)
      client.batch().atomic()
          .map("accounts").put("acc:1", newBalance)
          .counter("total").add(delta)
          .execute();
      
      // Replicated atomic (Raft-committed, crash-safe)
      client.batch().atomic().replicated()
          .map("accounts").put("acc:1", newBalance)
          .counter("total").add(delta)
          .execute();
      
      Returns:
      a new batch builder
      Since:
      1.3
    • newTransaction

      public LoomTransaction newTransaction()
      Begin a new server-side transaction with the default timeout.
      Returns:
      a LoomTransaction facade for buffering and committing ops
      Throws:
      LoomException - if the server fails to start the transaction
      Since:
      2.0
      See Also:
    • newTransaction

      public LoomTransaction newTransaction(@Nullable Duration timeout)
      Begin a new server-side transaction with an explicit timeout.

      When the timeout elapses before commit or rollback, the server automatically rolls back the transaction and its id becomes invalid.

      Usage:

      try (LoomTransaction tx = client.newTransaction(Duration.ofSeconds(30))) {
          tx.put("users", "user:1", "Alice");
          tx.setAdd("active-users", "user:1");
          tx.commit();
      }
      
      Parameters:
      timeout - optional timeout; null means use the server default
      Returns:
      a LoomTransaction facade
      Throws:
      LoomException - if the server fails to start the transaction
      Since:
      2.0
    • isConnected

      public boolean isConnected()
    • connectedNodeCount

      public int connectedNodeCount()
    • connectedNodes

      public Set<String> connectedNodes()
    • isStrictHandshakeEnabled

      public boolean isStrictHandshakeEnabled()
      Returns:
      true iff the client was built with strictHandshake(true). Session 7 Sub-mission 2a — accessor is provided ahead of the full ConnectionPool wire-up to give future-session ITs a way to verify the flag propagated without re-exercising the Builder plumbing.
      Since:
      2.1
    • partitionTable

      public ClientPartitionTable partitionTable()
      Returns:
      the client-side partition ownership cache (never null). Populated lazily — consult ClientPartitionTable.currentRevision() to detect whether a snapshot has been installed yet. Session 7 Sub-mission 2b — consumers arrive in Session 8 when the RequestRouter.selectNode rewrite lands.
      Since:
      2.1
    • async

      public AsyncLoomClient async()
      Create an async wrapper for this client using CompletableFuture.
      Returns:
      a borrowed async facade wrapping this client
    • routingMode

      public ClientRoutingMode routingMode()
    • routingMemberGroup

      public @Nullable String routingMemberGroup()
    • routingPartitionGroupConfig

      public PartitionGroupConfig routingPartitionGroupConfig()
    • asyncStart

      public boolean asyncStart()
    • reconnectMode

      public ClientReconnectMode reconnectMode()
    • isReconnecting

      public boolean isReconnecting()
    • registerCqcListener

      public void registerCqcListener(String mapName, LoomClient.MapChangeListener listener, SerializablePredicate predicate) throws LoomException
      Register a CQC-filtered event listener for the given map. Sends a MessageType.CQC_SUBSCRIBE to every connected node carrying the encoded predicate; events matching the predicate are pushed back as MessageType.CQC_EVENT and dispatched via handleListenerEvent(Message).
      Parameters:
      mapName - source map name (non-blank)
      listener - the per-event callback (non-null)
      predicate - the wire-serializable predicate to evaluate server-side
      Throws:
      LoomException - if no connected node accepts the subscription
      Since:
      2.0
    • deregisterCqcListener

      public void deregisterCqcListener(String mapName, LoomClient.MapChangeListener listener)
      Remove a previously registered CQC listener and deliver MessageType.CQC_UNSUBSCRIBE to every connected node.
      Since:
      2.0
    • registerMapListener

      public void registerMapListener(String mapName, LoomClient.MapChangeListener listener) throws LoomException
      Register a listener for entry events on a specific map. This method now uses resilient retry logic with exponential backoff. If server-push listener registration fails, the client will fall back to TTL-based polling.
      Parameters:
      mapName - the name of the map to listen to
      listener - the listener to register
      Throws:
      LoomException - if the registration fails
    • registerLocalMapListener

      public void registerLocalMapListener(String mapName, LoomClient.MapChangeListener listener) throws LoomException
      Register an entry listener on exactly one connected member.

      Unlike registerMapListener(String, MapChangeListener), this does not fan out the server-side subscription to every node. Events are delivered only when the member that accepted this subscription produces the event.

      Parameters:
      mapName - the name of the map to listen to
      listener - the listener to register
      Throws:
      LoomException - if no connected member accepts the registration
    • deregisterLocalMapListener

      public void deregisterLocalMapListener(String mapName, LoomClient.MapChangeListener listener) throws LoomException
      Deregister a local-only map listener.
      Throws:
      LoomException
    • deregisterMapListener

      public void deregisterMapListener(String mapName, LoomClient.MapChangeListener listener) throws LoomException
      Unregister a listener for entry events on a specific map.
      Parameters:
      mapName - the name of the map
      listener - the listener to unregister
      Throws:
      LoomException - if the deregistration fails
    • evaluatePoolScaling

      public int evaluatePoolScaling(String nodeId, int activeConnections, int totalConnections)
      Monitor and evaluate auto-scaling metrics for a node's connection pool. Returns scaling recommendations that can be used by the ConnectionPool to dynamically adjust pool size based on utilization.

      This method evaluates whether pool should scale up (sustained high utilization) or down (sustained low utilization) based on configurable thresholds.

      Parameters:
      nodeId - the target node ID
      activeConnections - current active connections
      totalConnections - total connections in pool
      Returns:
      recommended new pool size, or -1 if no scaling is needed