carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "sync"
5 "testing"
6 "time"
7)
8
9// The property the walks reason from: two holders of one store's lock never
10// run at once. Asserted by counting concurrent occupancy rather than by
11// timing, so the test fails on a broken lock rather than on a slow machine.
12func TestStoreWalkLocksExcludePerApp(t *testing.T) {
13 l := newStoreWalkLocks()
14 var mu sync.Mutex
15 live, maxLive := 0, 0
16
17 var wg sync.WaitGroup
18 for i := 0; i < 8; i++ {
19 wg.Add(1)
20 go func() {
21 defer wg.Done()
22 defer l.lock("bab", "titogo", "")()
23 mu.Lock()
24 live++
25 if live > maxLive {
26 maxLive = live
27 }
28 mu.Unlock()
29 time.Sleep(time.Millisecond)
30 mu.Lock()
31 live--
32 mu.Unlock()
33 }()
34 }
35 wg.Wait()
36 if maxLive != 1 {
37 t.Fatalf("max concurrent holders of one app's walk lock = %d, want 1", maxLive)
38 }
39}
40
41// Two apps share no estate — one bucket and one IAM user each, both named for
42// the (account, app) pair — so their walks must not queue behind each other.
43func TestStoreWalkLocksDoNotSerialiseAcrossApps(t *testing.T) {
44 l := newStoreWalkLocks()
45 release := l.lock("bab", "titogo", "")
46 defer release()
47
48 done := make(chan struct{})
49 go func() {
50 l.lock("bab", "other", "")()
51 close(done)
52 }()
53 select {
54 case <-done:
55 case <-time.After(2 * time.Second):
56 t.Fatal("a walk on another app blocked behind this one's lock")
57 }
58}
59
60// Two STORES of one app share no estate either (2026-09-19): a bucket, an IAM
61// user and a config bundle each, all named for the (account, app,
62// environment) triple. An environment store's walk must not queue behind the
63// app store's, or behind another environment's.
64func TestStoreWalkLocksDoNotSerialiseAcrossEnvironments(t *testing.T) {
65 l := newStoreWalkLocks()
66 release := l.lock("bab", "titogo", "")
67 defer release()
68
69 done := make(chan struct{})
70 go func() {
71 l.lock("bab", "titogo", "beta")()
72 l.lock("bab", "titogo", "alpha")()
73 close(done)
74 }()
75 select {
76 case <-done:
77 case <-time.After(2 * time.Second):
78 t.Fatal("a walk on another store of the same app blocked behind this one's lock")
79 }
80}
81
82// An app walked once must not hold a mutex for the life of the process.
83func TestStoreWalkLocksDropEntriesAtZero(t *testing.T) {
84 l := newStoreWalkLocks()
85 l.lock("bab", "titogo", "")()
86
87 l.mu.Lock()
88 n := len(l.m)
89 l.mu.Unlock()
90 if n != 0 {
91 t.Fatalf("locks map holds %d entries after the last release, want 0", n)
92 }
93}
94
95// Holding one app's lock while a second waiter is queued must keep the entry
96// alive — dropping it at the wrong moment would hand the waiter a different
97// mutex and silently defeat the exclusion above.
98func TestStoreWalkLocksKeepEntryWhileWaitersQueue(t *testing.T) {
99 l := newStoreWalkLocks()
100 first := l.lock("bab", "titogo", "")
101
102 queued := make(chan func(), 1)
103 go func() { queued <- l.lock("bab", "titogo", "") }()
104
105 // Give the waiter time to register its reference before we look.
106 deadline := time.Now().Add(2 * time.Second)
107 for {
108 l.mu.Lock()
109 e := l.m[storeWalkKey("bab", "titogo", "")]
110 refs := 0
111 if e != nil {
112 refs = e.refs
113 }
114 l.mu.Unlock()
115 if refs == 2 {
116 break
117 }
118 if time.Now().After(deadline) {
119 t.Fatalf("waiter never registered: refs = %d", refs)
120 }
121 time.Sleep(time.Millisecond)
122 }
123
124 first()
125 (<-queued)()
126
127 l.mu.Lock()
128 n := len(l.m)
129 l.mu.Unlock()
130 if n != 0 {
131 t.Fatalf("locks map holds %d entries after both released, want 0", n)
132 }
133}
134
135// Except where the names collide (#218): app "shop" with environment "eu" and
136// app "shop-eu" resolve to one bucket and one IAM user, so their walks must
137// queue. The name claims refuse the second grant either way; the lock keeps
138// the two from interleaving their reads of each other's records.
139func TestStoreWalkLocksSerialiseCollidingStores(t *testing.T) {
140 l := newStoreWalkLocks()
141 release := l.lock("bab", "shop-eu", "")
142
143 acquired := make(chan struct{})
144 go func() {
145 l.lock("bab", "shop", "eu")()
146 close(acquired)
147 }()
148 select {
149 case <-acquired:
150 t.Fatal("a walk on a store whose names collide with this one's did not queue behind its lock")
151 case <-time.After(50 * time.Millisecond):
152 }
153 release()
154 select {
155 case <-acquired:
156 case <-time.After(2 * time.Second):
157 t.Fatal("the queued walk never got the lock")
158 }
159}
160