Documentation
¶
Overview ¶
Package redis provides a Redis-backed gateway.Coordinator, for sharing certificate issuance arbitration and distribution across every process in a deployment. It is a separate package specifically so that importing the root gateway package never pulls in github.com/redis/go-redis/v9 for applications that do not want it (e.g. those using the default gateway.MemoryCoordinator, or a different Coordinator entirely).
Index ¶
- type Coordinator
- func (c *Coordinator) CompareAndSwap(ctx context.Context, key string, expected, newValue []byte, ttl time.Duration) (bool, error)
- func (c *Coordinator) Get(ctx context.Context, key string) ([]byte, bool, error)
- func (c *Coordinator) Put(ctx context.Context, key string, value []byte, ttl time.Duration) error
- func (c *Coordinator) TryLock(ctx context.Context, key string, ttl time.Duration) (func(context.Context) error, error)
- type Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Coordinator ¶
type Coordinator struct {
// contains filtered or unexported fields
}
Coordinator is a gateway.Coordinator backed by a Redis client: Put/Get use plain SET/GET with the given TTL, TryLock uses SET NX PX for acquisition and the Lua script above for release, and CompareAndSwap uses the Lua script above for an atomic read-compare-write - real mutual exclusion and CAS primitives, unlike best-effort distributed locking.
TryLock's mutual exclusion, and CompareAndSwap's atomicity, hold against every client talking to the same Redis primary. They do not survive a Redis failover: SET NX PX (and this CAS script) only excludes other callers on the primary that accepted the write: if that primary fails before the write reaches a replica and a replica is promoted, the new primary has no record of it and a second caller can acquire the same lock or win the same CAS again. This is a fundamental limitation of single-primary Redis with asynchronous replication, not something a client-side Coordinator can close: a deployment that needs a lock/CAS to survive failover without any repeat-grant window needs a consensus-backed store (e.g. Redlock across independent Redis primaries, or etcd/ZooKeeper), which is outside the scope of this package.
func New ¶
func New(client goredis.UniversalClient, opts ...Option) *Coordinator
New creates a Coordinator backed by client. client may be a *redis.Client, *redis.ClusterClient, *redis.Ring, or any other goredis.UniversalClient implementation.
Example ¶
ExampleNew arbitrates certificate issuance across a deployment: TryLock elects the one node that issues, and Put/Get share the result with the others.
package main
import (
"context"
"log"
"time"
goredis "github.com/redis/go-redis/v9"
coordinatorredis "github.com/StringKe/goakt-gateway/coordinator/redis"
)
func main() {
client := goredis.NewClient(&goredis.Options{Addr: "localhost:6379"})
defer func() { _ = client.Close() }()
coordinator := coordinatorredis.New(client, coordinatorredis.WithKeyPrefix("myapp:"))
ctx := context.Background()
unlock, err := coordinator.TryLock(ctx, "issue:example.com", 30*time.Second)
if err != nil {
// Another node already holds the lock and is issuing; wait and read the result.
return
}
defer func() { _ = unlock(ctx) }()
if err := coordinator.Put(ctx, "cert:example.com", []byte("...PEM..."), time.Hour); err != nil {
log.Fatal(err)
}
if _, ok, err := coordinator.Get(ctx, "cert:example.com"); err != nil || !ok {
log.Fatal(err)
}
}
Output:
func (*Coordinator) CompareAndSwap ¶ added in v0.2.0
func (c *Coordinator) CompareAndSwap(ctx context.Context, key string, expected, newValue []byte, ttl time.Duration) (bool, error)
CompareAndSwap implements gateway.CASCoordinator, in the same "d:" data namespace Get/Put use, via casScript.
type Option ¶
type Option func(*Coordinator)
Option configures a Coordinator created with New.
func WithKeyPrefix ¶
WithKeyPrefix namespaces every key this Coordinator reads or writes, so multiple gateway deployments (or unrelated applications) can share one Redis instance/database without colliding. Defaults to no prefix.