batch.go RawHistoryFolder
1// SPDX-License-Identifier: MIT
2
3package kv
4
5// Batch is an ordered set of mutations and preconditions that Apply commits
6// atomically. The zero value is ready to use. A Batch must not be modified
7// while Apply runs and is not safe for concurrent use.
8//
9// Batch methods do not return errors: the batch records its first error and
10// Apply returns it.
11type Batch struct {
12 ops []op
13 pre []precond
14 opsBytes int // encoded size of ops
15 err error
16}
17
18// precond is a precondition checked by the writer before any op.
19type precond struct {
20 key []byte
21 absent bool // the key must not exist
22 version uint64 // otherwise the key must exist at this version
23}
24
25// Put sets key to value. It copies key and value.
26func (b *Batch) Put(key, value []byte) {
27 if !b.validKey(key) {
28 return
29 }
30 // One allocation holds both; the tree retains the slices, so they are
31 // never reused (not even by Reset).
32 buf := make([]byte, len(key)+len(value))
33 n := copy(buf, key)
34 copy(buf[n:], value)
35 b.add(op{kind: opPut, key: buf[:n:n], value: buf[n:len(buf):len(buf)]})
36}
37
38// Delete removes key. It copies key. Deleting a missing key is not an error.
39func (b *Batch) Delete(key []byte) {
40 if !b.validKey(key) {
41 return
42 }
43 b.add(op{kind: opDelete, key: append([]byte(nil), key...)})
44}
45
46// RequireAbsent adds the precondition that key does not exist. If it does
47// not hold when the batch is committed, Apply returns ErrConflict.
48func (b *Batch) RequireAbsent(key []byte) {
49 if !b.validKey(key) {
50 return
51 }
52 b.pre = append(b.pre, precond{key: append([]byte(nil), key...), absent: true})
53}
54
55// RequireVersion adds the precondition that key exists at version v, the seq
56// of the batch that last wrote it. v must be greater than zero, otherwise the
57// batch records ErrInvalidKey.
58func (b *Batch) RequireVersion(key []byte, v uint64) {
59 if !b.validKey(key) {
60 return
61 }
62 if v == 0 {
63 b.fail(ErrInvalidKey)
64 return
65 }
66 b.pre = append(b.pre, precond{key: append([]byte(nil), key...), version: v})
67}
68
69// Len returns the number of ops and preconditions.
70func (b *Batch) Len() int { return len(b.ops) + len(b.pre) }
71
72// Reset empties the batch, clears its error and keeps allocated capacity for
73// the op list.
74func (b *Batch) Reset() {
75 clear(b.ops)
76 b.ops = b.ops[:0]
77 clear(b.pre)
78 b.pre = b.pre[:0]
79 b.opsBytes = 0
80 b.err = nil
81}
82
83// encodedSize returns the size of the batch in a log record (op count and
84// ops), which MaxBatchBytes limits; 0 for a batch without ops, which writes
85// nothing.
86func (b *Batch) encodedSize() int {
87 if len(b.ops) == 0 {
88 return 0
89 }
90 return batchHeader + b.opsBytes
91}
92
93func (b *Batch) add(o op) {
94 b.ops = append(b.ops, o)
95 b.opsBytes += opSize(o)
96}
97
98func (b *Batch) fail(err error) {
99 if b.err == nil {
100 b.err = err
101 }
102}
103
104// validKey records ErrInvalidKey and reports false for an invalid key.
105func (b *Batch) validKey(key []byte) bool {
106 if len(key) == 0 || len(key) > maxKeyLen {
107 b.fail(ErrInvalidKey)
108 return false
109 }
110 return b.err == nil
111}