Custom Storage
ACOR does not support custom storage backends. Redis is the only backend, and
Create() always builds it. This page explains why the interface you may have seen in
older releases is gone, and what to do instead for the case it was usually reached for:
testing.
What changed in v1.5.0
Through v1.4.0 the package exported a KVStorage interface, along with Pipeliner,
Subscription, StringMapResult, PubSubMessage, and Z. They were exported in
anticipation of pluggable backends.
They are unexported as of v1.5.0, for two reasons:
- Nothing could be plugged in. No public constructor accepted a
KVStorage, and no public function returned one. You could name the interface and write an implementation, but there was no way to hand it to ACOR — so the capability the interface implied never existed. - Freezing it would have blocked the feature it was for. The
compatibility policy forbids adding a method to an
exported interface inside
v1.KVStoragehas 23 methods; hadv1.5.0frozen it, a future backend needing one more operation would have had nowhere to put it for the rest of thev1line.
Leaving the shape unfrozen is what keeps pluggable storage possible. Publishing an
interface later is an addition, which v1 allows; growing a frozen one is not.
Testing without Redis
This is what the interface was most often reached for, and it has a better answer that works today: miniredis, an in-process Redis-compatible server. ACOR’s own test suite uses it, so the behavior you test against is the behavior ACOR is tested against.
package example
import (
"testing"
"github.com/alicebob/miniredis/v2"
"github.com/skyoo2003/acor/pkg/acor"
)
func TestWithMiniredis(t *testing.T) {
mr := miniredis.RunT(t)
ac, err := acor.Create(&acor.AhoCorasickArgs{
Addr: mr.Addr(),
Name: "test-collection",
})
if err != nil {
t.Fatal(err)
}
defer func() { _ = ac.Close() }()
if _, err := ac.Add("hello"); err != nil {
t.Fatal(err)
}
matches, err := ac.Find("hello world")
if err != nil {
t.Fatal(err)
}
if len(matches) != 1 || matches[0] != "hello" {
t.Fatalf("Find() = %v, want [hello]", matches)
}
}
miniredis covers the commands ACOR issues, including the Lua scripts the V2 schema
uses for atomic writes. To exercise Pub/Sub-driven cache invalidation across instances,
point two instances at the same miniredis address.
Reducing Redis traffic
If the reason for a custom backend was to avoid Redis round trips on reads rather than to replace Redis, that is already available without one:
Presetserves reads from a local automaton and touches Redis only on writes and invalidations. See Presets.EnableCachecaches trie data locally and invalidates over Pub/Sub.
CacheStats() reports whether either is working — hit rate, rebuild cost, and
invalidation lag — without any Redis I/O of its own.