blob: 8d0c595d1e43dae028dcd1936a2b2815a478ac7d [file] [log] [blame]
Abhay Kumar40252eb2025-10-13 13:25:53 +00001// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package clientv3
16
17import (
18 "context"
19
20 "google.golang.org/grpc"
21
22 pb "go.etcd.io/etcd/api/v3/etcdserverpb"
23 "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
24)
25
26type (
27 CompactResponse pb.CompactionResponse
28 PutResponse pb.PutResponse
29 GetResponse pb.RangeResponse
30 DeleteResponse pb.DeleteRangeResponse
31 TxnResponse pb.TxnResponse
32)
33
34type KV interface {
35 // Put puts a key-value pair into etcd.
36 // Note that key,value can be plain bytes array and string is
37 // an immutable representation of that bytes array.
38 // To get a string of bytes, do string([]byte{0x10, 0x20}).
39 Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)
40
41 // Get retrieves keys.
42 // By default, Get will return the value for "key", if any.
43 // When passed WithRange(end), Get will return the keys in the range [key, end).
44 // When passed WithFromKey(), Get returns keys greater than or equal to key.
45 // When passed WithRev(rev) with rev > 0, Get retrieves keys at the given revision;
46 // if the required revision is compacted, the request will fail with ErrCompacted .
47 // When passed WithLimit(limit), the number of returned keys is bounded by limit.
48 // When passed WithSort(), the keys will be sorted.
49 Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)
50
51 // Delete deletes a key, or optionally using WithRange(end), [key, end).
52 Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)
53
54 // Compact compacts etcd KV history before the given rev.
55 Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)
56
57 // Do applies a single Op on KV without a transaction.
58 // Do is useful when creating arbitrary operations to be issued at a
59 // later time; the user can range over the operations, calling Do to
60 // execute them. Get/Put/Delete, on the other hand, are best suited
61 // for when the operation should be issued at the time of declaration.
62 Do(ctx context.Context, op Op) (OpResponse, error)
63
64 // Txn creates a transaction.
65 Txn(ctx context.Context) Txn
66}
67
68type OpResponse struct {
69 put *PutResponse
70 get *GetResponse
71 del *DeleteResponse
72 txn *TxnResponse
73}
74
75func (op OpResponse) Put() *PutResponse { return op.put }
76func (op OpResponse) Get() *GetResponse { return op.get }
77func (op OpResponse) Del() *DeleteResponse { return op.del }
78func (op OpResponse) Txn() *TxnResponse { return op.txn }
79
80func (resp *PutResponse) OpResponse() OpResponse {
81 return OpResponse{put: resp}
82}
83
84func (resp *GetResponse) OpResponse() OpResponse {
85 return OpResponse{get: resp}
86}
87
88func (resp *DeleteResponse) OpResponse() OpResponse {
89 return OpResponse{del: resp}
90}
91
92func (resp *TxnResponse) OpResponse() OpResponse {
93 return OpResponse{txn: resp}
94}
95
96type kv struct {
97 remote pb.KVClient
98 callOpts []grpc.CallOption
99}
100
101func NewKV(c *Client) KV {
102 api := &kv{remote: RetryKVClient(c)}
103 if c != nil {
104 api.callOpts = c.callOpts
105 }
106 return api
107}
108
109func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
110 api := &kv{remote: remote}
111 if c != nil {
112 api.callOpts = c.callOpts
113 }
114 return api
115}
116
117func (kv *kv) Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) {
118 r, err := kv.Do(ctx, OpPut(key, val, opts...))
119 return r.put, ContextError(ctx, err)
120}
121
122func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) {
123 r, err := kv.Do(ctx, OpGet(key, opts...))
124 return r.get, ContextError(ctx, err)
125}
126
127func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
128 r, err := kv.Do(ctx, OpDelete(key, opts...))
129 return r.del, ContextError(ctx, err)
130}
131
132func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
133 resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest(), kv.callOpts...)
134 if err != nil {
135 return nil, ContextError(ctx, err)
136 }
137 return (*CompactResponse)(resp), err
138}
139
140func (kv *kv) Txn(ctx context.Context) Txn {
141 return &txn{
142 kv: kv,
143 ctx: ctx,
144 callOpts: kv.callOpts,
145 }
146}
147
148func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) {
149 var err error
150 switch op.t {
151 case tRange:
152 if op.IsSortOptionValid() {
153 var resp *pb.RangeResponse
154 resp, err = kv.remote.Range(ctx, op.toRangeRequest(), kv.callOpts...)
155 if err == nil {
156 return OpResponse{get: (*GetResponse)(resp)}, nil
157 }
158 } else {
159 err = rpctypes.ErrInvalidSortOption
160 }
161 case tPut:
162 var resp *pb.PutResponse
163 r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
164 resp, err = kv.remote.Put(ctx, r, kv.callOpts...)
165 if err == nil {
166 return OpResponse{put: (*PutResponse)(resp)}, nil
167 }
168 case tDeleteRange:
169 var resp *pb.DeleteRangeResponse
170 r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
171 resp, err = kv.remote.DeleteRange(ctx, r, kv.callOpts...)
172 if err == nil {
173 return OpResponse{del: (*DeleteResponse)(resp)}, nil
174 }
175 case tTxn:
176 var resp *pb.TxnResponse
177 resp, err = kv.remote.Txn(ctx, op.toTxnRequest(), kv.callOpts...)
178 if err == nil {
179 return OpResponse{txn: (*TxnResponse)(resp)}, nil
180 }
181 default:
182 panic("Unknown op")
183 }
184 return OpResponse{}, ContextError(ctx, err)
185}