Documentation
¶
Overview ¶
Package etcd is a thin, option-configured wrapper around go.etcd.io/etcd/client/v3.
It targets the dominant etcd use case in ad-tech/finance services — service registration (Put + Lease) and discovery (Get + Watch) — and provides ergonomic construction (functional options + sane defaults), a fail-fast cluster ping at construction, pass-through KV (Put/Get/Delete), Lease (Grant/KeepAlive/Revoke) and Watch operations, lightweight metrics + an event hook, an escape hatch to the underlying *clientv3.Client, and a graceful Close. The concurrency primitives (Mutex/Lock/Election) are deliberately NOT wrapped (0 local usage; reach them via Client() if needed) — this keeps the surface thin.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) Client() *clientv3.Client
- func (c *Client) Close() error
- func (c *Client) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error)
- func (c *Client) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error)
- func (c *Client) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseGrantResponse, error)
- func (c *Client) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error)
- func (c *Client) Metrics() Metrics
- func (c *Client) Options() Options
- func (c *Client) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error)
- func (c *Client) Revoke(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error)
- func (c *Client) SetOnEvent(fn func(Event))
- func (c *Client) Status(ctx context.Context, endpoint string) (*clientv3.StatusResponse, error)
- func (c *Client) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan
- type Event
- type Metrics
- type Option
- func WithAutoSyncInterval(d time.Duration) Option
- func WithDialKeepAliveTime(d time.Duration) Option
- func WithDialTimeout(d time.Duration) Option
- func WithEndpoints(endpoints ...string) Option
- func WithPassword(p string) Option
- func WithRejectOldCluster(b bool) Option
- func WithTLSConfig(c *tls.Config) Option
- func WithUsername(u string) Option
- type Options
Examples ¶
Constants ¶
const ( KindPut = "put" KindGet = "get" KindDelete = "delete" KindGrant = "grant" KindKeepAlive = "keepalive" KindRevoke = "revoke" KindWatch = "watch" KindStatus = "status" )
Event kinds.
const ( OutcomeSuccess = "success" OutcomeError = "error" )
Event outcomes.
Variables ¶
var ErrNoEndpoints = errors.New("etcd: at least one endpoint required (WithEndpoints)")
ErrNoEndpoints is returned by New when no endpoints were configured.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps an etcd client/v3 client. It is safe for concurrent use: all methods are goroutine-safe and Close is idempotent.
func New ¶
New connects to the etcd cluster, verifies reachability with a Status call, and returns a Client.
The context bounds the construction-time ping. If it carries no deadline, a 10s fallback is applied. etcd's clientv3.New opens a gRPC connection but does not guarantee the peer is live, so the Status ping fail-fast on a dead node.
Example ¶
ExampleNew shows the service-registration + discovery flow. It is a compile-checked illustration (an Example without an // Output: comment is compiled but not executed); wire your own endpoints to run it against a live etcd.
package main
import (
"context"
"log"
clientv3 "go.etcd.io/etcd/client/v3"
"github.com/v8fg/kit4go/etcd"
)
func main() {
ctx := context.Background()
c, err := etcd.New(ctx, etcd.WithEndpoints("http://localhost:2379"))
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Register a service instance with a lease: the key auto-expires if the
// process stops keep-aliving (crash → key vanishes → discovery drops it).
lease, err := c.Grant(ctx, 30) // 30s TTL
if err != nil {
log.Fatal(err)
}
if _, err := c.Put(ctx, "/services/bidder/inst-1", "10.0.0.1:8080", clientv3.WithLease(lease.ID)); err != nil {
log.Fatal(err)
}
// Discover: list all bidder instances.
resp, err := c.Get(ctx, "/services/bidder/", clientv3.WithPrefix())
if err != nil {
log.Fatal(err)
}
for _, kv := range resp.Kvs {
_ = kv // key = instance path, value = address
}
}
Output:
func Wrap ¶
Wrap adopts an existing *clientv3.Client. The Client does not own it: Close is a no-op. Useful for sharing a client.
func (*Client) Client ¶
Client returns the underlying *clientv3.Client for anything the wrapper does not expose directly (Txn, Compact, Cluster, Auth, concurrency.Mutex/Election). Returns nil when the Client was built from a mock (newWithAPI).
func (*Client) Close ¶
Close releases the underlying gRPC connection. No-op for a wrapped or mock-injected client, and safe to call any number of times: clientv3.Client.Close is not itself idempotent (a second close returns "context canceled"), so for an owning client only the first call reaches the underlying close.
func (*Client) Delete ¶
func (c *Client) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error)
Delete removes key(s). Forward OpOptions (WithPrefix, WithFromKey, ...) untouched.
func (*Client) Get ¶
func (c *Client) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error)
Get reads key(s). Forward OpOptions (WithPrefix, WithRange, WithLimit, ...) untouched.
func (*Client) Grant ¶
Grant creates a lease with the given TTL (seconds) and returns its ID. Pair with KeepAlive to keep it alive, or Put(..., WithLease(id)) to attach it.
func (*Client) KeepAlive ¶
func (c *Client) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error)
KeepAlive keeps a lease alive until ctx is cancelled, streaming responses on the returned channel. The caller MUST drain it (range over it) or the keep-alive goroutine stalls. The wrapper increments the counter once on start.
func (*Client) Options ¶
Options returns the resolved options the client was built with.
The struct includes Password: do not log or serialize it verbatim.
func (*Client) Put ¶
func (c *Client) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error)
Put stores a key/value. Forward OpOptions (WithLease, WithPrevKV, ...) untouched.
func (*Client) Revoke ¶
func (c *Client) Revoke(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error)
Revoke revokes a lease immediately (deletes all keys attached to it).
func (*Client) SetOnEvent ¶
SetOnEvent installs a hook fired after each operation (nil disables it). The hook runs on the calling goroutine; keep it cheap and must not panic (a panic propagates to the caller — the wrapper does not recover). When nil, the cost is a single atomic-pointer load per operation (effectively zero overhead).
func (*Client) Status ¶
Status returns cluster status for the given endpoint (health, leader, version).
func (*Client) Watch ¶
func (c *Client) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan
Watch subscribes to changes on key (or a prefix with WithPrefix). It returns etcd's WatchChan; range over it to receive events. The wrapper fires one event on subscription start. The channel is closed when ctx is cancelled.
type Event ¶
type Event struct {
Kind string // KindPut, KindGet, KindDelete, KindGrant, KindKeepAlive, KindRevoke, KindWatch, KindStatus
Outcome string // OutcomeSuccess or OutcomeError
}
Event is fired after each operation when an OnEvent hook is installed.
type Metrics ¶
type Metrics struct {
Puts uint64 // Put calls
Gets uint64 // Get calls
Deletes uint64 // Delete calls
Grants uint64 // Grant calls
Watches uint64 // Watch subscriptions started
Errors uint64 // any operation returning a non-nil error
}
Metrics is a point-in-time snapshot of Client counters (all atomic loads).
type Option ¶
type Option func(*Options)
Option configures Options.
func WithAutoSyncInterval ¶
WithAutoSyncInterval sets how often endpoints are re-resolved from the cluster (0 disables auto-sync).
func WithDialKeepAliveTime ¶
WithDialKeepAliveTime sets the gRPC keepalive ping interval (0 -> client default 10s).
func WithDialTimeout ¶
WithDialTimeout sets the connection dial timeout (0 -> client default 5s).
func WithEndpoints ¶
WithEndpoints sets the etcd cluster URLs (e.g. "http://localhost:2379"). Copies the slice so later caller mutation cannot affect the client. Required.
func WithRejectOldCluster ¶
WithRejectOldCluster rejects connecting to an incompatible (old) cluster.
func WithUsername ¶
WithUsername sets the auth username (pair with WithPassword).
type Options ¶
type Options struct {
Endpoints []string
DialTimeout time.Duration
DialKeepAliveTime time.Duration
TLS *tls.Config
Username string
Password string
AutoSyncInterval time.Duration
RejectOldCluster bool
}
Options configures the etcd client. Zero-valued tuning fields defer to client/v3's own defaults; only Endpoints is required.