{"id":13483164,"url":"https://github.com/sorentwo/readthis","last_synced_at":"2025-05-15T15:07:10.452Z","repository":{"id":23650613,"uuid":"27021112","full_name":"sorentwo/readthis","owner":"sorentwo","description":":newspaper: Pooled active support compliant caching with redis","archived":false,"fork":false,"pushed_at":"2019-09-30T17:09:51.000Z","size":276,"stargazers_count":503,"open_issues_count":0,"forks_count":39,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-05-15T03:55:29.118Z","etag":null,"topics":["caching","redis","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/sorentwo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-23T03:45:07.000Z","updated_at":"2025-02-08T12:40:58.000Z","dependencies_parsed_at":"2022-08-21T00:50:30.172Z","dependency_job_id":null,"html_url":"https://github.com/sorentwo/readthis","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorentwo%2Freadthis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorentwo%2Freadthis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorentwo%2Freadthis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorentwo%2Freadthis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sorentwo","download_url":"https://codeload.github.com/sorentwo/readthis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"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","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":["caching","redis","ruby"],"created_at":"2024-07-31T17:01:08.733Z","updated_at":"2025-05-15T15:07:05.445Z","avatar_url":"https://github.com/sorentwo.png","language":"Ruby","readme":"[![Gem Version](https://badge.fury.io/rb/readthis.svg)](http://badge.fury.io/rb/readthis)\n[![Build Status](https://travis-ci.org/sorentwo/readthis.svg?branch=master)](https://travis-ci.org/sorentwo/readthis)\n[![Code Climate](https://codeclimate.com/github/sorentwo/readthis/badges/gpa.svg)](https://codeclimate.com/github/sorentwo/readthis)\n[![Coverage Status](https://coveralls.io/repos/sorentwo/readthis/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/sorentwo/readthis?branch=master)\n[![Inline Docs](http://inch-ci.org/github/sorentwo/readthis.svg?branch=master)](http://inch-ci.org/github/sorentwo/readthis)\n\n# Readthis\n\nReadthis is a Redis backed cache client for Ruby. It is a drop in replacement\nfor any `ActiveSupport` compliant cache and can also be used for [session\nstorage](#session-storage). Above all Readthis emphasizes performance,\nsimplicity, and explicitness.\n\nFor new projects there isn't any reason to stick with Memcached. Redis is as\nfast, if not faster in many scenarios, and is far more likely to be used\nelsewhere in the stack. See [this blog post][hp-caching] for more details.\n\n[hp-caching]: http://sorentwo.com/2015/07/20/high-performance-caching-with-readthis.html\n\n## Rails 5.2+\n\nRails 5.2 and beyond has a [Redis Cache][rc] built in. The built in Redis cache\nsupports many of the same features as Readthis, as well as multi-tier caches and\nnewer additions like cache key recycling.\n\n_Readthis is maintained for versions of Rails prior to 5.2 and new features will\nnot be supported. If you are using Rails 5.2+ you should migrate to the built in\nRedis cache._\n\n[rc]: https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html\n\n## Footprint \u0026 Performance\n\nSee [Performance](PERFORMANCE.md)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'readthis'\ngem 'hiredis' # Highly recommended\n```\n\n## Usage\n\nUse it the same way as any other [ActiveSupport::Cache::Store][store]. Within a\nRails environment config:\n\n```ruby\nconfig.cache_store = :readthis_store, {\n  expires_in: 2.weeks.to_i,\n  namespace: 'cache',\n  redis: { url: ENV.fetch('REDIS_URL'), driver: :hiredis }\n}\n```\n\nOtherwise you can use it anywhere, without any reliance on `ActiveSupport`:\n\n```ruby\nrequire 'readthis'\n\ncache = Readthis::Cache.new(\n  expires_in: 2.weeks.to_i,\n  redis: { url: ENV.fetch('REDIS_URL') }\n)\n```\n\nYou can also specify `host`, `port`, `db` or any other valid Redis options. For\nmore details about connection options see in [redis gem documentation][redisrb]\n\n[store]: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html\n[redisrb]: https://github.com/redis/redis-rb#getting-started\n\n### Instances \u0026 Databases\n\nAn isolated Redis instance that is only used for caching is ideal. Dedicated\ninstances have numerous benefits like: more predictable performance, avoiding\nexpires in favor of LRU, and tuning the persistence mechanism. See [Optimizing\nRedis Usage for Caching][optimizing-usage] for more details.\n\nAt the very least, you'll want to use a specific database for caching. In the\nevent the database needs to be purged you can do so with a single `clear`\ncommand, rather than finding all keys in a namespace and deleting them.\nAppending a number between 0 and 15 will specify the redis database, which\ndefaults to `0`. For example, using database `2`:\n\n```bash\nREDIS_URL=redis://localhost:6379/2\n```\n\n[optimizing-usage]: http://sorentwo.com/2015/07/27/optimizing-redis-usage-for-caching.html\n\n### Expiration\n\nBe sure to use an integer value when setting expiration time. The default\nrepresentation of `ActiveSupport::Duration` values won't work when setting\nexpiration time, which will cause all keys to have `-1` as the TTL. Expiration\nvalues are always cast as an integer on write. For example:\n\n```ruby\nReadthis::Cache.new(expires_in: 1.week) # don't do this\nReadthis::Cache.new(expires_in: 1.week.to_i) # do this\n```\n\nBy using the `refresh` option the TTL for keys can be refreshed automatically\nevery time the key is read. This is helpful for ensuring commonly hit keys are\nkept cached, effectively making the cache a hybrid LRU.\n\n```ruby\nReadthis::Cache.new(refresh: true)\n```\n\nBe aware that `refresh` adds a slight overhead to all read operations, as they\nare now all write operations as well.\n\n### Compression\n\nCompression can be enabled for all actions by passing the `compress` flag. By\ndefault all values greater than 1024k will be compressed automatically. If there\nis any content has not been stored with compression, or perhaps was compressed\nbut is beneath the compression threshold, it will be passed through as is. This\nmeans it is safe to enable or change compression with an existing cache. There\nwill be a decoding performance penalty in this case, but it should be minor.\n\n```ruby\nconfig.cache_store = :readthis_store, {\n  compress: true,\n  compression_threshold: 2.kilobytes\n}\n```\n\n### Serializing\n\nReadthis uses Ruby's `Marshal` module for serializing all values by default.\nThis isn't always the fastest option, and depending on your use case it may be\ndesirable to use a faster but less flexible serializer.\n\nBy default Readthis knows about 3 different serializers:\n\n* Marshal\n* JSON\n* Passthrough\n\nIf all cached data can safely be represented as a string then use the\npass-through serializer:\n\n```ruby\nReadthis::Cache.new(marshal: Readthis::Passthrough)\n```\n\nYou can introduce up to four additional serializers by configuring `serializers`\non the Readthis module. For example, if you wanted to use the extremely fast Oj\nlibrary for JSON serialization:\n\n```ruby\nReadthis.serializers \u003c\u003c Oj\n\n# Freeze the serializers to ensure they aren't changed at runtime.\nReadthis.serializers.freeze!\n\nReadthis::Cache.new(marshal: Oj)\n```\n\nBe aware that the *order in which you add serializers matters*. Serializers are\nsticky and a flag is stored with each cached value. If you subsequently go to\ndeserialize values and haven't configured the same serializers in the same order\nyour application will raise errors.\n\n## Fault Tolerance\n\nIn some situations it is desirable to keep serving requests from disk or the\ndatabase if Redis crashes. This can be achieved with connection fault tolerance\nby enabling it at the top level:\n\n```ruby\nReadthis.fault_tolerant = true\n```\n\nThe default value is `false`, because while it may work for `fetch` operations,\nit isn't compatible with other state-based commands like `increment`.\n\n## Running Arbitrary Redis Commands\n\nReadthis provides access to the underlying Redis connection pool, allowing you\nto run arbitrary commands directly through the cache instance. For example, if\nyou wanted to expire a key manually using an instance of `Rails.cache`:\n\n```ruby\nRails.cache.pool.with { |client| client.expire('foo-key', 60) }\n```\n\n## Differences From ActiveSupport::Cache\n\nReadthis supports all of standard cache methods except for the following:\n\n* `cleanup` - Redis does this with TTL or LRU already.\n* `mute` and `silence!` - You must subscribe to the events `/cache*.active_support/`\n  with `ActiveSupport::Notifications` to [log cache calls manually][notifications].\n\n[notifications]: https://github.com/sorentwo/readthis/issues/22#issuecomment-142595938\n\nLike other `ActiveSupport::Cache` implementations it is possible to cache `nil`\nas a value. However, the fetch methods treat `nil` values as a cache miss and\nre-generate/re-cache the value. Caching `nil` isn't recommended.\n\n## Session Storage\n\nBy using [ActionDispatch::Session::CacheStore][cache-store] it's possible to\nreuse `:readthis_store` or specify a new Readthis cache store for storing\nsessions.\n\n```ruby\nRails.application.config.session_store :cache_store\n```\n\nTo specify a separate Readthis instance you can use the `:cache` option:\n\n```ruby\nRails.application.config.session_store :cache_store,\n  cache: Readthis::Cache.new,\n  expire_after: 2.weeks.to_i\n```\n\n[cache-store]: http://api.rubyonrails.org/classes/ActionDispatch/Session/CacheStore.html\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","funding_links":[],"categories":["Ruby","Caching"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorentwo%2Freadthis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsorentwo%2Freadthis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorentwo%2Freadthis/lists"}