Skip to content

kdihalas/write-through-cache

Repository files navigation

write-through-cache

ci codecov

An afero.Fs implementation that wraps a remote afero.Fs with an in-memory write-through / read-through cache.

Every write goes to the remote filesystem — synchronously by default, or asynchronously if configured — and is mirrored into an in-memory afero.MemMapFs for a configurable TTL. Reads served within the TTL never touch the remote fs. Once a cache entry expires, a background sweeper reclaims the memory, and the next read transparently falls through to the remote fs and repopulates the cache.

Install

go get github.com/kdihalas/write-through-cache
go get github.com/spf13/afero

Usage

import (
	"time"

	cache "github.com/kdihalas/write-through-cache"
	"github.com/spf13/afero"
)

remote := afero.NewOsFs() // or any other afero.Fs: S3, SFTP, NFS, ...
fs := cache.New(remote, 2*time.Second)
defer fs.Close() // stops the background eviction sweeper

afero.WriteFile(fs, "/data/hello.txt", []byte("hi"), 0o644) // hits remote + cache
afero.ReadFile(fs, "/data/hello.txt")                       // served from cache, TTL permitting

See example/example.go for a runnable end-to-end demo (write, cached read, and read-through after TTL expiry):

go run ./example

Behavior

  • TTL counts from write time, refreshed on every write to the same path.
  • Write mode is configurable:
    • Default: synchronous write-through — Write/Close only return once the remote write completes.
    • cache.WithAsync(onError): write-behind — the call returns as soon as the in-memory write succeeds; the remote write happens in the background, and onError(path, err) fires if it fails.
  • Reads are read-through: a cache miss or expired entry falls through to the remote fs, repopulating the cache on success.
  • Eviction is handled by a background sweeper goroutine on a ticker (default interval: ttl/4, minimum 1s; override with cache.WithSweepInterval). Call Fs.Close() to stop it on shutdown.
  • Remove, RemoveAll, Rename, Chmod, Chown, and Chtimes are applied to the remote fs (source of truth) and mirrored to the cache/expiry state.

Testing

go build ./...
go vet ./...
go test ./... -race

Releases

Versioning and CHANGELOG.md are managed by release-please (see .github/workflows/release-please.yml). It watches main, and on every push opens or updates a "release PR" summarizing pending changes; merging that PR tags a release and publishes a GitHub Release. Since this is a Go module, there's nothing further to "publish" — consumers pull the new version straight from the git tag via the Go module proxy.

Commit messages on main must follow Conventional Commits (fix:, feat:, feat!: / BREAKING CHANGE:, etc.) — release-please uses them to decide the next version bump and changelog entries.

About

An afero.Fs implementation that wraps a remote afero.Fs with an in-memory write-through / read-through cache.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages