aboutsummaryrefslogtreecommitdiff
path: root/internal/postgres/insert_module_test.go
diff options
context:
space:
mode:
authorJonathan Amsterdam <jba@google.com>2020-06-02 09:32:40 -0400
committerJonathan Amsterdam <jba@google.com>2020-06-04 02:18:45 +0000
commitbc42b7ce3cb16956272a77fb69c5e8700f0d8f08 (patch)
tree26aa9149e37a43715f69680046f911f13d5ffa04 /internal/postgres/insert_module_test.go
parentba4f3b3e6d7f4d52c6f2aa607dece5871a164027 (diff)
downloadgo-x-pkgsite-bc42b7ce3cb16956272a77fb69c5e8700f0d8f08.tar.xz
internal/postgres: lock latest-version changes by module path
Use Postgres advisory locks (see https://www.postgresql.org/docs/11/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS) to prevent more than one transaction from thinking it is the latest version of a particular module. Change-Id: Ie928dac240b6e8679b41434b2433f89fcb2f49ae Reviewed-on: https://team-review.git.corp.google.com/c/golang/discovery/+/760100 CI-Result: Cloud Build <devtools-proctor-result-processor@system.gserviceaccount.com> Reviewed-by: Julie Qiu <julieqiu@google.com>
Diffstat (limited to 'internal/postgres/insert_module_test.go')
-rw-r--r--internal/postgres/insert_module_test.go52
1 files changed, 52 insertions, 0 deletions
diff --git a/internal/postgres/insert_module_test.go b/internal/postgres/insert_module_test.go
index 1b9532c6..8ef13359 100644
--- a/internal/postgres/insert_module_test.go
+++ b/internal/postgres/insert_module_test.go
@@ -10,12 +10,14 @@ import (
"errors"
"io/ioutil"
"path/filepath"
+ "sync"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"golang.org/x/pkgsite/internal"
+ "golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/experiment"
"golang.org/x/pkgsite/internal/licenses"
@@ -335,3 +337,53 @@ func TestMakeValidUnicode(t *testing.T) {
check("gin-gonic", true)
check("subchord", true)
}
+
+func TestLock(t *testing.T) {
+ // Verify that two transactions cannot both hold the same lock, but that every one
+ // that wants the lock eventually gets it.
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+ defer ResetTestDB(testDB, t)
+
+ db := testDB.Underlying()
+
+ const n = 4
+ errc := make(chan error)
+ var (
+ mu sync.Mutex
+ lockHeld bool
+ count int
+ )
+
+ for i := 0; i < n; i++ {
+ go func() {
+ errc <- db.Transact(ctx, sql.LevelDefault, func(tx *database.DB) error {
+ if err := lock(ctx, tx, sample.ModulePath); err != nil {
+ return err
+ }
+
+ mu.Lock()
+ h := lockHeld
+ lockHeld = true
+ count++
+ mu.Unlock()
+ if h {
+ return errors.New("lock already held")
+ }
+ time.Sleep(50 * time.Millisecond)
+ mu.Lock()
+ lockHeld = false
+ mu.Unlock()
+ return nil
+ })
+ }()
+ }
+ for i := 0; i < n; i++ {
+ if err := <-errc; err != nil {
+ t.Fatal(err)
+ }
+ }
+ if count != n {
+ t.Errorf("got %d, want %d", count, n)
+ }
+}