From 4bb8c6f7d0be1aa8a42138b0f610945ca672e36b Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Wed, 20 Dec 2023 17:06:54 +0300 Subject: [PATCH] Fix missing dirty part refcnt++ leading to fsync hangs --- internal/buffer_list.go | 16 ++++++++-------- internal/buffer_list_test.go | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/internal/buffer_list.go b/internal/buffer_list.go index 8b45dc04..9948999c 100644 --- a/internal/buffer_list.go +++ b/internal/buffer_list.go @@ -256,14 +256,13 @@ func (l *BufferList) unqueue(b *FileBuffer) { ep := l.helpers.PartNum(b.offset+b.length-1) for i := sp; i < ep+1; i++ { p := l.dirtyParts[i] - if p != nil { - p.refcnt-- - if p.refcnt < 0 { - panic("BUG: dirty buffer count of part < 0") - } else if p.refcnt == 0 { - l.dirtyQueue.Delete(p.queueId) - delete(l.dirtyParts, i) - } + if p == nil || p.refcnt == 0 { + panic("BUG: dirty buffer count of part < 0") + } + p.refcnt-- + if p.refcnt == 0 { + l.dirtyQueue.Delete(p.queueId) + delete(l.dirtyParts, i) } } } else if b.state == BUF_CLEAN || b.state == BUF_FLUSHED_FULL { @@ -280,6 +279,7 @@ func (l *BufferList) referenceDirtyPart(partNum uint64) { l.dirtyQueue.Set(p.queueId, p) } else { l.dirtyQueue.Delete(p.queueId) + p.refcnt++ p.queueId = l.dirtyQid l.dirtyQueue.Set(p.queueId, p) } diff --git a/internal/buffer_list_test.go b/internal/buffer_list_test.go index add05d02..70aa2bf2 100644 --- a/internal/buffer_list_test.go +++ b/internal/buffer_list_test.go @@ -74,25 +74,31 @@ func (s *BufferListTest) TestSplitDirtyQueue(t *C) { zeroed, allocated := l.ZeroRange(0, 100*1024) t.Assert(zeroed, Equals, true) t.Assert(allocated, Equals, int64(0)) - // 6*1024 and 12*1024 isn't part boundary, refcnt of part 2 and 3 = 2, 1 and others = 1 - t.Assert(l.Add(0, make([]byte, 6*1024), BUF_DIRTY, false), Equals, int64(6*1024)) + // 6*1024 and 12*1024 isn't part boundary, refcnts should be: 4 2 2 1 1 ... 1 + t.Assert(l.Add(0*1024, make([]byte, 1*1024), BUF_DIRTY, false), Equals, int64(1*1024)) + t.Assert(l.Add(1*1024, make([]byte, 2*1024), BUF_DIRTY, false), Equals, int64(2*1024)) + t.Assert(l.Add(3*1024, make([]byte, 3*1024), BUF_DIRTY, false), Equals, int64(3*1024)) t.Assert(l.Add(6*1024, make([]byte, 6*1024), BUF_DIRTY, false), Equals, int64(6*1024)) data, ids, err := l.GetData(12*1024, (100-12)*1024, true) t.Assert(err, IsNil) t.Assert(len(data), Equals, 1) t.Assert(len(data[0]), Equals, (100-12)*1024) t.Assert(ids, DeepEquals, map[uint64]bool{ - 4: true, + 8: true, }) l.SetState(12*1024, (100-12)*1024, ids, BUF_CLEAN) data, ids, err = l.GetData(0, 12*1024, true) t.Assert(err, IsNil) - t.Assert(len(data), Equals, 2) - t.Assert(len(data[0]), Equals, 6*1024) - t.Assert(len(data[1]), Equals, 6*1024) + t.Assert(len(data), Equals, 4) + t.Assert(len(data[0]), Equals, 1*1024) + t.Assert(len(data[1]), Equals, 2*1024) + t.Assert(len(data[2]), Equals, 3*1024) + t.Assert(len(data[3]), Equals, 6*1024) t.Assert(ids, DeepEquals, map[uint64]bool{ 3: true, 5: true, + 7: true, + 9: true, }) l.SetState(0, 12*1024, ids, BUF_CLEAN) // Now check dirty list - it should be empty