Dalli [![Tests](https://github.com/petergoldstein/dalli/actions/workflows/tests.yml/badge.svg)](https://github.com/petergoldstein/dalli/actions/workflows/tests.yml) ===== Dalli is a high performance pure Ruby client for accessing memcached servers. Dalli supports: * Simple and complex memcached configurations * Failover between memcached instances * Fine-grained control of data serialization and compression * Thread-safe operation (either through use of a connection pool, or by using the Dalli client in threadsafe mode) * SSL/TLS connections to memcached * OpenTelemetry distributed tracing (automatic when SDK is present) The name is a variant of Salvador Dali for his famous painting [The Persistence of Memory](http://en.wikipedia.org/wiki/The_Persistence_of_Memory). ## Requirements * Ruby 3.3 or later (JRuby also supported) * memcached 1.6 or later ## Configuration Options ### Namespace Use namespaces to partition your cache and avoid key collisions between different applications or environments: ```ruby # All keys will be prefixed with "myapp:" Dalli::Client.new('localhost:11211', namespace: 'myapp') # Dynamic namespace using a Proc (evaluated on each operation) Dalli::Client.new('localhost:11211', namespace: -> { "tenant:#{Thread.current[:tenant_id]}" }) ``` ### Namespace Separator By default, the namespace and key are joined with a colon (`:`). You can customize this with the `namespace_separator` option: ```ruby # Keys will be prefixed with "myapp/" instead of "myapp:" Dalli::Client.new('localhost:11211', namespace: 'myapp', namespace_separator: '/') ``` The separator must be a single non-alphanumeric character. Valid examples: `:`, `/`, `|`, `.`, `-`, `_`, `#` ## Security Note By default, Dalli uses Ruby's Marshal for serialization. Deserializing untrusted data with Marshal can lead to remote code execution. If you cache user-controlled data, consider using a safer serializer: ```ruby Dalli::Client.new('localhost:11211', serializer: JSON) ``` See the [5.0-Upgrade.md](5.0-Upgrade.md) guide for upgrade information. ## OpenTelemetry Tracing Dalli automatically instruments operations with [OpenTelemetry](https://opentelemetry.io/) when the SDK is present. No configuration is required - just add the OpenTelemetry gems to your application: ```ruby # Gemfile gem 'opentelemetry-sdk' gem 'opentelemetry-exporter-otlp' # or your preferred exporter ``` When OpenTelemetry is loaded, Dalli creates spans for: - Single key operations: `get`, `set`, `delete`, `add`, `replace`, `incr`, `decr`, etc. - Multi-key operations: `get_multi`, `set_multi`, `delete_multi` - Advanced operations: `get_with_metadata`, `fetch_with_lock` ### Span Attributes All spans include: - `db.system`: `memcached` - `db.operation`: The operation name (e.g., `get`, `set_multi`) Single-key operations also include: - `server.address`: The memcached server that handled the request (e.g., `localhost:11211`) Multi-key operations include cache efficiency metrics: - `db.memcached.key_count`: Number of keys in the request - `db.memcached.hit_count`: Number of keys found (for `get_multi`) - `db.memcached.miss_count`: Number of keys not found (for `get_multi`) ### Error Handling Exceptions are automatically recorded on spans with error status. When an operation fails: 1. The exception is recorded on the span via `span.record_exception(e)` 2. The span status is set to error with the exception message 3. The exception is re-raised to the caller ### Disabling Instrumentation To disable instrumentation at runtime (e.g., in tests or specific environments): ```ruby Dalli::Instrumentation.disable! ``` You can also assign a custom tracer directly: ```ruby Dalli::Instrumentation.tracer = my_custom_tracer ``` ### Zero Overhead When OpenTelemetry is not present, there is zero overhead - the tracing code checks once at startup and bypasses all instrumentation logic entirely when the SDK is not loaded. ![Persistence of Memory](https://upload.wikimedia.org/wikipedia/en/d/dd/The_Persistence_of_Memory.jpg) ## Documentation and Information * [User Documentation](https://github.com/petergoldstein/dalli/wiki) - The documentation is maintained in the repository's wiki. * [Announcements](https://github.com/petergoldstein/dalli/discussions/categories/announcements) - Announcements of interest to the Dalli community will be posted here. * [Bug Reports](https://github.com/petergoldstein/dalli/issues) - If you discover a problem with Dalli, please submit a bug report in the tracker. * [Forum](https://github.com/petergoldstein/dalli/discussions/categories/q-a) - If you have questions about Dalli, please post them here. * [Client API](https://www.rubydoc.info/gems/dalli) - Ruby documentation for the `Dalli::Client` API ## Development After checking out the repo, run `bin/setup` to install dependencies. You can run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. ## Contributing Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on how to contribute, including our policy on AI-authored contributions. ## Appreciation Dalli would not exist in its current form without the contributions of many people. But special thanks go to several individuals and organizations: * Mike Perham - for originally authoring the Dalli project and serving as maintainer and primary contributor for many years * Eric Wong - for help using his [kgio](http://bogomips.org/kgio/) library. * Brian Mitchell - for his remix-stash project which was helpful when implementing and testing the binary protocol support. * [CouchBase](http://couchbase.com) - for their sponsorship of the original development ## Authors * [Peter M. Goldstein](https://github.com/petergoldstein) - current maintainer * [Mike Perham](https://github.com/mperham) and contributors ## Copyright Copyright (c) Mike Perham, Peter M. Goldstein. See LICENSE for details.