A pure-Go (no cgo), MRI-faithful reimplementation of the Ruby
etcdv3 gem's client surface for
etcd v3 — the ergonomics and result/error model of the gem's connection
object (get / put / del / exists?, watch, lease_*, transaction,
lock / unlock) layered over the official pure-Go etcd client.
It does not reimplement the etcd protocol. It consumes
go.etcd.io/etcd/client/v3 as
its transport and maps the gem's API onto it, so a static, CGO=0 binary talks to
a real etcd cluster.
It is the etcd backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-redis and go-ruby-pg.
Transport is a host seam. A
Clientdrives an injected transport whose method set is satisfied directly by*clientv3.Client(no adapter). This makes every method's request-building and response-mapping logic testable against a deterministic in-memory transport — no external etcd, no cgo — so the suite holds 100% coverage on every arch under qemu, mirroring the go-ruby-nats embedded-server-plus-deterministic-fallback split. A separate live suite drives a real in-process embedded etcd for round-trip validation on native lanes.
- KV —
Get(single, range viaWithRange, prefix viaWithPrefix,WithFromKey,WithLimit,WithRevision,WithSerializable,WithKeysOnly,WithCountOnly),Put(WithLease,WithPrevKV),Del,Exists. - Watch —
Watch(channel form) andWatchBlock(the gem'swatch(key){ |events| }block form) over keys, prefixes and ranges, withPUT/DELETEevents and previous values. - Lease —
LeaseGrant,LeaseKeepAliveOnce,LeaseRevoke,LeaseTTL(with attached keys). - Transaction —
Transaction { If / Then / Else }mapping the gem's compare / success / failure lists, withCompareoverValue/Version/CreateRevision/ModRevision/LeaseValue. - Lock —
Lock/Unlock, a lease-backed mutex implementing etcd's concurrency recipe (lowest creation-revision owns; waiters watch their predecessor). - Maintenance —
Members,Status. - Errors — an
Etcdv3-style error tree (Error+ one sentinel per gRPC status code) matchable witherrors.Is.
c, err := etcd.New(etcd.Config{Endpoints: []string{"127.0.0.1:2379"}})
if err != nil {
log.Fatal(err)
}
defer c.Close()
ctx := context.Background()
c.Put(ctx, "/service/a", "up", etcd.WithLease(lease.ID))
gr, _ := c.Get(ctx, "/service/", etcd.WithPrefix())
for _, kv := range gr.Kvs {
fmt.Println(kv.Key, "=", kv.Value)
}
c.Transaction(ctx, func(t *etcd.Txn) {
t.If(etcd.Compare(etcd.Value("/service/a"), "=", "up")).
Then(etcd.OpPut("/service/a", "draining")).
Else(etcd.OpGet("/service/a"))
})| etcdv3 gem | go-ruby-etcd/etcd |
|---|---|
Etcdv3.new(endpoints: ...) |
etcd.New(etcd.Config{Endpoints: ...}) |
conn.put('k', 'v') |
c.Put(ctx, "k", "v") |
conn.get('k', range_end: 'l') |
c.Get(ctx, "k", etcd.WithRange("l")) |
conn.del('k') / conn.exists?('k') |
c.Del(ctx, "k") / c.Exists(ctx, "k") |
conn.watch('k') { |evs| ... } |
c.WatchBlock(ctx, fn, "k") / c.Watch(ctx, "k") |
conn.lease_grant(10) |
c.LeaseGrant(ctx, 10) |
conn.transaction { |t| ... } |
c.Transaction(ctx, func(t *etcd.Txn){ ... }) |
conn.lock('n', 10) / conn.unlock |
c.Lock(ctx, "n", 10) / c.Unlock(ctx, lk) |
The default suite runs with -race and holds 100% statement coverage on all
three host OSes and the six supported 64-bit architectures (amd64, arm64,
riscv64, loong64, ppc64le and big-endian s390x), driving the full client logic
against a deterministic in-memory transport with no external etcd:
go test -race -cover ./...The live/ nested module validates real round-trip behaviour against
an in-process embedded etcd (native-only; kept out of the main module so its
large dependency tree never enters this go.mod):
cd live && go test ./...BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-etcd/etcd authors.
Unlike the rest of the go-ruby family, this library does not target WebAssembly:
its backing engine — go.etcd.io/bbolt (memory-mapped files + file locking) — relies on mmap and native filesystem syscalls that
the js/wasm and wasip1/wasm sandboxes do not provide. It ships for the six
64-bit native/qemu arches only.
