Packages

A simple, fast ETS-based cache with timed expiry for Erlang and Elixir. Provides both basic put/get operations and serialized fetch operations to minimize thundering herd problems when caching expensive computations.

Current section

Files

Jump to
kiss_cache CHANGELOG.md
Raw

CHANGELOG.md

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.0] - 2026-08-03
### Added
- An `ExpiryMs` of `0` or less passed to `fetch/{5,6,8}` or `fetch_noserialize/8`
now bypasses the cache: the underlying function is called directly, nothing is
read from or written to the cache, and no fetcher process is involved. This lets
a call site keep a single `fetch` call and turn caching off by configuration
(for example a TTL of 0 in the test environment).
- `invalidate_fetch/{4,5,6,8}`, which drops the cached result for one fetch so
that the next fetch for the same `M:F(A)` calls through again. Takes the same
arguments as the corresponding `fetch`, so an invalidation call site is a copy
of the fetch call site with the function name changed.
### Fixed
- All `fetch` arities now cache the same `M:F(A)` under the same key. The default
`ApplyFunc` was written as `fun apply/3`, a module-local fun whose value differs
per source occurrence, so `fetch/4`, `fetch/5` and `fetch/6` each cached the same
call under a key of their own. The default is now `fun erlang:apply/3`.
The blast radius was small: the key was consistent *per arity*, so the common
pattern of always reaching a given `M:F(A)` through one `fetch` arity cached and
hit exactly as expected. What was broken is calling the same `M:F(A)` through
more than one arity, which then kept a separate entry (and made a separate
upstream call) per arity, and constructing the key of a default fetch from
outside the module in order to delete it — which is what `invalidate_fetch`
needs, and why this had to be fixed first.
Callers that pass `ApplyFunc` explicitly are unaffected, and callers that passed
`fun erlang:apply/3` (`&:erlang.apply/3` in Elixir) now share keys with the
shorter fetch arities.
The new default changes the cache keys of existing `fetch/{4,5,6}` entries. This
only matters if you hot code reload into a running node, where entries written
before the upgrade stop being read and simply expire; restarting the node empties
the cache anyway, since it lives in ETS.
- The published package now contains `src/kiss_cache.app.src` and `rebar.config`,
so rebar3 can build it as a dependency. The 1.0.0 package was built by mix only
and shipped no app file, which rebar3 requires, despite the README documenting
rebar3 installation.
## [1.0.0] - 2025-10-18
### Added
- First open source release of kiss_cache
- Simple ETS-based cache with timed expiry
- Basic put/get/delete operations with minimal overhead
- Lazy cleanup without background processes
- Serialized fetch operations to prevent thundering herd problems
- Zero external dependencies (uses only Erlang/OTP stdlib)
- Comprehensive documentation and examples
- Unit tests
- MIT License
### Notes
- This library has been used in production at CrankWheel since 2021
- Battle tested in high-traffic, high-contention scenarios
- Now available as an open source package on Hex.pm