github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/lock/lock_test.go (about) 1 package lock_test 2 3 import ( 4 "sync" 5 "time" 6 7 "code.cloudfoundry.org/lager" 8 9 "code.cloudfoundry.org/lager/lagertest" 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/db/lock" 13 "github.com/pf-qiu/concourse/v6/atc/db/lock/lockfakes" 14 "github.com/lib/pq" 15 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 var _ = Describe("Locks", func() { 21 var ( 22 listener *pq.Listener 23 lockFactory lock.LockFactory 24 25 dbLock lock.Lock 26 27 dbConn db.Conn 28 29 team db.Team 30 teamFactory db.TeamFactory 31 32 logger *lagertest.TestLogger 33 fakeLogFunc = func(logger lager.Logger, id lock.LockID) {} 34 ) 35 36 BeforeEach(func() { 37 postgresRunner.Truncate() 38 39 listener = pq.NewListener(postgresRunner.DataSourceName(), time.Second, time.Minute, nil) 40 Eventually(listener.Ping, 5*time.Second).ShouldNot(HaveOccurred()) 41 42 logger = lagertest.NewTestLogger("test") 43 44 lockFactory = lock.NewLockFactory(postgresRunner.OpenSingleton(), fakeLogFunc, fakeLogFunc) 45 46 dbConn = postgresRunner.OpenConn() 47 teamFactory = db.NewTeamFactory(dbConn, lockFactory) 48 49 var err error 50 team, err = teamFactory.CreateTeam(atc.Team{Name: "team-name"}) 51 Expect(err).NotTo(HaveOccurred()) 52 }) 53 54 AfterEach(func() { 55 err := dbConn.Close() 56 Expect(err).NotTo(HaveOccurred()) 57 58 err = listener.Close() 59 Expect(err).NotTo(HaveOccurred()) 60 61 if dbLock != nil { 62 _ = dbLock.Release() 63 } 64 }) 65 66 Describe("locks in general", func() { 67 It("Acquire can only obtain lock once", func() { 68 var acquired bool 69 var err error 70 dbLock, acquired, err = lockFactory.Acquire(logger, lock.LockID{42}) 71 Expect(err).NotTo(HaveOccurred()) 72 Expect(acquired).To(BeTrue()) 73 74 _, acquired, err = lockFactory.Acquire(logger, lock.LockID{42}) 75 Expect(err).NotTo(HaveOccurred()) 76 Expect(acquired).To(BeFalse()) 77 }) 78 79 It("Acquire accepts list of ids", func() { 80 var acquired bool 81 var err error 82 dbLock, acquired, err = lockFactory.Acquire(logger, lock.LockID{42, 56}) 83 Expect(err).NotTo(HaveOccurred()) 84 Expect(acquired).To(BeTrue()) 85 86 Consistently(func() error { 87 connCount := 3 88 89 var anyError error 90 var wg sync.WaitGroup 91 wg.Add(connCount) 92 93 for i := 0; i < connCount; i++ { 94 go func() { 95 defer wg.Done() 96 97 _, _, err := lockFactory.Acquire(logger, lock.LockID{42, 56}) 98 if err != nil { 99 anyError = err 100 } 101 102 }() 103 } 104 105 wg.Wait() 106 107 return anyError 108 }, 1500*time.Millisecond, 100*time.Millisecond).ShouldNot(HaveOccurred()) 109 110 dbLock, acquired, err = lockFactory.Acquire(logger, lock.LockID{56, 42}) 111 Expect(err).NotTo(HaveOccurred()) 112 Expect(acquired).To(BeTrue()) 113 114 _, acquired, err = lockFactory.Acquire(logger, lock.LockID{56, 42}) 115 Expect(err).NotTo(HaveOccurred()) 116 Expect(acquired).To(BeFalse()) 117 }) 118 119 Context("when another connection is holding the lock", func() { 120 var lockFactory2 lock.LockFactory 121 122 BeforeEach(func() { 123 lockFactory2 = lock.NewLockFactory(postgresRunner.OpenSingleton(), fakeLogFunc, fakeLogFunc) 124 }) 125 126 It("does not acquire the lock", func() { 127 var acquired bool 128 var err error 129 dbLock, acquired, err = lockFactory.Acquire(logger, lock.LockID{42}) 130 Expect(err).NotTo(HaveOccurred()) 131 Expect(acquired).To(BeTrue()) 132 133 _, acquired, err = lockFactory2.Acquire(logger, lock.LockID{42}) 134 Expect(err).NotTo(HaveOccurred()) 135 Expect(acquired).To(BeFalse()) 136 137 err = dbLock.Release() 138 Expect(err).NotTo(HaveOccurred()) 139 }) 140 141 It("acquires the locks once it is released", func() { 142 var acquired bool 143 var err error 144 dbLock, acquired, err = lockFactory.Acquire(logger, lock.LockID{42}) 145 Expect(err).NotTo(HaveOccurred()) 146 Expect(acquired).To(BeTrue()) 147 148 dbLock2, acquired, err := lockFactory2.Acquire(logger, lock.LockID{42}) 149 Expect(err).NotTo(HaveOccurred()) 150 Expect(acquired).To(BeFalse()) 151 152 err = dbLock.Release() 153 Expect(err).NotTo(HaveOccurred()) 154 155 dbLock2, acquired, err = lockFactory2.Acquire(logger, lock.LockID{42}) 156 Expect(err).NotTo(HaveOccurred()) 157 Expect(acquired).To(BeTrue()) 158 159 err = dbLock2.Release() 160 Expect(err).NotTo(HaveOccurred()) 161 }) 162 }) 163 164 Context("when two locks are being acquired at the same time", func() { 165 var fakeLockDB *lockfakes.FakeLockDB 166 var acquiredLock2 chan struct{} 167 var lock2Err error 168 var lock2Acquired bool 169 var fakeLockFactory lock.LockFactory 170 171 BeforeEach(func() { 172 fakeLockDB = new(lockfakes.FakeLockDB) 173 fakeLockFactory = lock.NewTestLockFactory(fakeLockDB) 174 acquiredLock2 = make(chan struct{}) 175 176 called := false 177 readyToAcquire := make(chan struct{}) 178 179 fakeLockDB.AcquireStub = func(id lock.LockID) (bool, error) { 180 if !called { 181 called = true 182 183 go func() { 184 close(readyToAcquire) 185 _, lock2Acquired, lock2Err = fakeLockFactory.Acquire(logger, id) 186 close(acquiredLock2) 187 }() 188 189 <-readyToAcquire 190 } 191 192 return true, nil 193 } 194 }) 195 196 It("only acquires one of the locks", func() { 197 _, acquired, err := fakeLockFactory.Acquire(logger, lock.LockID{57}) 198 Expect(err).NotTo(HaveOccurred()) 199 Expect(acquired).To(BeTrue()) 200 201 <-acquiredLock2 202 203 Expect(lock2Err).NotTo(HaveOccurred()) 204 Expect(lock2Acquired).To(BeFalse()) 205 }) 206 207 Context("when locks are being created on different lock factory (different db conn)", func() { 208 var fakeLockFactory2 lock.LockFactory 209 210 BeforeEach(func() { 211 fakeLockFactory2 = lock.NewTestLockFactory(fakeLockDB) 212 213 called := false 214 readyToAcquire := make(chan struct{}) 215 216 fakeLockDB.AcquireStub = func(id lock.LockID) (bool, error) { 217 if !called { 218 called = true 219 220 go func() { 221 close(readyToAcquire) 222 _, lock2Acquired, lock2Err = fakeLockFactory2.Acquire(logger, id) 223 close(acquiredLock2) 224 }() 225 226 <-readyToAcquire 227 } 228 229 return true, nil 230 } 231 }) 232 233 It("allows to acquire both locks", func() { 234 _, acquired, err := fakeLockFactory.Acquire(logger, lock.LockID{57}) 235 Expect(err).NotTo(HaveOccurred()) 236 Expect(acquired).To(BeTrue()) 237 238 <-acquiredLock2 239 240 Expect(lock2Err).NotTo(HaveOccurred()) 241 Expect(lock2Acquired).To(BeTrue()) 242 }) 243 }) 244 }) 245 }) 246 247 Describe("taking out a lock on build tracking", func() { 248 var build db.Build 249 250 BeforeEach(func() { 251 var err error 252 build, err = team.CreateOneOffBuild() 253 Expect(err).NotTo(HaveOccurred()) 254 }) 255 256 It("gets and keeps the lock and stops others from getting it", func() { 257 lock, acquired, err := build.AcquireTrackingLock(logger, 1*time.Second) 258 Expect(err).NotTo(HaveOccurred()) 259 Expect(acquired).To(BeTrue()) 260 261 Consistently(func() bool { 262 _, acquired, err = build.AcquireTrackingLock(logger, 1*time.Second) 263 Expect(err).NotTo(HaveOccurred()) 264 265 return acquired 266 }, 1500*time.Millisecond, 100*time.Millisecond).Should(BeFalse()) 267 268 err = lock.Release() 269 Expect(err).NotTo(HaveOccurred()) 270 271 time.Sleep(time.Second) 272 273 newLock, acquired, err := build.AcquireTrackingLock(logger, 1*time.Second) 274 Expect(err).NotTo(HaveOccurred()) 275 Expect(acquired).To(BeTrue()) 276 277 err = newLock.Release() 278 Expect(err).NotTo(HaveOccurred()) 279 }) 280 }) 281 })