RamStorage (r9e) is a small, thread-safe, generic, dependency-free
Go library of in-memory key-value containers with a rich set
of convenient methods to store, retrieve, transform, and iterate data.
It is focused on usability and simplicity β without giving up performance. Only the Go standard library is used; there are zero third-party dependencies.
- π Thread-safe β every operation is safe for concurrent use.
- 𧬠Generic β
MapKeyValue[K comparable, T any]stores any comparable key and any value type. - π§± Two containers, one API β a
sync.RWMutex-backed map and async.Map-backed store expose the same method set, so you can switch by workload. - π Range-over-func iterators β
for k, v := range kv.All()(Go 1.23+). - π§° Functional helpers β
Map,Filter,Partition,Merge,Clone,GetOrSet,SortKeys,SortValuesβ all non-mutating where it matters. - ποΈ JSON-ready β implements
json.Marshaler/json.Unmarshaler. - β‘ O(1)
Size()on both containers. - π« Zero dependencies β standard library only.
- π Apache-2.0 licensed.
r9e takes advantage of Go generics and
the standard library's own concurrency primitives to provide a simple way to
store and retrieve data from memory. Two containers share the same method
set:
flowchart TD
API["Shared API<br/>Set Β· Get Β· GetOrSet Β· Delete Β· All Β· Map Β· Filter Β· Partition Β· Merge Β· JSON"]
API --> M["MapKeyValue[K, T]<br/>native map + sync.RWMutex"]
API --> S["SMapKeyValue[K, T]<br/>sync.Map + atomic counter"]
M --> M1["Best for read-heavy / mixed<br/>consistent bulk snapshots"]
S --> S1["Best for disjoint-key writes<br/>write-once / read-many"]
| Container | Backing | Best for |
|---|---|---|
MapKeyValue[K, T] |
map + sync.RWMutex |
Read-heavy / mixed workloads, consistent snapshots |
SMapKeyValue[K, T] |
sync.Map + atomic counter |
Disjoint-key writes, write-once/read-many |
Not sure which to use? Start with
MapKeyValue. Switch toSMapKeyValueonly if profiling shows thesync.Mapaccess pattern fits your workload better. See docs/containers.md.
- Go 1.26 or newer
- No external Go modules
Add the latest release to your module:
go get github.com/slashdevops/r9e@latestPin a specific version:
go get github.com/slashdevops/r9e@vX.Y.ZUpdate to the newest available version later:
go get -u github.com/slashdevops/r9eThen import it:
import "github.com/slashdevops/r9e"package main
import (
"fmt"
"github.com/slashdevops/r9e"
)
func main() {
kv := r9e.NewMapKeyValue[string, int]()
kv.Set("answer", 42)
if v, ok := kv.GetAndCheck("answer"); ok {
fmt.Println("answer =", v) // answer = 42
}
// Range-over-func iteration (Go 1.23+).
for k, v := range kv.All() {
fmt.Printf("%s = %d\n", k, v)
}
}type Constant struct {
Name string
Value float64
}
kv := r9e.NewMapKeyValue[string, Constant](r9e.WithCapacity(8))
kv.Set("pi", Constant{"Archimedes' constant", 3.141592})
kv.Set("e", Constant{"Euler's number", 2.718281})
kv.Set("phi", Constant{"Golden ratio", 1.618033})
// Keep only the "large" constants (returns a NEW container).
large := kv.FilterValue(func(c Constant) bool { return c.Value > 2.0 })
// Sort the survivors by value.
sorted := large.SortValues(func(a, b Constant) bool { return a.Value > b.Value })
for _, c := range sorted {
fmt.Printf("%s = %v\n", c.Name, c.Value)
}Both containers implement the same methods:
| Group | Methods |
|---|---|
| Access | Set, Get, GetAndCheck, GetOrSet, GetAndDelete, Delete, Clear |
| Query | Size, IsEmpty, ContainsKey, ContainsValue |
| Bulk | Keys, Values, All (iterator), ForEach, ForEachKey, ForEachValue |
| Copy/Combine | Clone, CloneAndClear, Merge, DeepEqual |
| Transform | Map, MapKey, MapValue, Filter, FilterKey, FilterValue |
| Split | Partition, PartitionKey, PartitionValue |
| Sort | SortKeys, SortValues |
| Serialize | MarshalJSON, UnmarshalJSON |
Full reference: pkg.go.dev/github.com/slashdevops/r9e
Extensive guides with runnable examples and diagrams live in docs/:
| Guide | What it covers |
|---|---|
| Getting Started | Install, first program, core types. |
| Containers | MapKeyValue vs SMapKeyValue, and how to choose. |
| Concurrency | Thread-safety model and locking rules. |
| Operations | Map / Filter / Partition / Merge / Clone / sorting. |
| Iteration | All() iterators and the ForEach family. |
| JSON | Marshaling containers to and from JSON. |
| Performance | Cost model, benchmarks, and tuning. |
| Migration | What's New in v1.0.0 and breaking changes. |
| FAQ | Common questions and gotchas. |
Runnable examples also live in example_test.go.
v1.0.0 is the first stable release. It modernizes the library for Go 1.26
and adds several long-requested capabilities.
- π Range-over-func iterators β
All()returns aniter.Seq2[K, T]. - π§°
GetOrSetβ atomic get-or-insert. - π
Mergeβ combine one container into another. - ποΈ JSON support β
MarshalJSON/UnmarshalJSONon both containers. - π Correctness fixes:
- Fixed potential deadlocks in
MapKeyValuecaused by recursive read locking inClone,Map,Filter,Partition,DeepEqual, and sorting. - Fixed
SMapKeyValue.Size()over-counting when overwriting existing keys. - Fixed a non-atomic counter update race in
SMapKeyValue.
- Fixed potential deadlocks in
- π§Ή Modern internals β uses
maps,slices, theclearbuiltin, andsync.Map.Clear.
| Before | After | Why |
|---|---|---|
go 1.19 |
go 1.26 |
Iterators, maps/slices, clear, sync.Map.Clear. |
GetAnDelete(...) |
GetAndDelete(...) |
Fixes a spelling typo in the public API. |
IsFull() bool |
removed β use !IsEmpty() |
The old method meant "not empty", which was misleading. |
Key(k) K |
removed β use ContainsKey(k) |
The old method returned the key or a zero value; ContainsKey is clearer. |
SortKeys(...) []*K |
SortKeys(...) []K |
Returning pointers into internal state was unsafe and unidiomatic. |
SortValues(...) []*T |
SortValues(...) []T |
Same as above. |
Migration guide with before/after snippets: docs/migration.md.
r9e keeps the common operations cheap:
Set,Get,GetAndCheck,GetOrSet,Delete,Sizeare O(1).ContainsValueand the bulkMap/Filter/Partition/ sort operations are O(n).WithCapacity(n)presizesMapKeyValueto avoid incremental map growth.
Run the benchmarks yourself:
git clone git@github.com:slashdevops/r9e.git
cd r9e/
make bench
# or:
go test -run '^$' -bench . -benchmem ./...See docs/performance.md for the cost model and tuning tips.
The same checks CI runs:
make check # fmt + vet + race tests + build
# individually:
go fmt ./...
go vet ./...
go test -race -covermode=atomic -coverprofile=coverage.txt ./...
go build ./...Issues and pull requests are welcome at
github.com/slashdevops/r9e. Please keep
changes small, idiomatic, tested, documented, and dependency-free unless there
is a clear reason to expand the project scope. See
.github/copilot-instructions.md (also linked
as AGENTS.md) for conventions.
RamStorage (r9e) is released under the Apache License 2.0: