The underlying connector has rotted due to gRPC breaking changes. Since usage has drop to near zero, no more development will happen for this gem. THIS GEM WILL NOT WORK EFFICIENTLY (OR POSSIBLY CORRECTLY) BEYOND RUBY 2.7
The Etcd v3 client library, provide high level functions for Etcd v3.
The full API document: https://xfers.github.io/xfers-etcd-client-ruby/
- GET:
get,get_all,get_prefix,get_prefix_count,get_range,get_range_count - PUT:
put,put_all,put_prefix,put_range - DELETE:
del,del_all,del_prefix,del_range - LEASE:
lease_grant,lease_revoke,lease_ttl,lease_keep_alive_once - WATCH:
watch,watch_forever,watch_prefix,watch_prefix_forever - TRANSACTION:
transaction - Distributed Lock:
Xfers::Etcd::Mutex
Create a new etcd client
conn = Xfers::Etcd::Client.new(endpoints: "http://127.0.0.2:2379")Simple get & put
conn.put("/config/token", "token_1")
token = conn.get("/config/token")
# token.key == "/config/token", token.value == "token_1"Get with prefix
conn.put("/personal_accounts/1", "value2")
conn.put("/personal_accounts/2", "value1")
conn.put("/personal_accounts/3", "value3")
conn.put("/biz_accounts/1", "biz_account1")
conn.get_prefix("/personal_accounts/", sort_target: :key, sort_order: :ascend).map { |kv| { key: kv.key, value: kv.value } }
# [
# {key: "/personal_accounts/1", value: "value2"},
# {key: "/personal_accounts/2", value: "value1"},
# {key: "/personal_accounts/3", value: "value3"},
# ]
conn.get_prefix("/personal_accounts/", sort_target: :key, sort_order: :descend).map { |kv| { key: kv.key, value: kv.value } }
# [
# {key: "/personal_accounts/3", value: "value3"},
# {key: "/personal_accounts/2", value: "value1"},
# {key: "/personal_accounts/1", value: "value2"},
# ]
conn.get_prefix("/personal_accounts/", sort_target: :value, sort_order: :ascend).map { |kv| { key: kv.key, value: kv.value } }
# [
# {key: "/personal_accounts/3", value: "value3"},
# {key: "/personal_accounts/1", value: "value2"},
# {key: "/personal_accounts/2", value: "value1"},
# ]Get range
conn.put("/personal_accounts/1", "value2")
conn.put("/personal_accounts/2", "value1")
conn.put("/personal_accounts/3", "value3")
conn.put("/personal_accounts/4", "value4")
conn.get_range("/personal_accounts/1", "/personal_accounts/4", sort_order: :ascend).map { |kv| { key: kv.key, value: kv.value } }
# [
# {key: "/personal_accounts/1", value: "value2"},
# {key: "/personal_accounts/2", value: "value1"},
# {key: "/personal_accounts/3", value: "value3"},
# ]Put with TTL
# put a key which will expire after 10 seconds
conn.put("/connections/1/tx_start_at", Time.now, ttl: 10)
sleep(11)
# data == nil after 11 seconds
data = conn.get("/connections/1/tx_start_at")Watch a key
# Watcher
# will print the following messages:
#
# new value: test
# /models/bank_account/updated_at delete
# new value: test2
# /models/bank_account/updated_at delete
conn.watch_forever("/models/bank_account/updated_at") do |events|
events.each do |event|
case event.type
when :PUT
puts "new value: #{event.kv.value}"
when :DELETE
puts "#{event.kv.key} deleted"
end
end
end# Notifier
conn.put("/models/bank_account/updated_at", "test")
conn.delete("/models/bank_account/updated_at")
conn.put("/models/bank_account/updated_at", "test2", ttl: 3)Mutex lock
# create a new mutex instance with 10 seconds TTL
mutex = conn.mutex_new("jobs/1", ttl: 10)
# try to lock this mutex, will fail if it can't acquire lock after 2 second
mutex.lock(2) do |mu|
# critical code
# ...
# refresh TTL of lock to avoid expire
mu.refresh
# critical code
# ...
end
# will unlock automatically when block finished
# can re-lock again
mutex.try_lock do |mu|
# will be executed
endmutex = conn.mutex_new("jobs/1", ttl: 10)
mutex.lock
mutex.lock(2) do |mu|
# will not be executed, because the TTL is 10 second, and the lock will
# fail and return after 2 seconds
end
mutex.lock(10) do |mu|
# will be executed, because the TTL is 10 second, and the lock wait 10
# seconds, and the previous lock wait 2 seconds, so it can lock successfully
endTransaction
This sample code shows how to put the whole balance update procedures in transaction and makes the operation atomically.
balanceKv = conn.get("balance")
remaining_balance = balanceKv.value.to_d - 1000
txn_result = conn.transaction do
response = conn.transaction do |txn|
# compare the last modification revision to make sure the balanceKv didn't modified by others
txn.compare = [
txn.mod_revision("balance", :equal, balanceKv.mod_revision),
]
# set balance key to remaining_balance if compare success
txn.success = [
txn.put("balance", remaining_balance.to_s),
]
# return current balance if compare fail
txn.failure = [
txn.get("balance"),
]
end
if txn_result.succeeded
# the transaction success
else
# the transaction failed
current_balance = txn_result.responses[0].response_range.kvs[0].value.to_d
end
end