An etcd v3 client for MoonBit.
| Package | Contents |
|---|---|
| moonbitstack/moonetcd | Protobuf message types and codecs for the etcd v3 API, plus key helpers and the error model. Pure data — builds on every target. |
| moonbitstack/moonetcd/client | The gRPC transport: Client, KV, Watch, Lease, Cluster, Auth, Maintenance, Lock, Election. Native only. |
| moonbitstack/moonetcd/concurrency | Session, Mutex, Election built on the client. Native only. |
| moonbitstack/moonetcd/sd | The moonsd contract on etcd: Registry. Native only. |
| moonbitstack/moonetcd/examples/... | Runnable examples (see below). |
moon add moonbitstack/moonetcdasync fn main {
let config = @moonetcd.ClientConfig::{
..@moonetcd.default_client_config,
endpoints: ["127.0.0.1:2379"],
}
let client = @client.Client::connect(config)
let _ = client.kv().put(b"/greeting", b"hello")
let resp = client.kv().get(b"/greeting")
for item in resp.kvs {
println("value = \{@utf8.decode_lossy(item.value[:])}")
}
client.close()
}| Method | RPC |
|---|---|
| put(key, value, lease?, prev_kv?) | Put |
| get(key), get_prefix(prefix) | Range |
| range(RangeRequest) | Range |
| delete_range(DeleteRangeRequest) | DeleteRange |
| txn(TxnRequest) | Txn |
| compact(revision, physical?) | Compact |
@async.with_task_group <| g => {
let chan = client.watch().watch_create(
g,
@moonetcd.WatchCreateRequest::{ ..@moonetcd.WatchCreateRequest::empty(), key: b"/watched" },
)
match chan.next() {
Some(resp) => println("revision \{resp.header.revision}, \{resp.events.length()} event(s)")
None => println("watch ended")
}
chan.cancel()
}| Method | RPC |
|---|---|
| grant(ttl, id?) | LeaseGrant |
| revoke(id) | LeaseRevoke |
| time_to_live(id, keys?) | LeaseTimeToLive |
| leases() | LeaseLeases |
| keep_alive(task_group) | LeaseKeepAlive (bidi) |
import {
"moonbitstack/moonetcd/client",
"moonbitstack/moonetcd/concurrency",
}
let session = @concurrency.Session::new(client, 30L) // grants a lease
let mutex = @concurrency.Mutex::new(client, session, b"/my-lock")
mutex.lock()
// critical section
mutex.unlock()
session.close() // revokes the leaselet registry = @sd.Registry::new(client)
group.spawn_bg(() => @moonsd.hold(registry, "greeter", @moonsd.Endpoint::new("10.0.0.1", 12000)))
let endpoints = registry.resolve("greeter")| Field | Meaning |
|---|---|
| endpoints | Array[String] of "host:port"; a missing port defaults to 2379. |
| username, password | etcd credentials; authentication happens at connect. |
| token | a pre-existing auth token to reuse. |
| dial_timeout_millis | connection timeout. |
| keep_alive_millis | HTTP/2 keep-alive interval. |
| tls | TLS settings (see the note below). |
moon run --target native src/examples/00-connect # dial and print member status
moon run --target native src/examples/01-kv # put / get / prefix scan / delete
moon run --target native src/examples/02-watch # watch a key and read the event
moon run --target native src/examples/03-lease # grant a lease, attach a key, revoke
moon run --target native src/examples/04-txn # compare-and-swap with a transaction
moon run --target native src/examples/05-lock # distributed mutex over a sessionmoon check --target all --deny-warn
moon build --target all
moon test --target all
moon info --target allpub(all) struct AlarmRequest {
action : AlarmAction
member_id : UInt64
alarm : AlarmType
} derive(Eq)fn AuthRoleDeleteResponse::not_equal(x : AuthRoleDeleteResponse, y : AuthRoleDeleteResponse) -> Boolfn AuthRoleGrantPermissionRequest::decode(data : Bytes) -> AuthRoleGrantPermissionRequest raise PbErrorfn AuthRoleGrantPermissionRequest::not_equal(x : AuthRoleGrantPermissionRequest, y : AuthRoleGrantPermissionRequest) -> Boolfn AuthRoleGrantPermissionResponse::decode(data : Bytes) -> AuthRoleGrantPermissionResponse raise PbErrorfn AuthRoleGrantPermissionResponse::not_equal(x : AuthRoleGrantPermissionResponse, y : AuthRoleGrantPermissionResponse) -> Boolpub(all) struct AuthRoleRevokePermissionRequest {
role : String
key : Bytes
range_end : Bytes
} derive(Eq)fn AuthRoleRevokePermissionRequest::decode(data : Bytes) -> AuthRoleRevokePermissionRequest raise PbErrorfn AuthRoleRevokePermissionRequest::not_equal(x : AuthRoleRevokePermissionRequest, y : AuthRoleRevokePermissionRequest) -> Boolfn AuthRoleRevokePermissionResponse::decode(data : Bytes) -> AuthRoleRevokePermissionResponse raise PbErrorfn AuthRoleRevokePermissionResponse::not_equal(x : AuthRoleRevokePermissionResponse, y : AuthRoleRevokePermissionResponse) -> Boolpub(all) struct AuthStatusResponse {
header : ResponseHeader
enabled : Bool
auth_revision : Int64
} derive(Eq)pub(all) struct AuthUserAddRequest {
name : String
password : String
options : UserAddOptions
} derive(Eq)fn AuthUserChangePasswordRequest::decode(data : Bytes) -> AuthUserChangePasswordRequest raise PbErrorfn AuthUserChangePasswordRequest::equal(AuthUserChangePasswordRequest, AuthUserChangePasswordRequest) -> Boolfn AuthUserChangePasswordRequest::not_equal(x : AuthUserChangePasswordRequest, y : AuthUserChangePasswordRequest) -> Boolfn AuthUserChangePasswordResponse::decode(data : Bytes) -> AuthUserChangePasswordResponse raise PbErrorfn AuthUserChangePasswordResponse::not_equal(x : AuthUserChangePasswordResponse, y : AuthUserChangePasswordResponse) -> Boolfn AuthUserDeleteResponse::not_equal(x : AuthUserDeleteResponse, y : AuthUserDeleteResponse) -> Boolfn AuthUserGrantRoleRequest::not_equal(x : AuthUserGrantRoleRequest, y : AuthUserGrantRoleRequest) -> Boolfn AuthUserGrantRoleResponse::not_equal(x : AuthUserGrantRoleResponse, y : AuthUserGrantRoleResponse) -> Boolfn AuthUserRevokeRoleRequest::not_equal(x : AuthUserRevokeRoleRequest, y : AuthUserRevokeRoleRequest) -> Boolfn AuthUserRevokeRoleResponse::equal(AuthUserRevokeRoleResponse, AuthUserRevokeRoleResponse) -> Boolfn AuthUserRevokeRoleResponse::not_equal(x : AuthUserRevokeRoleResponse, y : AuthUserRevokeRoleResponse) -> Boolpub(all) enum CmpTarget {
Version(Int64)
CreateRevision(Int64)
ModRevision(Int64)
Value(Bytes)
Lease(Int64)
} derive(Eq)pub(all) struct Compare {
result : CompareResult
target : CompareTarget
key : Bytes
range_end : Bytes
target_union : CmpTarget
} derive(Eq)pub(all) struct DeleteRangeResponse {
header : ResponseHeader
deleted : Int64
prev_kvs : Array[KeyValue]
} derive(Eq)pub(all) struct HashKVResponse {
header : ResponseHeader
hash : UInt
compact_revision : Int64
hash_revision : Int64
} derive(Eq)pub(all) struct KeyValue {
key : Bytes
create_revision : Int64
mod_revision : Int64
version : Int64
value : Bytes
lease : Int64
} derive(Eq)pub(all) struct LeaseGrantResponse {
header : ResponseHeader
id : Int64
ttl : Int64
error : String
} derive(Eq)fn LeaseKeepAliveResponse::not_equal(x : LeaseKeepAliveResponse, y : LeaseKeepAliveResponse) -> Boolpub(all) struct LeaseLeasesResponse {
header : ResponseHeader
leases : Array[LeaseStatus]
} derive(Eq)fn LeaseTimeToLiveRequest::not_equal(x : LeaseTimeToLiveRequest, y : LeaseTimeToLiveRequest) -> Boolpub(all) struct LeaseTimeToLiveResponse {
header : ResponseHeader
id : Int64
ttl : Int64
granted_ttl : Int64
keys : Array[Bytes]
} derive(Eq)fn LeaseTimeToLiveResponse::not_equal(x : LeaseTimeToLiveResponse, y : LeaseTimeToLiveResponse) -> Boolpub(all) struct MemberAddResponse {
header : ResponseHeader
member_ : Member
members : Array[Member]
} derive(Eq)pub(all) struct PutRequest {
key : Bytes
value : Bytes
lease : Int64
prev_kv : Bool
ignore_value : Bool
ignore_lease : Bool
} derive(Eq)pub(all) struct RangeRequest {
key : Bytes
range_end : Bytes
limit : Int64
revision : Int64
sort_order : SortOrder
sort_target : SortTarget
serializable : Bool
keys_only : Bool
count_only : Bool
min_mod_revision : Int64
max_mod_revision : Int64
min_create_revision : Int64
max_create_revision : Int64
} derive(Eq)pub(all) struct RangeResponse {
header : ResponseHeader
kvs : Array[KeyValue]
more : Bool
count : Int64
} derive(Eq)pub(all) enum RequestOp {
RequestRange(RangeRequest)
RequestPut(PutRequest)
RequestDeleteRange(DeleteRangeRequest)
RequestTxn(TxnRequest)
} derive(Eq)pub(all) struct ResponseHeader {
cluster_id : UInt64
member_id : UInt64
revision : Int64
raft_term : UInt64
} derive(Eq)pub(all) enum ResponseOp {
ResponseRange(RangeResponse)
ResponsePut(PutResponse)
ResponseDeleteRange(DeleteRangeResponse)
ResponseTxn(TxnResponse)
} derive(Eq)pub(all) struct SnapshotResponse {
header : ResponseHeader
blob : Bytes
remaining_bytes : Int64
} derive(Eq)pub(all) struct StatusResponse {
header : ResponseHeader
version : String
db_size : Int64
leader : UInt64
raft_index : UInt64
raft_term : UInt64
raft_applied_index : UInt64
errors : Array[Alarm]
db_size_in_use : Int64
} derive(Eq)pub(all) struct TlsConfig {
enabled : Bool
ca_cert : Bytes
cert : Bytes
key : Bytes
server_name : String
} derive(Eq)pub(all) struct TxnResponse {
header : ResponseHeader
succeeded : Bool
responses : Array[ResponseOp]
} derive(Eq)pub(all) struct WatchCreateRequest {
key : Bytes
range_end : Bytes
start_revision : Int64
progress_notify : Bool
filters : Array[FilterType]
prev_kv : Bool
watch_id : Int64
fragment : Bool
} derive(Eq)pub(all) enum WatchRequest {
CreateRequest(WatchCreateRequest)
CancelRequest(WatchCancelRequest)
ProgressRequest(WatchProgressRequest)
} derive(Eq)pub(all) struct WatchResponse {
header : ResponseHeader
watch_id : Int64
created : Bool
canceled : Bool
events : Array[Event]
compact_revision : Int64
cancel_reason : String
fragment : Bool
} derive(Eq)fn exact(key : Bytes) -> (Bytes, Bytes)fn has_prefix(key : Bytes, prefix : Bytes) -> Boolfn prefix(key : Bytes) -> Bytesfn prefix_range(key : Bytes) -> (Bytes, Bytes)