From 6fba8aba8b1009e363ef85c1d8bbf4349535e376 Mon Sep 17 00:00:00 2001 From: Seebs Date: Thu, 13 Jan 2022 10:57:12 -0600 Subject: [PATCH] track field directly in view to prevent deadlocks The central reason this exists: **sync.RWMutex can block read locks even when no write lock is yet held.** If a write lock is *requested*, this can block future read locks. In particular, this means that recursive read locks are unsafe. But there's additional problems. The specific case that bit us involves not two, but *three* things running at once. Thing #1: executor doing AvailableShards. This RLocks the index, and then each field, and then each view. To complete, it must be able to obtain a read lock on each view in turn. Thing #2: DeleteField. This Locks the index. Even if it is stuck waiting for the lock (which it will be until AvailableShards completes), it can prevent *additional* RLocks of the index. Thing #3: CreateFragment. This Locks a view, then RLocks the index in order to look up a field. CreateFragment can't proceed until DeleteField completes. DeleteField can't proceed until AvailableShards completes. And AvailableShards can't proceed until CreateFragment completes. Solution: Cache the *Field in the view, so we don't need a read lock on the field or index to complete a CreateFragment. --- field.go | 1 + view.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/field.go b/field.go index e45e82d6e..b78ed051c 100644 --- a/field.go +++ b/field.go @@ -1032,6 +1032,7 @@ func (f *Field) createViewIfNotExistsBase(cvm *CreateViewMessage) (*view, bool, func (f *Field) newView(path, name string) *view { view := newView(f.holder, path, f.index, f.name, name, f.options) view.idx = f.idx + view.fld = f view.stats = f.Stats view.broadcaster = f.broadcaster return view diff --git a/view.go b/view.go index 5a8e23ae1..43eed3c34 100644 --- a/view.go +++ b/view.go @@ -40,6 +40,7 @@ type view struct { holder *Holder idx *Index + fld *Field fieldType string cacheType string @@ -363,7 +364,7 @@ func (v *view) notifyIfNewShard(shard uint64) { } func (v *view) newFragment(shard uint64) *fragment { - fld := v.idx.Field(v.field) + fld := v.fld spec := fragSpec{ index: v.idx, field: fld,