From ba50d7c62648471e6e7cf74afe14e9c3268a3a98 Mon Sep 17 00:00:00 2001 From: Sida Chen Date: Wed, 6 Mar 2019 16:29:31 -0500 Subject: [PATCH] pgsql: Move lock to lock module --- database/pgsql/{ => lock}/lock.go | 33 +++++----- database/pgsql/lock/lock_test.go | 100 ++++++++++++++++++++++++++++++ database/pgsql/lock_test.go | 99 ----------------------------- 3 files changed, 118 insertions(+), 114 deletions(-) rename database/pgsql/{ => lock}/lock.go (69%) create mode 100644 database/pgsql/lock/lock_test.go delete mode 100644 database/pgsql/lock_test.go diff --git a/database/pgsql/lock.go b/database/pgsql/lock/lock.go similarity index 69% rename from database/pgsql/lock.go rename to database/pgsql/lock/lock.go index 0fd73f5b..29a4abe1 100644 --- a/database/pgsql/lock.go +++ b/database/pgsql/lock/lock.go @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package pgsql +package lock import ( + "database/sql" "time" + "github.com/coreos/clair/database/pgsql/monitoring" + "github.com/coreos/clair/database/pgsql/util" log "github.com/sirupsen/logrus" ) @@ -38,12 +41,12 @@ const ( SELECT owner, until FROM lock WHERE name = $1` ) -func (tx *pgSession) AcquireLock(lockName, whoami string, desiredDuration time.Duration) (bool, time.Time, error) { +func AcquireLock(tx *sql.Tx, lockName, whoami string, desiredDuration time.Duration) (bool, time.Time, error) { if lockName == "" || whoami == "" || desiredDuration == 0 { panic("invalid lock parameters") } - if err := tx.pruneLocks(); err != nil { + if err := PruneLocks(tx); err != nil { return false, time.Time{}, err } @@ -54,22 +57,22 @@ func (tx *pgSession) AcquireLock(lockName, whoami string, desiredDuration time.D lockOwner string ) - defer observeQueryTime("Lock", "soiLock", time.Now()) + defer monitoring.ObserveQueryTime("Lock", "soiLock", time.Now()) err := tx.QueryRow(soiLock, lockName, whoami, desiredLockedUntil).Scan(&lockOwner, &lockedUntil) - return lockOwner == whoami, lockedUntil, err + return lockOwner == whoami, lockedUntil, util.HandleError("AcquireLock", err) } -func (tx *pgSession) ExtendLock(lockName, whoami string, desiredDuration time.Duration) (bool, time.Time, error) { +func ExtendLock(tx *sql.Tx, lockName, whoami string, desiredDuration time.Duration) (bool, time.Time, error) { if lockName == "" || whoami == "" || desiredDuration == 0 { panic("invalid lock parameters") } desiredLockedUntil := time.Now().Add(desiredDuration) - defer observeQueryTime("Lock", "update", time.Now()) + defer monitoring.ObserveQueryTime("Lock", "update", time.Now()) result, err := tx.Exec(updateLock, lockName, whoami, desiredLockedUntil) if err != nil { - return false, time.Time{}, handleError("updateLock", err) + return false, time.Time{}, util.HandleError("updateLock", err) } if numRows, err := result.RowsAffected(); err == nil { @@ -77,27 +80,27 @@ func (tx *pgSession) ExtendLock(lockName, whoami string, desiredDuration time.Du return numRows > 0, desiredLockedUntil, nil } - return false, time.Time{}, handleError("updateLock", err) + return false, time.Time{}, util.HandleError("updateLock", err) } -func (tx *pgSession) ReleaseLock(name, owner string) error { +func ReleaseLock(tx *sql.Tx, name, owner string) error { if name == "" || owner == "" { panic("invalid lock parameters") } - defer observeQueryTime("Unlock", "all", time.Now()) + defer monitoring.ObserveQueryTime("Unlock", "all", time.Now()) _, err := tx.Exec(removeLock, name, owner) return err } // pruneLocks removes every expired locks from the database -func (tx *pgSession) pruneLocks() error { - defer observeQueryTime("pruneLocks", "all", time.Now()) +func PruneLocks(tx *sql.Tx) error { + defer monitoring.ObserveQueryTime("pruneLocks", "all", time.Now()) if r, err := tx.Exec(removeLockExpired, time.Now().UTC()); err != nil { - return handleError("removeLockExpired", err) + return util.HandleError("removeLockExpired", err) } else if affected, err := r.RowsAffected(); err != nil { - return handleError("removeLockExpired", err) + return util.HandleError("removeLockExpired", err) } else { log.Debugf("Pruned %d Locks", affected) } diff --git a/database/pgsql/lock/lock_test.go b/database/pgsql/lock/lock_test.go new file mode 100644 index 00000000..356825e3 --- /dev/null +++ b/database/pgsql/lock/lock_test.go @@ -0,0 +1,100 @@ +// Copyright 2019 clair 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 lock + +import ( + "testing" + "time" + + "github.com/coreos/clair/database/pgsql/testutil" + "github.com/stretchr/testify/require" +) + +func TestAcquireLockReturnsExistingLockDuration(t *testing.T) { + tx, cleanup := testutil.CreateTestTxWithFixtures(t, "Lock") + defer cleanup() + + acquired, originalExpiration, err := AcquireLock(tx, "test1", "owner1", time.Minute) + require.Nil(t, err) + require.True(t, acquired) + + acquired2, expiration, err := AcquireLock(tx, "test1", "owner2", time.Hour) + require.Nil(t, err) + require.False(t, acquired2) + require.Equal(t, expiration, originalExpiration) +} + +func TestLock(t *testing.T) { + db, cleanup := testutil.CreateTestDBWithFixture(t, "Lock") + defer cleanup() + + tx, err := db.Begin() + if err != nil { + panic(err) + } + + // Create a first lock. + l, _, err := AcquireLock(tx, "test1", "owner1", time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, true) + + // lock again by itself, the previous lock is not expired yet. + l, _, err = AcquireLock(tx, "test1", "owner1", time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, false) + + // Try to renew the same lock with another owner. + l, _, err = ExtendLock(tx, "test1", "owner2", time.Minute) + require.Nil(t, err) + require.False(t, l) + tx = testutil.RestartTransaction(db, tx, false) + + l, _, err = AcquireLock(tx, "test1", "owner2", time.Minute) + require.Nil(t, err) + require.False(t, l) + tx = testutil.RestartTransaction(db, tx, false) + + // Renew the lock. + l, _, err = ExtendLock(tx, "test1", "owner1", 2*time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, true) + + // Unlock and then relock by someone else. + err = ReleaseLock(tx, "test1", "owner1") + require.Nil(t, err) + tx = testutil.RestartTransaction(db, tx, true) + + l, _, err = AcquireLock(tx, "test1", "owner2", time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, true) + + // Create a second lock which is actually already expired ... + l, _, err = AcquireLock(tx, "test2", "owner1", -time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, true) + + // Take over the lock + l, _, err = AcquireLock(tx, "test2", "owner2", time.Minute) + require.Nil(t, err) + require.True(t, l) + tx = testutil.RestartTransaction(db, tx, true) + + require.Nil(t, tx.Rollback()) +} diff --git a/database/pgsql/lock_test.go b/database/pgsql/lock_test.go deleted file mode 100644 index 538961b6..00000000 --- a/database/pgsql/lock_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019 clair 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 pgsql - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAcquireLockReturnsExistingLockDuration(t *testing.T) { - tx, cleanup := createTestPgSessionWithFixtures(t, "Lock") - defer cleanup() - - acquired, originalExpiration, err := tx.AcquireLock("test1", "owner1", time.Minute) - require.Nil(t, err) - require.True(t, acquired) - - acquired2, expiration, err := tx.AcquireLock("test1", "owner2", time.Hour) - require.Nil(t, err) - require.False(t, acquired2) - require.Equal(t, expiration, originalExpiration) -} - -func TestLock(t *testing.T) { - datastore, tx := openSessionForTest(t, "Lock", true) - defer datastore.Close() - - var l bool - - // Create a first lock. - l, _, err := tx.AcquireLock("test1", "owner1", time.Minute) - assert.Nil(t, err) - assert.True(t, l) - tx = restartSession(t, datastore, tx, true) - - // lock again by itself, the previous lock is not expired yet. - l, _, err = tx.AcquireLock("test1", "owner1", time.Minute) - assert.Nil(t, err) - assert.True(t, l) // acquire lock no-op when owner already has the lock. - tx = restartSession(t, datastore, tx, false) - - // Try to renew the same lock with another owner. - l, _, err = tx.ExtendLock("test1", "owner2", time.Minute) - assert.Nil(t, err) - assert.False(t, l) - tx = restartSession(t, datastore, tx, false) - - l, _, err = tx.AcquireLock("test1", "owner2", time.Minute) - assert.Nil(t, err) - assert.False(t, l) - tx = restartSession(t, datastore, tx, false) - - // Renew the lock. - l, _, err = tx.ExtendLock("test1", "owner1", 2*time.Minute) - assert.Nil(t, err) - assert.True(t, l) - tx = restartSession(t, datastore, tx, true) - - // Unlock and then relock by someone else. - err = tx.ReleaseLock("test1", "owner1") - assert.Nil(t, err) - tx = restartSession(t, datastore, tx, true) - - l, _, err = tx.AcquireLock("test1", "owner2", time.Minute) - assert.Nil(t, err) - assert.True(t, l) - tx = restartSession(t, datastore, tx, true) - - // Create a second lock which is actually already expired ... - l, _, err = tx.AcquireLock("test2", "owner1", -time.Minute) - assert.Nil(t, err) - assert.True(t, l) - tx = restartSession(t, datastore, tx, true) - - // Take over the lock - l, _, err = tx.AcquireLock("test2", "owner2", time.Minute) - assert.Nil(t, err) - assert.True(t, l) - tx = restartSession(t, datastore, tx, true) - - if !assert.Nil(t, tx.Rollback()) { - t.FailNow() - } -}