perf(spanner/spannertest): speed up no-wait DDL changes
spannertest simulates DDL application delays but skips the delay if
there is a waiter. Improve that mechanism so a waiter arriving during
the delay will immediately cancel the delay.
This speeds up the local execution time of spannertest's own tests from
~5s to ~3s.
@@ -55,6 +55,7 @@ import (
"net"
"strconv"
"sync"
+ "sync/atomic"
"time"
"github.com/golang/protobuf/proto"
@@ -147,7 +148,24 @@ func timestampProto(t time.Time) *timestamppb.Timestamp {
type lro struct {
mu sync.Mutex
state *lropb.Operation
- waiters bool // Whether anyone appears to be waiting.
+
+ // waitc is closed when anyone starts waiting on the LRO.
+ // waitatom is CAS'd from 0 to 1 to make that closing safe.
+ waitc chan struct{}
+ waitatom int32
+}
+
+func newLRO(initState *lropb.Operation) *lro {
+ return &lro{
+ state: initState,
+ waitc: make(chan struct{}),
+ }
+}
+
+func (l *lro) noWait() {
+ if atomic.CompareAndSwapInt32(&l.waitatom, 0, 1) {
+ close(l.waitc)
+ }
}
func (l *lro) State() *lropb.Operation {
@@ -229,9 +247,7 @@ func (s *server) GetOperation(ctx context.Context, req *lropb.GetOperationReques
}
// Someone is waiting on this LRO. Disable sleeping in its Run method.
- lro.mu.Lock()
- lro.waiters = true
- lro.mu.Unlock()
+ lro.noWait()
return lro.State(), nil
}
@@ -276,11 +292,7 @@ func (s *server) UpdateDatabaseDdl(ctx context.Context, req *adminpb.UpdateDatab
// Nothing should be depending on the exact structure of this,
// but it is specified in google/spanner/admin/database/v1/spanner_database_admin.proto.
id := "projects/fake-proj/instances/fake-instance/databases/fake-db/operations/" + genRandomOperation()
- lro := &lro{
- state: &lropb.Operation{
- Name: id,
- },
- }
+ lro := newLRO(&lropb.Operation{Name: id})
s.mu.Lock()
s.lros[id] = lro
s.mu.Unlock()
@@ -293,12 +305,10 @@ func (l *lro) Run(s *server, stmts []spansql.DDLStmt) {
ctx := context.Background()
for _, stmt := range stmts {
- l.mu.Lock()
- waiters := l.waiters
- l.mu.Unlock()
- if !waiters {
// Simulate delayed DDL application, but only if nobody is waiting.
- time.Sleep(100 * time.Millisecond)
+ select {
+ case <-time.After(100 * time.Millisecond):
+ case <-l.waitc:
}
if st := s.runOneDDL(ctx, stmt); st.Code() != codes.OK {
