Chisle
The coding-agent plugin that bills 52% of a bare model across 20 live tasks. Terse persona + tool-output compressor + context diet.
翻訳待ち
High performance memcached client for Ruby
翻訳待ち
開発ツールRuby · ShellGitHub @petergoldstein自分でインストール所有者未確認
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Dalli is a high performance pure Ruby client for accessing memcached servers.
Dalli supports:
The name is a variant of Salvador Dali for his famous painting The Persistence of Memory.
Dalli is tested against both the minimum supported memcached version and the
latest release. Earlier 1.6.x servers are not supported: the meta protocol
rejects unknown flags outright, so features added after a server's release fail
with CLIENT_ERROR invalid flag rather than degrading.
Dalli sets socket timeouts with IO#timeout. Ruby 3.3.0–3.3.7 and 3.4.0–3.4.2 have a bug (Ruby #21195) where a timed read interrupted by a signal aborts the whole process:
[BUG] rb_sys_fail_path_in(io_fillbuf, fd:N ) - errno == 0
The crash is inside Ruby itself, so it can't be rescued. It's fixed in Ruby 3.3.8 and 3.4.3. If you see this crash, upgrade to one of those versions or later.
Use namespaces to partition your cache and avoid key collisions between different applications or environments:
# 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]}" })
By default, the namespace and key are joined with a colon (:). You can customize this with the namespace_separator option:
# 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: :, /, |, ., -, _, #
memcached rejects items larger than its -I setting (1MB by default). Set value_max_bytes to the same value if you change it:
# memcached started with -I 2m
Dalli::Client.new('localhost:11211', value_max_bytes: 2 * 1024 * 1024)
memcached's limit covers the whole item, so Dalli checks the stored (serialized and compressed) value plus the key plus 63 bytes of per-item overhead against value_max_bytes, and raises Dalli::ValueOverMaxSize before sending anything larger. With the defaults, the largest value you can store under a 10-byte key is 1,048,576 - 10 - 63 = 1,048,503 bytes.
By default, a quiet (or multi) block ends by waiting for the replies memcached still sends for its quiet requests, one round trip for each server the block wrote to. With defer_drain: true, the block returns without waiting:
dc = Dalli::Client.new('localhost:11211', defer_drain: true)
dc.quiet { dc.set('key', 'value') } # returns without a round trip
dc.get('key') # first drains that connection with a noop, then gets the value
The requests are still sent right away. Their replies stay unread on the connection until the next non-quiet request to that server, which reads them first with one noop. Error replies to quiet requests are discarded either way. With defer_drain they're discarded at that later point instead of at the end of the block.
Replies accumulate until a non-quiet request drains them. memcached still replies to some quiet requests: NF for a quiet delete, incr or decr of a missing key, NS for an add or replace that didn't store, and error replies. Those replies wait on the socket. A client that mixes quiet writes with reads (or other non-quiet requests) to the same servers drains them as it goes, and needs nothing more.
A client that only ever makes quiet requests, such as a worker that only issues quiet deletes, never sends that next request. Its unread replies keep growing, and if they fill the connection's socket buffers, the connection can stall until a socket timeout closes it and Dalli reconnects. Workloads like that should call drain_deferred_responses periodically, for example after each batch or job:
jobs.each do |job|
job.keys.each { |key| dc.quiet { dc.delete(key) } }
dc.drain_deferred_responses # reads and discards the replies waiting on each server
end
drain_deferred_responses only sends a noop to servers that were sent quiet requests since they were last drained, so calling it when nothing is pending costs nothing.
When a request fails with a network error (a refused connection, a reset, or a read that times out), Dalli reconnects and retries it. A server that fails socket_max_failures times in a row is marked down, and requests to it raise Dalli::NetworkError (or, with failover, go to another server).
On CRuby, a read that waits longer than socket_timeout raises a network error, which is retried and counted like any other. JRuby and TruffleRuby don't time out a blocked socket read, so on those engines a server that accepts a request and never replies blocks the caller indefinitely. Pass enforce_read_timeout: true to apply socket_timeout there too:
Dalli::Client.new('localhost:11211', socket_timeout: 0.5, enforce_read_timeout: true)
This is the beginning of the README. Read the rest at the original. Read the full README ↗