From c95d71cf60808fec9904d5d31c93c8e373aed9c9 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Wed, 2 Dec 2020 13:58:33 -0800 Subject: [PATCH] content: include the staleness of the lock when tryLock() fails When multiple clients are pulling the same image, we may have this lock error. Short-lived locks are probably fine, but long-lived locks may indicate that containerd has some issues. Signed-off-by: Kazuyoshi Kato --- content/local/locks.go | 13 +++++++++---- content/local/locks_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 content/local/locks_test.go diff --git a/content/local/locks.go b/content/local/locks.go index bc3bd18e0..d1d2d564d 100644 --- a/content/local/locks.go +++ b/content/local/locks.go @@ -18,6 +18,7 @@ package local import ( "sync" + "time" "github.com/containerd/containerd/errdefs" "github.com/pkg/errors" @@ -25,9 +26,13 @@ import ( // Handles locking references +type lock struct { + since time.Time +} + var ( // locks lets us lock in process - locks = map[string]struct{}{} + locks = make(map[string]*lock) locksMu sync.Mutex ) @@ -35,11 +40,11 @@ func tryLock(ref string) error { locksMu.Lock() defer locksMu.Unlock() - if _, ok := locks[ref]; ok { - return errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", ref) + if v, ok := locks[ref]; ok { + return errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked since %s", ref, v.since) } - locks[ref] = struct{}{} + locks[ref] = &lock{time.Now()} return nil } diff --git a/content/local/locks_test.go b/content/local/locks_test.go new file mode 100644 index 000000000..c9d0034cc --- /dev/null +++ b/content/local/locks_test.go @@ -0,0 +1,32 @@ +/* + Copyright The containerd 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 local + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestTryLock(t *testing.T) { + err := tryLock("testref") + assert.NilError(t, err) + defer unlock("testref") + + err = tryLock("testref") + assert.ErrorContains(t, err, "ref testref locked since ") +}