Skip to content
WaterkyuuPublic

About

A lightweight, typed Go query cache with expiration, concurrent load sharing, and context cancellation

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

207 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cacheq

cacheq

English | 简体中文

Manage shared query data, state, and refresh lifecycles

A typed, dependency-free query client for Go 1.22 and later

Share data across components, combine duplicate requests, and observe query state.

Go 1.22+

Features · Install

When to use cacheq

Use cacheq when your Go application needs to coordinate data loading and synchronization: reuse cached results, share concurrent requests, batch reads, or refresh related queries after a write.

It fits backend services, CLI/TUI applications, and background workers. You provide the HTTP requests, database operations, or other loaders; cacheq manages how they execute and how consumers receive their results.

Application scenario How cacheq helps
Several handlers or components read the same user, configuration, or resource Reuse fresh cached data and share in-flight requests for the same key
A list or aggregation needs many objects from a backend with a bulk API Collect different-key loads into batches, or submit known keys with LoadMany
A successful write must update a detail view and refresh related lists or totals Coordinate cache updates and invalidation through Mutation; optionally wait for associated refreshes
Multiple callers need the same query key, loader, and loading policy Reuse a QueryDefinition and shared QueryOptions; use tags and filters to manage related queries
A view or long-running task needs to react to loading, results, errors, and refreshes Subscribe to typed query updates; inspect diagnostic events to trace execution
Reads and writes share a database or service with limited capacity Bound active callbacks and queued work with a shared Executor

cacheq stores its cache and query state within one process. For simple local values that only need expiration, a TTL cache may be sufficient. Use external storage when data must persist or be shared across processes.

Features

  • Type-safe shared queries and caching.
  • Cache policies and lifecycle management.
  • Cache updates, mutations, and invalidation.
  • Automatic batch loading.
  • Shared execution budgets.
  • Query observability.

Install

go get github.com/Waterkyuu/cacheq

Import github.com/Waterkyuu/cacheq as package cacheq. Requires Go 1.22 or later and has no external dependencies.

Quick start

Save this complete program as main.go in a Go module that imports cacheq, then run go run .. It needs no server or credentials.

package main

import (
	"context"
	"fmt"
	"sync/atomic"
	"time"

	"github.com/Waterkyuu/cacheq"
)

// main gives two components the same query without loading the data twice.
func main() {
	client := cacheq.NewClient(cacheq.Options{StaleTime: time.Minute})
	defer client.Close()

	var calls atomic.Int32
	load := func(context.Context) (string, error) {
		calls.Add(1)
		return "hello", nil
	}
	queries := []*cacheq.QueryHandle[string]{
		cacheq.Query(client, "greeting", load),
		cacheq.Query(client, "greeting", load),
	}
	for _, query := range queries {
		defer query.Close()
	}
	for i, query := range queries {
		for state := range query.Updates() {
			if state.Err != nil {
				panic(state.Err)
			}
			if state.HasData && !state.Fetching {
				fmt.Printf("component %d: %s\n", i+1, state.Data)
				break
			}
		}
	}
	fmt.Printf("loader calls: %d\n", calls.Load())
}

Expected output:

component 1: hello
component 2: hello
loader calls: 1

Query loads in the background. These two components either join the same active request or reuse its fresh result. Updates() delivers the initial and latest states; slow consumers can skip intermediate transitions. Use Snapshot() for current state, or Fetch to wait for a result without subscribing.

Use cases

Scenario Pair with Purpose
Bubble Tea / TUI Query + Updates() + tea.Cmd Deliver loading, refresh, and error state to the message loop
CLI / background jobs Fetch + Options.Timeout Wait for results, reuse in-process data, and bound load time
HTTP handlers A shared Client + Fetch(r.Context(), ...) Merge concurrent same-key requests and respond to cancellation
MCP resources FetchWithExpiry + Invalidate + Stats() Share backend reads across connections, preserve TTL, and notify after changes

After a successful mutation, use Invalidate for related queries. HTTP cache keys must include result-affecting parameters and user or tenant scope. See the query guide and invalidation guide for API usage.

Bubble Tea example

Run the task board to see list, detail, and summary views subscribe to one query. Toggle a task to refresh all three views, or inject a failure to see retained data and recovery. The example requires Go 1.26+.

See the walkthrough for the subscription and mutation flow.

cd examples/bubbletea
go run .

MCP example

Full source and instructions demonstrate resource caching, change notifications, and private scopes. The example requires Go 1.25+.

cd examples/mcp
go run .

Reproducible benchmarks

Compare no cache, first load, and cache hits for 100 sequential reads of the same data in the benchmark guide. The main table reports total time and actual loader calls; internal cost and concurrency diagnostics are documented separately.

go test -run '^$' -bench '^BenchmarkCacheBenefit$' -benchmem -count=3 -cpu=1

The concurrent request scenarios compare cache behavior for shared and distinct keys with 100 reads, GOMAXPROCS=4, and four request goroutines performing 25 reads each.

Feature guides

Read the documentation website in English or Chinese.

Guide Coverage
Queries and conditions Query, snapshots, updates, enablement, refresh, Fetch, and HTTP loaders
Query options Shared loading options, reusable typed query definitions, freshness, retries, and timeouts
Query filters Reusable key and predicate selection for invalidation, awaited refresh, cancellation, and removal
Query tags Declare related query groups, select by label, and manage registration and retention
Automatic batch loading Concurrent-key batching, LoadMany for known keys, query API integration, per-key results, and cancellation
Cache and lifecycle Cache operations, options, cleanup, cancellation, removal, closure, and errors
Invalidation & refresh Single-key, batch, predicate invalidation, awaitable refresh, and bounded follow-up loads
Atomic cache updates Conflict-safe typed transformations, immutable values, and explicit retry contracts
Mutations & synchronization Write state, optional refresh waiting, associated invalidation, and separate synchronization errors
Execution budgets Shared concurrency limits, bounded FIFO queues, overload handling, and cancellation
Observability Cache hits, shared requests, load outcomes and duration, retries, and cleanup statistics
Diagnostic events Per-key load causes and sources, shared operation IDs, joins, retries, discarded results, and bounded subscriptions
MCP resource caching Server-side reuse, remaining TTL, change notifications, private scopes, and e2e

Complete integrations live in examples; the guides cover API details. Treat cached values as immutable; copy slices and maps before modifying them. Different result types cannot reuse the same key.

Versioning and upgrades

cacheq follows Semantic Versioning. During v0, patch releases preserve compatibility; minor releases may introduce breaking changes with migration notes. From v1, breaking public API changes require a new major version. The core library currently supports Go 1.22 and later; example modules may require newer Go versions.

See the version policy for the public API boundary, deprecation policy, Go support, and upgrade guidance. Published version tags are never moved or reused.

Contributing

See the contribution guide for local setup, Go code guidelines, tests, documentation, and the PR workflow. Bug reports and feature proposals are welcome through GitHub Issues.

License

cacheq is licensed under the MIT License.

About

A lightweight, typed Go query cache with expiration, concurrent load sharing, and context cancellation

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages