Package dev.cachly

Class CachlyClient

java.lang.Object
dev.cachly.CachlyClient
All Implemented Interfaces:
Closeable, AutoCloseable

public final class CachlyClient extends Object implements Closeable
Official Java client for cachly.dev managed Valkey/Redis instances.

Wraps Jedis with a typed API and optional semantic AI caching. Thread-safe – one instance per application is recommended.

Quick start


 try (CachlyClient cache = CachlyClient.connect(System.getenv("CACHLY_URL"))) {

     // Simple key/value
     cache.set("user:42", Map.of("name", "Alice"), 300);
     Map<?,?> user = cache.get("user:42", Map.class);

     // Semantic AI cache with pgvector API (Speed / Business tiers)
     SemanticResult<String> result = cache.semantic().getOrSet(
         question,
         () -> openAi.ask(question),
         text -> openAi.embed(text),
         0.92
     );
     System.out.println(result.isHit() ? "⚡ hit" : "🔄 miss");
 }
 
  • Method Details

    • connect

      public static CachlyClient connect(String url)
      Connect to a cachly instance by URL (no pgvector API).
      Parameters:
      url - Redis connection URL: redis://:password@host:port
    • connect

      public static CachlyClient connect(String url, String vectorUrl)
      Connect to a cachly instance with pgvector API support (HNSW O(log n)).
      Parameters:
      url - Redis connection URL: redis://:password@host:port
      vectorUrl - pgvector API URL: https://api.cachly.dev/v1/sem/{token} Pass null to disable API mode (falls back to SCAN).
    • connect

      public static CachlyClient connect(String url, String vectorUrl, String batchUrl)
      Connect to a cachly instance with full API support.
      Parameters:
      url - Redis connection URL: redis://:password@host:port
      vectorUrl - pgvector API URL (or null).
      batchUrl - KV Batch API URL: https://api.cachly.dev/v1/cache/{token} When set, batch(java.util.List<dev.cachly.CachlyClient.BatchOp>) sends all ops in one HTTP request.
    • get

      public <T> T get(String key, Class<T> type)
      Retrieve a value by key and deserialize to type.
      Returns:
      deserialized value, or null when not found
    • set

      public void set(String key, Object value, long ttlSeconds)
      Store a value as JSON. Pass ttlSeconds > 0 for automatic expiry.
    • set

      public void set(String key, Object value)
      Store a value without TTL.
    • del

      public long del(String... keys)
      Delete one or more keys.
      Returns:
      number of keys deleted
    • exists

      public boolean exists(String key)
      Check whether a key exists.
    • expire

      public void expire(String key, long seconds)
      Update TTL for an existing key.
      Parameters:
      seconds - new TTL in seconds
    • ttl

      public long ttl(String key)
      Return the remaining TTL for a key in seconds.
      Returns:
      seconds remaining; -1 if no TTL; -2 if key not found
    • ping

      public boolean ping()
      Check connectivity to the cache server.
      Returns:
      true if the server is reachable and responds to PING
    • incr

      public long incr(String key)
      Atomically increment a counter.
      Returns:
      new value after increment
    • getOrSet

      public <T> T getOrSet(String key, Supplier<T> fn, Class<T> type, long ttlSeconds)
      Get-or-set: return cached value or call fn, cache and return.
      Parameters:
      ttlSeconds - TTL for newly cached entry; 0 = no expiry
    • getOrSet

      public <T> T getOrSet(String key, Supplier<T> fn, Class<T> type)
      Get-or-set without TTL.
    • mset

      public void mset(List<CachlyClient.MSetItem> items)
      Set multiple key-value pairs in a single pipeline round-trip. Supports per-key TTL – unlike native MSET which has no expiry option.
      
       cache.mset(List.of(
           new CachlyClient.MSetItem("user:1", Map.of("name", "Alice"), 300),
           new CachlyClient.MSetItem("user:2", Map.of("name", "Bob"))
       ));
       
    • mget

      public <T> List<T> mget(List<String> keys, Class<T> type)
      Retrieve multiple keys in one round-trip (native MGET). Returns a list in the same order as keys; missing keys return null.
      
       List<String> vals = cache.mget(List.of("user:1", "user:2"), String.class);
       
      Type Parameters:
      T - target type for each value
      Parameters:
      keys - list of keys to fetch
      type - class to deserialize each value into
      Returns:
      list of values (null for missing keys)
    • lock

      public CachlyClient.LockHandle lock(String key, long ttlMs, int retries, long retryDelayMs)
      Acquire a distributed lock using Redis SET NX PX (Redlock-lite pattern).

      Returns a CachlyClient.LockHandle on success, or null when all attempts are exhausted. The lock auto-expires after ttlMs to prevent deadlocks.

      
       try (CachlyClient.LockHandle lock = cache.lock("job:42", 5000, 5)) {
           if (lock == null) throw new IllegalStateException("Busy");
           processJob();
       }
       
      Parameters:
      key - Resource identifier (prefixed with cachly:lock:).
      ttlMs - Safety TTL in milliseconds.
      retries - Max acquire attempts (default 3).
      retryDelayMs - Milliseconds between retries (default 50).
      Returns:
      LockHandle or null
    • lock

      public CachlyClient.LockHandle lock(String key, long ttlMs)
      Acquire a distributed lock with default retry settings (3 retries, 50 ms delay).
    • batch

      Execute multiple cache operations in a single round-trip.

      When batchUrl is configured (pass to connect(String, String, String)), all ops are sent via POST {batchUrl}/batch (one HTTP request). Otherwise they fall back to a Jedis pipeline.

      
       List<CachlyClient.BatchOpResult> results = cache.batch(List.of(
           CachlyClient.BatchOp.get("user:1"),
           CachlyClient.BatchOp.get("config:app"),
           CachlyClient.BatchOp.set("visits", Long.toString(System.currentTimeMillis()), 86400),
       ));
       String user   = results.get(0).value;   // null if not found
       String config = results.get(1).value;
       boolean ok    = results.get(2).ok;
       
    • streamSet

      public void streamSet(String key, Iterable<String> chunks, long ttlSeconds)
      Cache a streaming response chunk-by-chunk via Redis RPUSH.

      After all chunks are stored, an optional TTL is set on the list key. Replay stored chunks with streamGet(String).

      Parameters:
      key - Cache key.
      chunks - Iterable of string chunks (e.g. LLM token stream).
      ttlSeconds - TTL in seconds; 0 = no expiry.
    • streamSet

      public void streamSet(String key, Iterable<String> chunks)
      Cache a streaming response without TTL.
    • streamGet

      public Iterator<String> streamGet(String key)
      Retrieve a cached stream as a lazy Iterator<String>.

      Returns null on a cache miss (key absent or empty list). The iterator fetches chunks one-by-one via LINDEX.

      
       Iterator<String> cached = cache.streamGet("chat:42");
       if (cached != null) {
           while (cached.hasNext()) response.write(cached.next());
       }
       
    • semantic

      public SemanticCache semantic()
      Access the SemanticCache helper. Available on Speed and Business tiers.
    • pubsub

      public PubSubClient pubsub()
      Return a PubSubClient for real-time messaging (feature U). Requires pubsubUrl via CachlyClient.Builder.pubsubUrl(String).
    • workflow

      public WorkflowClient workflow()
      Return a WorkflowClient for agent workflow checkpoints (feature V). Requires workflowUrl via CachlyClient.Builder.workflowUrl(String).
    • edge

      public EdgeCacheClient edge()
      Return an EdgeCacheClient for managing Cloudflare Edge Cache (feature #5). Requires edgeApiUrl via CachlyClient.Builder.edgeApiUrl(String).
      Returns:
      EdgeCacheClient for config, purge, and stats operations
      Throws:
      CachlyException - if edgeApiUrl was not configured
    • setTags

      public CachlyClient.TagsResult setTags(String key, List<String> tags)
      Associate a cache key with tags (max 50) (L). Requires batchUrl.
    • invalidateTag

      public CachlyClient.InvalidateTagResult invalidateTag(String tag)
      Delete all cache keys for a tag (M). Requires batchUrl.
    • getTags

      public List<String> getTags(String key)
      Get all tags for a cache key (N). Requires batchUrl.
    • deleteTags

      public boolean deleteTags(String key)
      Remove all tags for a cache key (O). Requires batchUrl.
    • swrRegister

      public boolean swrRegister(String key, int ttlSeconds, int staleWindowSeconds, String fetcherHint)
      Register a key for Stale-While-Revalidate (P). Requires batchUrl.
    • swrRegister

      public boolean swrRegister(String key, int ttlSeconds, int staleWindowSeconds)
    • swrCheck

      public CachlyClient.SwrCheckResult swrCheck()
      Return all keys currently stale (Q). Requires batchUrl.
    • swrRemove

      public boolean swrRemove(String key)
      Remove a key from the SWR registry (R). Requires batchUrl.
    • bulkWarmup

      KV Bulk-Warmup via server-side pipeline (S). Requires batchUrl.
    • llmProxyStats

      public CachlyClient.LlmProxyStats llmProxyStats()
      LLM cache proxy statistics (W). Requires llmProxyUrl.
    • builder

      public static CachlyClient.Builder builder(String url)
      Fluent builder for constructing a CachlyClient with all optional URLs.
      
       CachlyClient client = CachlyClient.builder(System.getenv("CACHLY_URL"))
           .vectorUrl(System.getenv("CACHLY_VECTOR_URL"))
           .batchUrl(System.getenv("CACHLY_BATCH_URL"))
           .pubsubUrl(System.getenv("CACHLY_PUBSUB_URL"))
           .workflowUrl(System.getenv("CACHLY_WORKFLOW_URL"))
           .build();
       
    • raw

      public redis.clients.jedis.UnifiedJedis raw()
      Direct access to the underlying Jedis client for advanced operations.
    • close

      public void close()
      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable