https://github.com/coreos/etcd/blob/master/Documentation/api.md
Atomic Compare-and-Swap
etcd can be used as a centralized coordination service in a cluster, and CompareAndSwap
(CAS) is the most basic operation used to build a distributed lock service.
This command will set the value of a key only if the client-provided conditions are equal to the current conditions.
The current comparable conditions are:
-
prevValue
- checks the previous value of the key. -
prevIndex
- checks the previous modifiedIndex of the key. -
prevExist
- checks existence of the key: ifprevExist
is true, it is anupdate
request; ifprevExist
isfalse
, it is acreate
request.
Here is a simple example. Let's create a key-value pair first: foo=one
.
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=one
Now let's try some invalid CompareAndSwap
commands.
Trying to set this existing key with prevExist=false
fails as expected:
curl http://127.0.0.1:2379/v2/keys/foo?prevExist=false -XPUT -d value=three
The error code explains the problem:
{ "cause": "/foo", "errorCode": 105, "index": 39776, "message": "Key already exists" }
Now let's provide a prevValue
parameter:
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=two -XPUT -d value=three
This will try to compare the previous value of the key and the previous value we provided. If they are equal, the value of the key will change to three.
{ "cause": "[two != one]", "errorCode": 101, "index": 8, "message": "Compare failed" }
which means CompareAndSwap
failed. cause
explains why the test failed. Note: the condition prevIndex=0 always passes.
Let's try a valid condition:
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=one -XPUT -d value=two
The response should be:
{ "action": "compareAndSwap", "node": { "createdIndex": 8, "key": "/foo", "modifiedIndex": 9, "value": "two" }, "prevNode": { "createdIndex": 8, "key": "/foo", "modifiedIndex": 8, "value": "one" } }
We successfully changed the value from "one" to "two" since we gave the correct previous value.
{"action":"set","node":{"key":"/foo","value":"one","modifiedIndex":27,"createdIndex":27}}
{"errorCode":105,"message":"Key already exists","cause":"/foo","index":27}
{"errorCode":101,"message":"Compare failed","cause":"[two != one]","index":27}
{"action":"compareAndSwap","node":{"key":"/foo","value":"two","modifiedIndex":28,"createdIndex":27},"prevNode":{"key":"/foo","value":"one","modifiedIndex":27,"createdIndex":27}}
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl ls / --recursive
/message
/workers
/workers/00000000000000000021
/workers/00000000000000000022
/workers/00000000000000000023
/foo
/locks
/locks/scec
/locks/scec/report
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report
/locks/scec/report: is a directory
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
Error: 100: Key not found (/locks/scec/report/order) [34]
192.168.1.10
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl get /locks/scec/report/order
Error: 100: Key not found (/locks/scec/report/order) [52]
[expire] /locks/scec/report/order
[set] /locks/scec/report/order
192.168.1.10
192.168.1.10
[root@ansible01 etcd-v2.2.0-linux-amd64]# ./etcdctl set /locks/scec/report/order "192.168.1.11"
192.168.1.11
[set] /locks/scec/report/order
192.168.1.11