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  })