{"id":19585497,"url":"https://github.com/stremovskyy/cachemar","last_synced_at":"2025-11-20T14:03:00.065Z","repository":{"id":186707715,"uuid":"675600059","full_name":"stremovskyy/cachemar","owner":"stremovskyy","description":"CacheMar is a cache management library designed to provide a unified and easy-to-use interface for working with various caching systems","archived":false,"fork":false,"pushed_at":"2025-10-02T08:48:26.000Z","size":76,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-12T09:48:14.138Z","etag":null,"topics":["cache","cache-control","go","golang","library","memcached","pkg","redis-cache"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stremovskyy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-07T09:43:45.000Z","updated_at":"2025-10-02T08:48:30.000Z","dependencies_parsed_at":"2025-10-02T10:16:41.768Z","dependency_job_id":"10c4d68a-8920-4118-8811-11c125abcf8f","html_url":"https://github.com/stremovskyy/cachemar","commit_stats":null,"previous_names":["stremovskyy/cachemar"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/stremovskyy/cachemar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stremovskyy%2Fcachemar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stremovskyy%2Fcachemar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stremovskyy%2Fcachemar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stremovskyy%2Fcachemar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stremovskyy","download_url":"https://codeload.github.com/stremovskyy/cachemar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stremovskyy%2Fcachemar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285447937,"owners_count":27173436,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-20T02:00:05.334Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","cache-control","go","golang","library","memcached","pkg","redis-cache"],"created_at":"2024-11-11T07:54:38.400Z","updated_at":"2025-11-20T14:03:00.059Z","avatar_url":"https://github.com/stremovskyy.png","language":"Go","readme":"# CacheMar - Cache Management Library\n\nCacheMar is a versatile cache management library crafted to offer a seamless interface across multiple caching systems. With its intuitive design, developers can effortlessly transition between different caching drivers, ensuring adaptability without substantial code alterations. Presently, CacheMar extends support to both in-memory caching and Memcached, with the flexibility for future expansions.\n\n## Table of Contents\n\n- [CacheMar - Cache Management Library](#cachemar---cache-management-library)\n  - [Table of Contents](#table-of-contents)\n  - [Features](#features)\n  - [Installation](#installation)\n  - [Getting Started](#getting-started)\n  - [Supported Drivers](#supported-drivers)\n  - [Usage](#usage)\n    - [Creating a CacheMar Service](#creating-a-cachemar-service)\n    - [Registering Caching Drivers](#registering-caching-drivers)\n    - [Setting the Current Cache Driver](#setting-the-current-cache-driver)\n    - [Using the Cache](#using-the-cache)\n    - [Using Tags for Invalidation](#using-tags-for-invalidation)\n    - [Using Chains](#using-chains)\n    - [Using Chaining](#using-chaining)\n    - [Setting a Fallback](#setting-a-fallback)\n    - [Overriding the Chain](#overriding-the-chain)\n    - [Other Cache Operations](#other-cache-operations)\n  - [Examples](#examples)\n    - [In-Memory Cache Example](#in-memory-cache-example)\n    - [Memcached Example](#memcached-example)\n    - [Redis Example](#redis-example)\n    - [Redis Cluster Example](#redis-cluster-example)\n- [License](#license)\n  - [Testing](#testing)\n    - [Quick Test Commands](#quick-test-commands)\n    - [Test Environment Setup](#test-environment-setup)\n    - [Automated Testing](#automated-testing)\n    - [Docker Testing Environment](#docker-testing-environment)\n  - [Documentation](#documentation)\n- [Contributing](#contributing)\n\n## Features\n\n* **Unified API**: A consistent interface across various caching drivers, making it easy to switch or combine them.\n* **Multiple Drivers**: Built-in support for in-memory caching, Memcached, Redis single instance, and Redis Cluster.\n* **LRU Memory Protection**: In-memory cache with configurable LRU eviction prevents unlimited memory growth and application crashes.\n* **Redis Cluster Support**: Full Redis Cluster support with automatic failover, load balancing, and advanced configuration options.\n* **Dynamic Switching**: Seamlessly switch between caching drivers with minimal code changes.\n* **Chaining Mechanism**: Chain multiple cache managers for a fallback mechanism. If one manager doesn't have the data or encounters an error, the next one in the chain is used.\n* **Tag-based Caching**: Invalidate cache entries easily using tags.\n* **Numeric Operations**: Increment and decrement operations for integer values in the cache.\n* **Context Compatibility**: Fully compatible with Go's context package, allowing for request-scoped caching.\n* **Fallback Support**: Set a default fallback cache manager to be used if none in the chain have the data.\n* **Chain Override**: Temporarily override the chain for specific calls without affecting the original configuration.\n* **Circuit Breaker Pattern**: Automatically switch to fallback cachers if the primary cacher fails, and switch back when it's available again.\n* **Advanced Redis Features**: TLS/SSL support, connection pooling, compression, and flexible routing options.\n* **100% Backward Compatibility**: Existing Redis single-instance code works unchanged.\n\n## Installation\nTo use CacheMar in your Go project, you can install it using the go get command:\n\n```bash\ngo get github.com/stremovskyy/cachemar\n```\n\n## Getting Started\nBefore you start using CacheMar, you need to import the package into your Go code:\n\n```go\nimport (\n\"context\"\n\"time\"\n\n    \"github.com/stremovskyy/cachemar\"\n)\n```\n\n## Supported Drivers\nCacheMar seamlessly integrates with a variety of caching drivers, including:\n\n1. **In-Memory Cache**: A high-performance in-memory caching solution with LRU (Least Recently Used) eviction support. Features include:\n   - **LRU Eviction**: Configurable maximum cache size with automatic eviction of least recently used items\n   - **Unlimited Mode**: Default unlimited cache size for backward compatibility\n   - **Thread-Safe**: Concurrent access with proper synchronization\n   - **Compression**: Built-in Gzip compression for stored data\n   - **Tag Support**: Tag-based cache invalidation\n   - **Memory Protection**: Prevents application crashes from unlimited memory growth\n2. **Memcached**: With CacheMar, interfacing with Memcached—a renowned distributed caching system—becomes effortless. It's tailored for expansive applications necessitating cache distribution across multiple instances or servers.\n3. **Redis**: CacheMar facilitates smooth interactions with Redis, a prominent in-memory data structure store. Supports both single instance and cluster modes with full backward compatibility.\n   - **Single Instance**: Traditional Redis setup with one server\n   - **Cluster Mode**: Distributed Redis cluster for high availability and scalability\n   - **TLS Support**: Secure connections with SSL/TLS encryption\n   - **Connection Pooling**: Optimized connection management for performance\n   - **Compression**: Optional Gzip compression for stored data\n\n\n## Usage\n### Creating a CacheMar Service\nTo use CacheMar, you first need to create a new Service instance using cachemar.New():\n\n```go\ncacheService := cachemar.New()\n```\n\n### Registering Caching Drivers\nNext, you can register the caching drivers you want to use with CacheMar:\n\n```go\ninMemoryCache := memory.NewMemoryCacheService()\ncacheService.Register(\"in-memory\", inMemoryCache)\n\nmemcachedOptions := \u0026memcached.Options{\nServers: []string{\"localhost:11211\"},\nPrefix:  \"my-cache\",\n}\nmemcachedCache := memcached.NewCacheService(memcachedOptions)\ncacheService.Register(\"memcached\", memcachedCache)\n```\n\n### Setting the Current Cache Driver\nAfter registering the caching drivers, you can set the current driver to use with CacheMar:\n\n```go\ncacheService.SetCurrentDriver(\"in-memory\")\n```\n\n### Using the Cache\nOnce you have set the current cache driver, you can use the cache methods provided by CacheMar:\n\n```go\nctx := context.Background()\nkey := \"my-key\"\nvalue := \"my-value\"\nttl := 5 * time.Minute\n\n// Set a value in the cache\nerr := cacheService.Set(ctx, key, value, ttl, nil)\nif err != nil {\n    // Handle error\n}\n\n// Get a value from the cache\nvar retrievedValue string\nerr = cacheService.Get(ctx, key, \u0026retrievedValue)\nif err != nil {\n    // Handle error or cache miss\n}\n\n// Remove a value from the cache\nerr = cacheService.Remove(ctx, key)\nif err != nil {\n    // Handle error\n}\n```\n\n### Using Tags for Invalidation\nCacheMar supports tag-based caching to easily invalidate multiple keys at once:\n\n```go\ntags := []string{\"tag1\", \"tag2\"}\n\n// Set a value in the cache with tags\nerr := cacheService.Set(ctx, key, value, ttl, tags)\nif err != nil {\n    // Handle error\n}\n\n// Remove all keys associated with a specific tag\nerr = cacheService.RemoveByTag(ctx, \"tag1\")\nif err != nil {\n    // Handle error\n}\n```\n\n### Using Chains\nCacheMar supports chaining multiple cache managers together for a fallback mechanism. If one manager doesn't have the data or encounters an error, the next one in the chain is used. You can create a chain of cache managers using cachemar.Chain():\n\n   ```go\n   manager := cachemar.New()\nmanager.Register(\"redis\", redisCache)\nmanager.Register(\"memory\", memoryCache)\nmanager.SetCurrent(\"redis\")\n   ```\n\n### Using Chaining\nWith chaining, you can specify an order of cache managers. If the first one doesn't have the data, the next one is queried, and so on.\n```go\nchainedManager := manager.Chain()\nchainedManager.AddToChain(\"redis\")\nchainedManager.AddToChain(\"memory\")\nchainedManager.Get(ctx, \"somekey\", \u0026value) // This will first check redis, then memory.\n\n```\n\n### Setting a Fallback\nA fallback cache manager can be set which will be queried if none in the chain have the data\n\n```go\nchainedManager.SetFallback(\"memory\")\n```\n\n### Overriding the Chain\nFor specific calls, you can override the chain without affecting the original chain configuration.\n```go\nchainedManager.Override(\"memory\", \"redis\").Get(ctx, \"somekey\", \u0026value) // This will first check memory, then redis.\n```\n\n### Using the Circuit Breaker Pattern\nCacheMar supports the circuit breaker pattern to automatically switch to fallback cachers if the primary cacher fails, and switch back when it's available again. This is useful for handling temporary failures in the primary cacher, such as network issues or server restarts.\n\n```go\n// Create a manager with circuit breaker\nmanager := cachemar.NewWithOptions(\n    cachemar.WithDebug(),\n    cachemar.WithCircuitBreaker(\"redis\", []string{\"memory\"}, 5*time.Second),\n)\n\n// Register the cachers\nmanager.Register(\"redis\", redisCache)\nmanager.Register(\"memory\", memoryCache)\n\n// Use the manager as usual\nerr := manager.Set(ctx, \"key\", \"value\", time.Minute, nil)\n```\n\nIn this example, if the Redis cacher fails, the manager will automatically switch to the memory cacher. It will periodically check if the Redis cacher is back online, and switch back to it when it is.\n\n### Other Cache Operations\nCacheMar also provides other cache operations like increment and decrement for integer values:\n\n```go\nkey := \"my-counter\"\n\n// Increment the value of the key by 1\nerr := cacheService.Increment(ctx, key)\nif err != nil {\n    // Handle error\n}\n\n// Decrement the value of the key by 1\nerr = cacheService.Decrement(ctx, key)\nif err != nil {\n    // Handle error\n}\n```\n\n## Examples\n### In-Memory Cache Example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"time\"\n\n    \"github.com/stremovskyy/cachemar\"\n    \"github.com/stremovskyy/cachemar/drivers/memory\"\n)\n\nfunc main() {\n    cacheService := cachemar.New()\n\n    // Basic unlimited cache (default behavior)\n    inMemoryCache := memory.New()\n    cacheService.Register(\"in-memory\", inMemoryCache)\n\n    // Or create cache with LRU eviction (recommended for production)\n    lruCache := memory.NewWithConfig(memory.Config{\n        MaxSize: 1000, // Maximum 1000 items before LRU eviction\n    })\n    cacheService.Register(\"lru-memory\", lruCache)\n\n    // Use the unlimited cache\n    cacheService.SetCurrent(\"in-memory\")\n\n    ctx := context.Background()\n    key := \"my-key\"\n    value := \"my-value\"\n    ttl := 5 * time.Minute\n\n    // Set a value in the cache\n    err := cacheService.Set(ctx, key, value, ttl, nil)\n    if err != nil {\n        fmt.Println(\"Error:\", err)\n    }\n\n    // Get a value from the cache\n    var retrievedValue string\n    err = cacheService.Get(ctx, key, \u0026retrievedValue)\n    if err != nil {\n        fmt.Println(\"Error:\", err)\n    }\n\n    fmt.Println(\"Retrieved Value:\", retrievedValue)\n\n    // Switch to LRU cache for memory protection\n    cacheService.SetCurrent(\"lru-memory\")\n    \n    // Now the cache will automatically evict least recently used items\n    // when it reaches 1000 items, preventing memory exhaustion\n}\n\n```\n\n### Memcached Example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"time\"\n\n    \"github.com/stremovskyy/cachemar\"\n    \"github.com/stremovskyy/cachemar/memcached\"\n)\n\nfunc main() {\n    cacheService := cachemar.New()\n\n    memcachedOptions := \u0026memcached.Options{\n        Servers: []string{\"localhost:11211\"},\n        Prefix:  \"my-cache\",\n    }\n    memcachedCache := memcached.NewCacheService(memcachedOptions)\n    cacheService.Register(\"memcached\", memcachedCache)\n\n    ctx := context.Background()\n    key := \"my-key\"\n    value := \"my-value\"\n    ttl := 5 * time.Minute\n\n    // Set a value in the cache\n    err := cacheService.Set(ctx, key, value, ttl, nil)\n    if err != nil {\n        fmt.Println(\"Error:\", err)\n    }\n\n    // Get a value from the cache\n    var retrievedValue string\n    err = cacheService.Get(ctx, key, \u0026retrievedValue)\n    if err != nil {\n        fmt.Println(\"Error:\", err)\n    }\n\n    fmt.Println(\"Retrieved Value:\", retrievedValue)\n}\n\n   ```\n### Redis Example\n\n```go\n    package main\n\n    import (\n        \"context\"\n        \"fmt\"\n        \"time\"\n\n        \"github.com/stremovskyy/cachemar\"\n        \"github.com/stremovskyy/cachemar/drivers/redis\"\n    )\n\n    func main() {\n        cacheService := cachemar.New()\n\n        // Single Redis instance (backward compatible)\n        redisOptions := redis.NewSingleInstanceOptions(\"localhost:6379\", \"\", 0).\n            WithCompression().\n            WithPrefix(\"my-cache\")\n\n        redisCache := redis.New(redisOptions)\n        cacheService.Register(\"redis\", redisCache)\n\n        ctx := context.Background()\n        key := \"my-key\"\n        value := \"my-value\"\n        ttl := 5 * time.Minute\n\n        // Set a value in the cache\n        err := cacheService.Set(ctx, key, value, ttl, nil)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        // Get a value from the cache\n        var retrievedValue string\n        err = cacheService.Get(ctx, key, \u0026retrievedValue)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        fmt.Println(\"Retrieved Value:\", retrievedValue)\n    }\n```\n\n### Redis Cluster Example\n\n```go\n    package main\n\n    import (\n        \"context\"\n        \"fmt\"\n        \"time\"\n\n        \"github.com/stremovskyy/cachemar\"\n        \"github.com/stremovskyy/cachemar/drivers/redis\"\n    )\n\n    func main() {\n        cacheService := cachemar.New()\n\n        // Redis Cluster configuration\n        clusterOptions := redis.NewClusterOptions(\n            []string{\n                \"localhost:7000\",\n                \"localhost:7001\", \n                \"localhost:7002\",\n                \"localhost:7003\",\n                \"localhost:7004\",\n                \"localhost:7005\",\n            },\n            \"\", // cluster password\n        ).WithCompression().WithPrefix(\"cluster-cache\")\n\n        clusterCache := redis.New(clusterOptions)\n        cacheService.Register(\"redis-cluster\", clusterCache)\n\n        ctx := context.Background()\n        key := \"cluster-key\"\n        value := map[string]interface{}{\n            \"user_id\": 123,\n            \"name\":    \"John Doe\",\n            \"active\":  true,\n        }\n        ttl := 1 * time.Hour\n\n        // Set a value in the cluster\n        err := cacheService.Set(ctx, key, value, ttl, []string{\"users\", \"active\"})\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        // Get a value from the cluster\n        var retrievedValue map[string]interface{}\n        err = cacheService.Get(ctx, key, \u0026retrievedValue)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        fmt.Printf(\"Retrieved Value: %+v\\n\", retrievedValue)\n\n        // Get keys by tag\n        keys, err := cacheService.GetKeysByTag(ctx, \"users\")\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        fmt.Printf(\"User keys: %v\\n\", keys)\n    }\n```\n\u003e 📖 **For comprehensive Redis cluster documentation**, including advanced configuration options, TLS setup, performance tuning, and migration guides, see [Redis Cluster Documentation](docs/REDIS_CLUSTER.md).\n        err := cacheService.Set(ctx, key, value, ttl, nil)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        // Get a value from the cache\n        var retrievedValue string\n        err = cacheService.Get(ctx, key, \u0026retrievedValue)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        fmt.Println(\"Retrieved Value:\", retrievedValue)\n    }\n\n```\n### Chaining Multiple Cache Managers Example\n\n```go\n    package main\n\n    import (\n        \"context\"\n        \"fmt\"\n        \"time\"\n\n        \"github.com/stremovskyy/cachemar\"\n    )\n\n    func main() {\n      // Initialize CacheMar and register cache managers\n      manager := cachemar.New()\n      manager.Register(\"inMemory\", inMemoryCache)\n      manager.Register(\"memcached\", memcachedCache)\n      manager.Register(\"redis\", redisCache)\n\n      // Create a chain of cache managers\n      chainedManager := manager.Chain()\n      chainedManager.AddToChain(\"inMemory\")\n      chainedManager.AddToChain(\"memcached\")\n      chainedManager.AddToChain(\"redis\")\n\n      // Use the chained manager\n      err := chainedManager.Get(ctx, \"someKey\", \u0026value)\n\n\t  // Or in manager \n\t  err := manager.Chain().Get(ctx, \"someKey\", \u0026value)\n    }\n\n```\n\n### Circuit Breaker Pattern Example\n\n```go\n    package main\n\n    import (\n        \"context\"\n        \"fmt\"\n        \"time\"\n\n        \"github.com/stremovskyy/cachemar\"\n        \"github.com/stremovskyy/cachemar/drivers/memory\"\n        \"github.com/stremovskyy/cachemar/drivers/redis\"\n    )\n\n    func main() {\n        // Create a manager with circuit breaker\n        // This will use Redis as the primary cacher and memory as the fallback\n        // It will check if Redis is back online every 5 seconds\n        manager := cachemar.NewWithOptions(\n            cachemar.WithDebug(),\n            cachemar.WithCircuitBreaker(string(cachemar.RedisCacherName), []string{string(cachemar.MemoryCacherName)}, 5*time.Second),\n        )\n\n        // Register the Redis cacher\n        redisOptions := redis.NewSingleInstanceOptions(\"localhost:6379\", \"\", 0).\n            WithCompression().\n            WithPrefix(\"my-cache\")\n        redisCache := redis.New(redisOptions)\n        manager.Register(string(cachemar.RedisCacherName), redisCache)\n\n        // Register the memory cacher\n        memoryCache := memory.New()\n        manager.Register(string(cachemar.MemoryCacherName), memoryCache)\n\n        // Use the manager as usual\n        ctx := context.Background()\n        key := \"my-key\"\n        value := \"my-value\"\n        ttl := 5 * time.Minute\n\n        // Set a value in the cache\n        // If Redis is unavailable, it will automatically use the memory cacher\n        err := manager.Set(ctx, key, value, ttl, nil)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        // Get a value from the cache\n        // If Redis is unavailable, it will automatically use the memory cacher\n        var retrievedValue string\n        err = manager.Get(ctx, key, \u0026retrievedValue)\n        if err != nil {\n            fmt.Println(\"Error:\", err)\n        }\n\n        fmt.Println(\"Retrieved Value:\", retrievedValue)\n\n        // When Redis comes back online, the manager will automatically switch back to it\n    }\n```\n\n# License\nCacheMar is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.\n\n# Contributing\nContributions are welcome! Feel free to open an issue or submit a pull request if you have a way to improve CacheMar.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstremovskyy%2Fcachemar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstremovskyy%2Fcachemar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstremovskyy%2Fcachemar/lists"}