Project

mudis

0.0
The project is in a healthy, maintained state
Thread-safe, bucketed, in-process cache for Ruby apps. Drop-in replacement for Kredis in some scenarios.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 0
 Project Readme

mudis_signet

Gem Version

Mudis is a fast, thread-safe, in-memory, sharded LRU (Least Recently Used) cache for Ruby applications. Inspired by Redis, it provides value serialization, optional compression, per-key expiry, and metric tracking in a lightweight, dependency-free package that lives inside your Ruby process.

It’s ideal for scenarios where performance and process-local caching are critical, and where a full Redis setup is overkill or otherwise not possible.

Alternatively, Mudis can be upscaled with higher sharding and resources in a dedicated rails app to provide a Mudis server.


Design

Write - Read - Eviction

mudis_flow

Cache Key Lifecycle

mudis_lru


Features

  • Thread-safe: Uses per-bucket mutexes for high concurrency.
  • Sharded: Buckets data across multiple internal stores to minimize lock contention.
  • LRU Eviction: Automatically evicts least recently used items as memory fills up.
  • Expiry Support: Optional TTL per key with background cleanup thread.
  • Compression: Optional Zlib compression for large values.
  • Metrics: Tracks hits, misses, and evictions.

Installation

Add this line to your Gemfile:

gem 'mudis'

Or install it manually:

gem install mudis

Configuration (Rails)

In your Rails app, create an initializer:

# config/initializers/mudis.rb

Mudis.serializer = JSON        # or Marshal | Oj
Mudis.compress = true          # Compress values using Zlib
Mudis.max_value_bytes = 2_000_000  # Reject values > 2MB
Mudis.start_expiry_thread(interval: 60) # Cleanup every 60s

at_exit do
  Mudis.stop_expiry_thread
end

If your lib/ folder isn't eager loaded, explicitly require 'mudis' in this file.


Basic Usage

require 'mudis'

# Write a value with optional TTL
Mudis.write('user:123', { name: 'Alice' }, expires_in: 600)

# Read it back
Mudis.read('user:123') # => { "name" => "Alice" }

# Check if it exists
Mudis.exists?('user:123') # => true

# Atomically update
Mudis.update('user:123') { |data| data.merge(age: 30) }

# Delete a key
Mudis.delete('user:123')

Rails Service Integration

For simplified or transient use in a controller, you can wrap your cache logic in a reusable thin class (TODO: add more useful abstraction once DSL and namespacing is introduced):

class MudisService
  attr_reader :cache_key

  def initialize(cache_key)
    @cache_key = cache_key
  end

  def write(data, expires_in: nil)
    Mudis.write(cache_key, data, expires_in: expires_in)
  end

  def read(default: nil)
    Mudis.read(cache_key) || default
  end

  def update
    Mudis.update(cache_key) { |current| yield(current) }
  end

  def delete
    Mudis.delete(cache_key)
  end

  def exists?
    Mudis.exists?(cache_key)
  end
end

Use it like:

cache = MudisService.new("user:#{current_user.id}")
cache.write({ preferences: "dark" }, expires_in: 3600)
cache.read # => { "preferences" => "dark" }

Metrics

Track cache effectiveness and performance:

Mudis.metrics
# => { hits: 15, misses: 5, evictions: 3 }

Optionally, return these metrics from a controller for remote analysis and monitoring if using rails.

class MudisController < ApplicationController

  def metrics
    render json: {
      mudis_metrics: Mudis.metrics,
      memory_used_bytes: Mudis.current_memory_bytes,
      memory_max_bytes: Mudis.max_memory_bytes,
      keys: Mudis.all_keys.size
    }
  end

end

Advanced Configuration

Setting Description Default
Mudis.serializer JSON, Marshal, or Oj JSON
Mudis.compress Enable Zlib compression false
Mudis.max_value_bytes Max allowed size in bytes for a value nil (no limit)
Mudis.buckets Number of cache shards (via ENV var) 32
start_expiry_thread Background TTL cleanup loop (every N sec) Disabled by default

To customize the number of buckets, set the MUDIS_BUCKETS environment variable.


Graceful Shutdown

Don’t forget to stop the expiry thread when your app exits:

at_exit { Mudis.stop_expiry_thread }

Known Limitations

  • Data is process-local and non-persistent (doesn't survive a shutdown).
  • Not suitable for cross-process or cross-language use.
  • Keys are globally scoped (no namespacing by default).
  • Compression introduces CPU overhead.

Roadmap

  • Namespaced cache keys
  • Stats per bucket
  • Optional max memory cap per bucket
  • Built-in fetch/read-or-write DSL

License

MIT License © kiebor81


Contributing

PRs are welcome! To get started:

git clone https://github.com/kiebor81/mudis
cd mudis
bundle install

Contact

For issues, suggestions, or feedback, please open a GitHub issue