-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
locking.go
162 lines (145 loc) · 5.4 KB
/
locking.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
// Package lock provides type definitions for locking-related concepts used by
// concurrency control in the key-value layer.
package lock
import (
"fmt"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/isolation"
"github.com/cockroachdb/cockroach/pkg/settings"
"github.com/cockroachdb/cockroach/pkg/util/hlc"
"github.com/cockroachdb/errors"
)
// ExclusiveLocksBlockNonLockingReads dictates locking interactions between
// non-locking reads and exclusive locks. Configuring this setting to true makes
// it such that non-locking reads from serializable transactions block on
// exclusive locks held by serializable transactions if the read's timestamp is
// at or above the timestamp at which the lock is held. If set to false,
// non-locking reads do not block on exclusive locks, regardless of isolation
// level or timestamp.
//
// Note that the setting only applies if both the reader and lock holder are
// running with serializable isolation level. If either of them is running with
// weaker isolation levels, the setting has no effect. To understand why,
// consider the tradeoff this setting presents -- the tradeoff here is increased
// concurrency (non-locking reads become non-blocking in the face of Exclusive
// locks) at the expense of forcing Exclusive lock holders to perform a read
// refresh (to prevent write skew), which in turn may force them to restart if
// the refresh fails.
//
// If the lock holder is running at a weaker isolation level (snapshot,
// read committed), then it is able to tolerate write skew. Thus, there is no
// tradeoff -- it is always a good idea to allow any non-locking read to
// proceed. On the other hand, non-locking reads running at weaker isolation
// levels should never block on exclusive locks.
var ExclusiveLocksBlockNonLockingReads = settings.RegisterBoolSetting(
settings.SystemOnly,
"kv.lock.exclusive_locks_block_non_locking_reads.enabled",
"dictates the locking interactions between exclusive locks and non-locking reads",
true,
)
// MaxDurability is the maximum value in the Durability enum.
const MaxDurability = Unreplicated
func init() {
for v := range Durability_name {
if d := Durability(v); d > MaxDurability {
panic(fmt.Sprintf("Durability (%s) with value larger than MaxDurability", d))
}
}
}
// Conflicts returns whether the supplied lock modes conflict with each other.
// Conflict rules are as described in the compatibility matrix in locking.pb.go.
func Conflicts(m1 *Mode, m2 *Mode, sv *settings.Values) bool {
if m1.Empty() || m2.Empty() {
panic("cannot check conflict for uninitialized locks")
}
if m1.Strength > m2.Strength {
// Conflict rules are symmetric, so reduce the number of cases we need to
// handle.
m1, m2 = m2, m1
}
switch m1.Strength {
case None:
switch m2.Strength {
case None, Shared, Update:
return false
case Exclusive:
// Non-locking reads only conflict with Exclusive locks if the following
// conditions hold:
// 1. both the non-locking read and the Exclusive lock belong to
// transactions that cannot tolerate write skew.
// 2. AND the non-locking read is reading at a timestamp above the lock.
// 3. AND the ExclusiveLocksBlockNonLockingReads cluster setting is
// configured to do as much.
if !m1.IsoLevel.ToleratesWriteSkew() &&
!m2.IsoLevel.ToleratesWriteSkew() && ExclusiveLocksBlockNonLockingReads.Get(sv) {
return !m1.Timestamp.Less(m2.Timestamp)
}
return false
case Intent:
return !m1.Timestamp.Less(m2.Timestamp) // non-locking read above the intent's timestamp
default:
panic(errors.AssertionFailedf("unknown strength: %s", m2.Strength))
}
case Shared:
return m2.Strength == Exclusive || m2.Strength == Intent
case Update, Exclusive, Intent:
return true
default:
panic(errors.AssertionFailedf("unknown strength: %s", m1.Strength))
}
}
// Empty returns true if m is an empty (uninitialized) lock Mode.
func (m *Mode) Empty() bool {
return m.Strength == None && m.Timestamp.IsEmpty()
}
// MakeModeNone constructs a Mode with strength None.
func MakeModeNone(ts hlc.Timestamp, isoLevel isolation.Level) Mode {
return Mode{
Strength: None,
Timestamp: ts,
IsoLevel: isoLevel,
}
}
// MakeModeShared constructs a Mode with strength Shared.
func MakeModeShared() Mode {
return Mode{
Strength: Shared,
}
}
// MakeModeUpdate constructs a Mode with strength Update.
func MakeModeUpdate() Mode {
return Mode{
Strength: Update,
}
}
// MakeModeExclusive constructs a Mode with strength Exclusive.
func MakeModeExclusive(ts hlc.Timestamp, isoLevel isolation.Level) Mode {
return Mode{
Strength: Exclusive,
Timestamp: ts,
IsoLevel: isoLevel,
}
}
// MakeModeIntent constructs a Mode with strength Intent.
func MakeModeIntent(ts hlc.Timestamp) Mode {
return Mode{
Strength: Intent,
Timestamp: ts,
}
}
// SafeValue implements redact.SafeValue.
func (Strength) SafeValue() {}
// SafeValue implements redact.SafeValue.
func (Mode) SafeValue() {}
// SafeValue implements redact.SafeValue.
func (Durability) SafeValue() {}
// SafeValue implements redact.SafeValue.
func (WaitPolicy) SafeValue() {}