diff options
| author | Jonathan Amsterdam <jba@google.com> | 2020-06-02 09:32:40 -0400 |
|---|---|---|
| committer | Jonathan Amsterdam <jba@google.com> | 2020-06-04 02:18:45 +0000 |
| commit | bc42b7ce3cb16956272a77fb69c5e8700f0d8f08 (patch) | |
| tree | 26aa9149e37a43715f69680046f911f13d5ffa04 /internal/postgres/insert_module_test.go | |
| parent | ba4f3b3e6d7f4d52c6f2aa607dece5871a164027 (diff) | |
| download | go-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.go | 52 |
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) + } +} |
