An in-memory string-interface{} map with various expiration options for golang


TTLCache - an in-memory cache with expiration

Documentation Release

TTLCache is a simple key/value cache in golang with the following functions:

  1. Expiration of items based on time, or custom function
  2. Loader function to retrieve missing keys can be provided. Additional Get calls on the same key block while fetching is in progress (groupcache style).
  3. Individual expiring time or global expiring time, you can choose
  4. Auto-Extending expiration on Get -or- DNS style TTL, see SkipTTLExtensionOnHit(bool)
  5. Can trigger callback on key expiration
  6. Cleanup resources by calling Close() at end of lifecycle.
  7. Thread-safe with comprehensive testing suite. This code is in production at on critical systems.

Note (issue #25): by default, due to historic reasons, the TTL will be reset on each cache hit and you need to explicitly configure the cache to use a TTL that will not get extended.

Build Status Go Report Card Coverage Status GitHub issues license


You can copy it as a full standalone demo program.

package main

import (


var (
	notFound = ttlcache.ErrNotFound
	isClosed = ttlcache.ErrClosed

func main() {
	newItemCallback := func(key string, value interface{}) {
		fmt.Printf("New key(%s) added\n", key)
	checkExpirationCallback := func(key string, value interface{}) bool {
		if key == "key1" {
			// if the key equals "key1", the value
			// will not be allowed to expire
			return false
		// all other values are allowed to expire
		return true
	expirationCallback := func(key string, value interface{}) {
		fmt.Printf("This key(%s) has expired\n", key)

	loaderFunction := func(key string) (data interface{}, ttl time.Duration, err error) {
		ttl = time.Second * 300
		data, err = getFromNetwork(key)

		return data, ttl, err

	cache := ttlcache.NewCache()
	defer cache.Close()
	cache.SetTTL(time.Duration(10 * time.Second))

	cache.Set("key", "value")
	cache.SetWithTTL("keyWithTTL", "value", 10*time.Second)

	if value, exists := cache.Get("key"); exists == nil {
		fmt.Printf("Got value: %v\n", value)
	count := cache.Count()
	if result := cache.Remove("keyNNN"); result == notFound {
		fmt.Printf("Not found, %d items left\n", count)

func getFromNetwork(key string) (string, error) {
	time.Sleep(time.Millisecond * 30)
	return "value", nil

TTLCache - Some design considerations

  1. The complexity of the current cache is already quite high. Therefore not all requests can be implemented in a straight-forward manner.
  2. The locking should be done only in the exported functions and startExpirationProcessing of the Cache struct. Else data races can occur or recursive locks are needed, which are both unwanted.
  3. I prefer correct functionality over fast tests. It's ok for new tests to take seconds to proof something.

Original Project

TTLCache was forked from wunderlist/ttlcache to add extra functions not avaiable in the original scope. The main differences are:

  1. A item can store any kind of object, previously, only strings could be saved
  2. Optionally, you can add callbacks too: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache
  3. The expiration can be either global or per item
  4. Items can exist without expiration time (time.Zero)
  5. Expirations and callbacks are realtime. Don't have a pooling time to check anymore, now it's done with a heap.
  6. A cache count limiter
  • Limit on no. of keys stored in ttlcache?

    Limit on no. of keys stored in ttlcache?

    is there a limit on no. of keys that can be stored using ttlcache library? In our service we recently faced increase in response time when ttlcache was being used.

    opened by raunak-kondiboyina 12
  • Returning loader errors

    Returning loader errors

    With new version v3 there is currently no way to return loader (either default or getter) error as Get returns only item and there is no way to propagate errors back to caller.

    opened by lafriks 11
  • Upgrade to v3

    Upgrade to v3

    Before the stable release of v3, some additional, non-backwards compatible changes could be made. Here is a list of my proposed changes:

    • [x] Update
    • [x] Allow TTL extension to be enabled/disabled per Get() call, rather than per cache instance. Having them both could also work: the global cache value would be used as the default one.
    • [x] Use functional options to set cache options.
    • [x] Remove the SimpleCache interface, since it serves no purpose, at least in this package. The idiomatic approach is to allow the client/user of the package to create their own interface, with their own limited set of methods. However, if there are clear and useful examples of where this is may be needed, this interface should be at least renamed to something more appropriate.
    • [x] Rename the event methods and make them follow On[Noun](fn) naming pattern (e.g. OnExpiration(fn)). The provided functions should also be stored in a slice rather than a simple field. A good example of this is bolt's OnCommit.
    • [x] Separate expiration queue and LRU item list.
    • [x] Rename existing methods.
    • [x] Remove evictionreason_enumer.go since it seems to add very little value yet creates quite a bit of unidiomatic code noise.
    • [x] Clean up some of the code/add proper docs.
    • [x] Add Loader interface and its helper types/funcs.
    • [x] Improve mutex usage.
    • [x] Make auto cleaner process optional. It should also be possible to stop and then restart it without closing the cache instance.
    • [x] Remove Close(), replace it with Start() and Stop()
    • [x] Remove error return values (most of them return only ErrClosed).
    • [x] Add a method for manual expired item deletion.
    • [x] Return/pass the whole Item type rather than its fields (applies to Get/Set/event functions).
    • [x] Rewrite/improve tests.

    I may have some more ideas later on.

    What do you think @ReneKroon?

    opened by swithek 9
  • call expirationCallback automatically on cache.Close()

    call expirationCallback automatically on cache.Close()

    Is it possible to call expirationCallback on each remaining keys in the cache, when calling cache.Close()?

    The purpose is to keep the overall expirationCallback behavior the same?

    opened by LihuaWu 8
  • func (cache *Cache) startExpirationProcessing() - Never Exits and Leaves residual Goroutines.

    func (cache *Cache) startExpirationProcessing() - Never Exits and Leaves residual Goroutines.

    Hey, I noticed there is no exit case in your Goroutine here:

    I was developing an application that start multiple caches as child-struct. But when the parent is removed this function - func (cache *Cache) startExpirationProcessing() - keeps running forever.

    Do you perhaps have a way of killing it somewhere in your code ? I like your package and I would love to keep using it, but in the current state I can not :(

    opened by zveinn 8
  • add GetItems method

    add GetItems method

    Hi! @ReneKroon ! Awesome cache! Want to switch to yours from as yours has a size limit.

    Can you accept a small PR which adds the GetItems method?

    opened by DoubleDi 7
  • Loaderfunction key not found

    Loaderfunction key not found

    Fixes #44

    I ran into some trouble where TestCache_Limit didn't work for me locally but that was the same before and after the change (length was 10 but keys were incorrect - this might be due to shitty windows timers).

    Let me know any thoughts.

    opened by jspri 7
  • Use type parameters

    Use type parameters

    This PR adds type parameters to all the relevant types and functions. The logic of the functions and their tests has not been changed.

    Other changes worth mentioning:

    • The top-level documentation comments have been reformatted (mainly missing periods have been added). The content of those comments has not been changed.
    • For the sake of consistency, some fields/variables have been renamed from 'data' to 'value'.
    • The go version specified in go.mod has been changed to 1.18.
    • has been renamed to

    Closes: #63

    opened by swithek 5
  • go1.18 and generics

    go1.18 and generics

    Are there any plans to upgrade the project to go1.18 (when it's released) and replace interface{} types with generic types?

    I can open a draft PR with changes made using gotip in the near future if this is something you'd be interested in @ReneKroon.

    opened by swithek 5
  • add customer loader function for each Get()

    add customer loader function for each Get()

    Use case: Web Server cache middleware.

    The middleware can hook the requests and cache the responses.


    func NewPathQueryCache(cache *ttlcache.Cache) fiber.Handler {
    	return func(c *fiber.Ctx) error {
    		path := c.Path()
    		query := c.Request().URI().QueryString()
    		key := path + "|" + string(query)
    		r, err := cache.Get(key, func(string) (data interface{}, ttl time.Duration, err error) {
    			begin := time.Now()
    			err = c.Next()
    			if err != nil {
    			ttl = time.Since(begin) * 120
    			resp := c.Response()
    			data = &cacheEntry{
    				code: resp.StatusCode(),
    				body: resp.Body(),
    		if err != nil {
    			return errors.Wrap(err, "cache get failed")
    		result, ok := r.(*cacheEntry)
    		if !ok {
    			return errors.Wrap(err, "cache cast failed")
    		return c.Status(result.code).Send(result.body)

    Thank you for a good library.

    opened by witchu 5
  • Feature request: ability to know why an expiry has occurred

    Feature request: ability to know why an expiry has occurred

    Currently if you register an expiry callback, that callback gets called in one of 3 different scenarios:

    1. The key expires due to the TTL
    2. The key is manually removed via a call to Remove
    3. The cache is closed via Close

    However, you have no way of knowing from within the expiry callback which scenario has occurred.

    For context, I have a scenario where I am maintaining a list of items that can be added and removed by users, and if they are removed by users, the cleanup process is already handled as part of handling the users request. However, items need to expire as well and in that case, the users need to be informed that their items have been removed automatically. In the current implementation, if a user removes their own items, they also get informed their item was removed automatically due to the expiration callback firing.

    My proposal is for the expiration callback to take an additional argument reason which can be checked against to see if it was triggered by a Remove, Close or TTL expiry, which then gives me the option of handling it differently for each scenario. Alternatively, having a flag much like the SkipTTLExtensionOnHit such as ExpireOnTTLOnly to allow me to strictly support callbacks firing only expirations due to actual expiry rather than removal from the cache would be useful.

    opened by darkliquid 5
  • feat(disable_overwrite_on_set): Add ability to disable overwrites when setting an item in the cache

    feat(disable_overwrite_on_set): Add ability to disable overwrites when setting an item in the cache

    Add ability to disable overwrites when setting an item in the cache

    • Added new option & associated test
    • Added functionality in cache.set() to return the item if overwrite is disabled. Could be done as an else.. but wanted to make explicit up front at top of func.

    Not sure if more tests need additional testing. I didn't a chance to review all existing cases / structure of testing framework

    Addresses issue / enhancement request

    opened by patpicos 2
  • Enhancement Req - Add option to the cache to prevent updates when using Set()

    Enhancement Req - Add option to the cache to prevent updates when using Set()

    Use case: I use the cache to capture data i receive over UDP. In some cases, i have competing producers that will send the same information for a Key. I want the first message to win and be the one persisted in the cache and not have further messages overwrite.

    Looking at the cache.set() function, it searches for an existing element and overwrites it.

    I would like a new option for the cache set on creation to prevent overwrites

    cache := ttlcache.New[string, string](
    		ttlcache.WithTTL[string, string](30 * time.Minute),
    //Pseudo-code to show the expectation
    cache.Set("first", "value1", ttlcache.DefaultTTL)
    cache.Set("first", "value2", ttlcache.DefaultTTL)
    cache.Set("first", "value3", ttlcache.DefaultTTL)
    v:= cache.Get("first").Value()
    assert.Equal(t, "value1",v)
    opened by patpicos 3
  • Add Version method to item type

    Add Version method to item type

    It should return a uint64 that determines how many times an item has been updated. This method may be useful in situations when a task's execution depends on whether the item has changed or not and when due to the duration of another task a transaction/mutex isn't acceptible.

    item := cache.Get("key")
    version := item.Version()
    // long task
    if item.Version() == version {
         // execute only if the item hasn't been updated during the long task's execution
    opened by swithek 1
  • Add a method that activates transaction mode

    Add a method that activates transaction mode

    It would be nice to have a method that would accept a function and execute it with a new global transaction mutex locked:

    func (c *Cache[K, V]) Tx(fn func(*Tx[K, V])) {
             tx := c.beginTx()

    This was addressed in this issue, however, due to some limitations of the previous versions it was closed.

    opened by swithek 0
  • Add Range/ForEach method

    Add Range/ForEach method

    It should iterate over all cache items and pass each of them into a func that is provided as a parameter:

    func (c *Cache[K, V]) Range(fn func(*Item[K, V]) bool) {
         // ...

    The bool return value should indicate whether the iteration should continue or be stopped.

    opened by swithek 2
  • PurgeCallback to be called on Purge()

    PurgeCallback to be called on Purge()

    I needed a callback to be called on Purge. If I just wrapped it in an internal package, I'd invent a possible race condition:

    thread1 purges 
    thread2 adds
    add callback
    purge callback

    Thus this PR.

    opened by fxff 5
  • v2.11.1(Mar 24, 2022)

  • v3.0.0(Mar 24, 2022)

    The version 3 of ttlcache includes many breaking changes, some of which improve the general usability and readability of the code, while others extend the current API with new Go features (mainly type parameters). The most notable of these additions/changes are:

    • The new Item type that is returned by both Get and Set methods.
    • The deletion of the Close() method.
    • The rename of the Purge() method to DeleteAll().
    • The rewritten New function that now accepts functional options. It also doesn't start the automatic expired item deletion process: the Start() method needs to be explicitly called for it to be activated.
    • Improved event/callback registration methods.
    • More predictable item deletion when the cache's capacity is reached.
    • The new Loader interface.

    All new or updated types and functions can be found on the documentation, README, or v3 issue page.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-beta(Dec 18, 2021)

    As contributed in #66

    This is a test release to change the cache into one that works with generics, allowing for a templated key value structure.

    Source code(tar.gz)
    Source code(zip)
  • v2.11.0(Dec 18, 2021)

    2.11.0 (December 2021) #64: @DoubleDi added a method GetItems to retrieve all items in the cache. This method also triggers all callbacks associated with a normal Get

    API changes:

    // GetItems returns a copy of all items in the cache. Returns nil when the cache has been closed. 
    func (cache *Cache) GetItems() map[string]interface{} {..}
    Source code(tar.gz)
    Source code(zip)
  • v2.10.0(Dec 6, 2021)

    2.10.0 (December 2021)

    #62 : @nikhilk1701 found a memory leak where removed items are not directly eligible for garbage collection. There are no API changes.

    Source code(tar.gz)
    Source code(zip)
  • v2.9.0(Oct 17, 2021)

    2.9.0 (October 2021)

    #55,#56,#57 : @chenyahui was on fire and greatly improved the peformance of the library. He also got rid of the blocking call to expirationNotification, making the code run twice as fast in the benchmarks!

    Source code(tar.gz)
    Source code(zip)
  • v2.8.1(Sep 17, 2021)

  • v2.8.0(Aug 19, 2021)

    API Change

    • The call GetWithTTL(key string) (interface{}, time.Duration, error) is added so that you can retrieve an item, and also know the remaining TTL. Thanks to @asgarciap for contributing.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.0(Jun 3, 2021)

    #46 : got panic

    A panic occured in a line that checks the maximum amount of items in the cache. While not definite root cause has been found, there is indeed the possibility of crashing an empty cache if the cache limit is set to 'zero' which codes for infinite. This would lead to removal of the first item in the cache which would panic on an empty cache.

    Fixed this by applying the global cache lock to all configuration options as well.

    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(May 26, 2021)

    #44 : There are no API changes, but a contribution was made to use as a way to provide everybody waiting for a key with that result when it is fetched. Thanks go out to @jspri

    This removes some complexity from the code and will make sure that all callers will get a return value even if there's high concurrency and low TTL (as proven by the test that was added).

    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(May 22, 2021)

    API changes:

    • #39 : Allow custom loader function for each key via GetByLoader

    Introduce the SimpleCache interface for quick-start and basic usage.

    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Apr 11, 2021)

    2.4.0 - Add some item manipulation

    #42 : Add option to get list of keys #40: Allow 'Touch' on items without other operation

    // Touch resets the TTL of the key when it exists, returns ErrNotFound if the key is not present.
    func (cache *Cache) Touch(key string) error 
    // GetKeys returns all keys of items in the cache. Returns nil when the cache has been closed.
    func (cache *Cache) GetKeys() []string 
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Feb 4, 2021)

    2.3.0 (February 2021)

    API changes:

    • #38: Added func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) This wil function will replace SetExpirationCallback(..) in the next major version.
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Jan 19, 2021)

    2.2.0 (January 2021)

    API changes: #37 : a GetMetrics call is now available for some information on hits/misses etc.

    #34 : Errors are now const

    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Oct 13, 2020)

  • v2.0.0(Jul 28, 2020)

    2.0.0 (July 2020)

    Fixes #29, #30, #31

    Behavioural changes

    • Remove(key) now also calls the expiration callback when it's set
    • Count() returns zero when the cache is closed

    API changes

    • SetLoaderFunction allows you to provide a function to retrieve data on missing cache keys.
    • Operations that affect item behaviour such as Close, Set, SetWithTTL, Get, Remove, Purge now return an error with standard errors ErrClosed an ErrNotFound instead of a bool or nothing
    • SkipTTLExtensionOnHit replaces SkipTtlExtensionOnHit to satisfy golint
    • The callback types are now exported
    Source code(tar.gz)
    Source code(zip)
  • v1.7.0(Jul 22, 2020)

    This release has two points:

    • Remove vendoring and continue with modules only. I consider this not breaking as this is a possibility since Go 1.11 and is not an issue on the current Go 1.13 and 1.14 build.
    • Fixed issue #28: call expirationCallback automatically on cache.Close()

    Note that while all expirationCallback routines are called for all items in the cache on cache.Close() there is no synchronized wait. This is the current behavior for expiration at runtime. It's up to the cache creator to decide whether to built in synchronization for a full clean shutdown. See TestCache_ExpirationOnClose(t *testing.T) for a sample with synchronization.

    Source code(tar.gz)
    Source code(zip)
  • v1.6.0(Oct 15, 2019)

    In issue #23 it became clear that people want to create and drop caches on the fly. There was a goroutine leak in that case.

    • The goroutine leak is fixed, after calling Close, an empty cache remains.
    • Tests are now done with uber goroutine leak detector.
    • Test logging has been cleaned.
    • 100% coverage, not by synthetic testing but by building up the test suite with seen issues over time.
    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Aug 20, 2019)

  • 1.5.0(Jun 17, 2019)

    Depending on your usage, the issues fixed may improve stability of your programs:

    #20 : A crash was observed by me in a configuration that was not in the test suite. This subsequently triggered some rework on the locking.

    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Apr 29, 2019)

    Depending on your usage, the issues fixed may improve stability of your programs:

    #17 : Fixes a data race on read and introduces RWLock which is better for performance on heavy workloads. #18 : Addresses excessive memory usage if you have a long running timer as global TTL. if pprof shows most of your memory in use coming from ttlcache then you must upgrade:

          flat  flat%   sum%        cum   cum%   calls calls% + context 	 	 
                                             1034.31MB   100% |*Cache).startExpirationProcessing
      983.58MB 82.74% 82.74%  1034.31MB 87.00%                | time.NewTimer
                                               50.73MB  4.90% |   time.startTimer
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Mar 27, 2019)

  • 1.2.0(Sep 8, 2018)

    This fixes issue #12 and updates the Travis CI to current go versions.

    SkipTtlExtensionOnHit allows the user to change the cache behaviour. When this flag is set to true it will no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated using SetCheckExpirationCallback.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Sep 8, 2018)

  • 1.1.0(Oct 28, 2017)

Rene Kroon
Rene Kroon
Recursively searches a map[string]interface{} structure for another map[string]interface{} structure

msirecurse Recursively searches a map[string]interface{} structure for existence of a map[string]interface{} structure Motivation I wrote this package

Fred Moyer 1 Mar 3, 2022
Decode / encode XML to/from map[string]interface{} (or JSON); extract values with dot-notation paths and wildcards. Replaces x2j and j2x packages.

mxj - to/from maps, XML and JSON Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-

Charles Banning 523 Jul 27, 2022
skipmap is a high-performance concurrent sorted map based on skip list. Up to 3x ~ 10x faster than sync.Map in the typical pattern.

Introduction skipmap is a high-performance concurrent map based on skip list. In typical pattern(one million operations, 90%LOAD 9%STORE 1%DELETE), th

ZhangYunHao 73 Jul 31, 2022
A fast (5x) string keyed read-only map for Go - particularly good for keys using a small set of nearby runes.

faststringmap faststringmap is a fast read-only string keyed map for Go (golang). For our use case it is approximately 5 times faster than using Go's

The Sensible Code Company 28 Jul 23, 2022
A memory-efficient trie for testing the existence/prefixes of string only(for now).

Succinct Trie A memory-efficient trie for testing the existence/prefixes of string only(for now). Install go get -u Docume

野辺かない 2 Mar 10, 2022
When storing a value in a Go interface allocates memory on the heap.

Go interface values This repository deep dives Go interface values, what they are, how they work, and when storing a value in a Go interface allocates

Andrew Kutz 27 Jul 20, 2022
Golang string comparison and edit distance algorithms library, featuring : Levenshtein, LCS, Hamming, Damerau levenshtein (OSA and Adjacent transpositions algorithms), Jaro-Winkler, Cosine, etc...

Go-edlib : Edit distance and string comparison library Golang string comparison and edit distance algorithms library featuring : Levenshtein, LCS, Ham

Hugo Bollon 335 Jul 19, 2022
Convert json string to Golang struct

json-to-go-cli Convert json string to Golang struct How to install git clone cd json-to-go-cli go bu

TianCheng 7 May 10, 2022
A thread safe map which has expiring key-value pairs

~ timedmap ~ A map which has expiring key-value pairs. go get Intro This package allows to set values to a map which will

zekro 42 Jun 28, 2022
A prefix-enhanced map in Go

PrefixMap PrefixMap is a prefix-enhanced map that eases the retrieval of values based on key prefixes. Quick Start Creating a PrefixMap // creates the

Alessandro Diaferia 30 Jun 13, 2022
A typed implementation of the Go sync.Map using code generation

syncmap A typed implementation of the Go sync.Map using code generation. Install go get -u[email protected] Examples: Using CLI $ syncma

Ariel Mashraki 236 Jul 22, 2022
💯 Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving

Package validator implements value validations for structs and individual fields based on tags.

Flamego 12 Feb 16, 2022
A Go library to iterate over potentially nested map keys using the visitor pattern

A Go library to iterate over potentially nested map keys using the visitor pattern

null 3 Mar 15, 2022
Go library for encoding native Go structures into generic map values.

wstructs origin: Go library for encoding native Go structures into generic map values. Installation Use go get. go ge

null 0 Jan 10, 2022
Multi-String Pattern Matching Algorithm Using TrieHashNode

Multi-String Pattern Matching algorithm. This implementation is inspired from Aho-Corasick algorithm Getting Started modelA = mspm.NewModel("mspm_mode

Sujit Shakya 17 Jan 23, 2022
Data structure and relevant algorithms for extremely fast prefix/fuzzy string searching.

Trie Data structure and relevant algorithms for extremely fast prefix/fuzzy string searching. Usage Create a Trie with: t := trie.New() Add Keys with:

Derek Parker 597 Jul 31, 2022
Implementation of Boyer-Moore fast string search algorithm in Go

boyermoore Implementation of Boyer-Moore fast string search algorithm in Go

sarp dağ demirel 50 Jun 25, 2022
Algorithms for various integer sequences from the OEIS site.

OEIS The ongoing quest to program every sequence in the OEIS database (in Golang) Content sequences -- The folder containing the seq package, which co

null 0 Dec 23, 2021
Implementation of various data structures and algorithms in Go

GoDS (Go Data Structures) Implementation of various data structures and algorithms in Go. Data Structures Containers Lists ArrayList SinglyLinkedList

TopXeQ 0 Jan 25, 2022