Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

storage/spanlatch: create spanlatch.Manager using immutable btrees #31997

Merged
merged 7 commits into from
Nov 29, 2018
65 changes: 39 additions & 26 deletions pkg/storage/command_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/util/hlc"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
)

var zeroTS = hlc.Timestamp{}
Expand Down Expand Up @@ -805,13 +806,14 @@ func assertExpectedPrereqs(
}
}

func BenchmarkCommandQueueGetPrereqsAllReadOnly(b *testing.B) {
func BenchmarkCommandQueueReadOnlyMix(b *testing.B) {
// Test read-only getPrereqs performance for various number of command queue
// entries. See #13627 where a previous implementation of
// CommandQueue.getOverlaps had O(n) performance in this setup. Since reads
// do not wait on other reads, expected performance is O(1).
for _, size := range []int{1, 4, 16, 64, 128, 256} {
b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
var mu syncutil.Mutex
cq := NewCommandQueue(true)
spans := []roachpb.Span{{
Key: roachpb.Key("aaaaaaaaaa"),
Expand All @@ -823,7 +825,10 @@ func BenchmarkCommandQueueGetPrereqsAllReadOnly(b *testing.B) {

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = cq.getPrereqs(true, zeroTS, spans)
mu.Lock()
prereqs := cq.getPrereqs(true, zeroTS, spans)
cq.add(true, zeroTS, prereqs, spans)
mu.Unlock()
}
})
}
Expand All @@ -833,32 +838,40 @@ func BenchmarkCommandQueueReadWriteMix(b *testing.B) {
// Test performance with a mixture of reads and writes with a high number
// of reads per write.
// See #15544.
for _, readsPerWrite := range []int{1, 4, 16, 64, 128, 256} {
for _, readsPerWrite := range []int{0, 1, 4, 16, 64, 128, 256} {
b.Run(fmt.Sprintf("readsPerWrite=%d", readsPerWrite), func(b *testing.B) {
for i := 0; i < b.N; i++ {
totalCmds := 1 << 10
liveCmdQueue := make(chan *cmd, 16)
cq := NewCommandQueue(true /* coveringOptimization */)
for j := 0; j < totalCmds; j++ {
a, b := randBytes(100), randBytes(100)
// Overwrite first byte so that we do not mix local and global ranges
a[0], b[0] = 'a', 'a'
if bytes.Compare(a, b) > 0 {
a, b = b, a
}
spans := []roachpb.Span{{
Key: roachpb.Key(a),
EndKey: roachpb.Key(b),
}}
var cmd *cmd
readOnly := j%(readsPerWrite+1) != 0
prereqs := cq.getPrereqs(readOnly, zeroTS, spans)
cmd = cq.add(readOnly, zeroTS, prereqs, spans)
if len(liveCmdQueue) == cap(liveCmdQueue) {
cq.remove(<-liveCmdQueue)
}
liveCmdQueue <- cmd
var mu syncutil.Mutex
cq := NewCommandQueue(true /* coveringOptimization */)
liveCmdQueue := make(chan *cmd, 16)

spans := make([][]roachpb.Span, b.N)
for i := range spans {
a, b := randBytes(100), randBytes(100)
// Overwrite first byte so that we do not mix local and global ranges
a[0], b[0] = 'a', 'a'
if bytes.Compare(a, b) > 0 {
a, b = b, a
}
spans[i] = []roachpb.Span{{
Key: roachpb.Key(a),
EndKey: roachpb.Key(b),
}}
}

b.ResetTimer()
for i := range spans {
mu.Lock()
readOnly := i%(readsPerWrite+1) != 0
prereqs := cq.getPrereqs(readOnly, zeroTS, spans[i])
cmd := cq.add(readOnly, zeroTS, prereqs, spans[i])
mu.Unlock()

if len(liveCmdQueue) == cap(liveCmdQueue) {
mu.Lock()
cq.remove(<-liveCmdQueue)
mu.Unlock()
}
liveCmdQueue <- cmd
}
})
}
Expand Down
42 changes: 42 additions & 0 deletions pkg/storage/spanlatch/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

/*
Package spanlatch provides a latch management structure for serializing access
to keys and key ranges. Latch acquitions affecting keys or key ranges must wait
on already-acquired latches which overlap their key range to be released.

The evolution of complexity can best be understood as a series of incremental
changes, each in the name of increased lock granularity to reduce contention and
enable more concurrency between requests. The structure can trace its lineage
back to a simple sync.Mutex. From there, the structure evolved through the
following progression:

* The structure began by enforcing strict mutual exclusion for access to any
keys. Conceptually, it was a sync.Mutex.
* Concurrent read-only access to keys and key ranges was permitted. Read and
writes were serialized with each other, writes were serialized with each other,
but no ordering was enforced between reads. Conceptually, the structure became
a sync.RWMutex.
* The structure became key range-aware and concurrent access to non-overlapping
key ranges was permitted. Conceptually, the structure became an interval
tree of sync.RWMutexes.
* The structure became timestamp-aware and concurrent access of non-causal
read and write pairs was permitted. The effect of this was that reads no
longer waited for writes at higher timestamps and writes no longer waited
for reads at lower timestamps. Conceptually, the structure became an interval
tree of timestamp-aware sync.RWMutexes.

*/
package spanlatch
Loading