From 134f2ba02e6f9b8dc128e8d3699ddb7fdaa298d5 Mon Sep 17 00:00:00 2001
From: Evgenii Stratonikov <evgeniy@morphbits.ru>
Date: Fri, 4 Nov 2022 18:07:17 +0300
Subject: [PATCH] [#1732] pilorama: Fix backwards log insertion

Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
---
 pkg/local_object_storage/pilorama/boltdb.go   |  6 +++
 .../pilorama/forest_test.go                   | 37 +++++++++++++++++++
 2 files changed, 43 insertions(+)

diff --git a/pkg/local_object_storage/pilorama/boltdb.go b/pkg/local_object_storage/pilorama/boltdb.go
index ddc6353ff..6a7edd330 100644
--- a/pkg/local_object_storage/pilorama/boltdb.go
+++ b/pkg/local_object_storage/pilorama/boltdb.go
@@ -324,6 +324,12 @@ func (t *boltForest) applyOperation(logBucket, treeBucket *bbolt.Bucket, lm *Log
 			return err
 		}
 	}
+
+	if key == nil {
+		// The operation is inserted in the beginning, reposition the cursor.
+		// Otherwise, `Next` call will return currently inserted operation.
+		c.First()
+	}
 	key, value = c.Next()
 
 	// 3. Re-apply all other operations.
diff --git a/pkg/local_object_storage/pilorama/forest_test.go b/pkg/local_object_storage/pilorama/forest_test.go
index 9dd9ecae7..90a3c11e9 100644
--- a/pkg/local_object_storage/pilorama/forest_test.go
+++ b/pkg/local_object_storage/pilorama/forest_test.go
@@ -514,6 +514,43 @@ func testForestTreeExists(t *testing.T, constructor func(t testing.TB) Forest) {
 	})
 }
 
+func TestApplyTricky1(t *testing.T) {
+	ops := []Move{
+		{
+			Parent: 1,
+			Meta:   Meta{Time: 100},
+			Child:  2,
+		},
+		{
+			Parent: 0,
+			Meta:   Meta{Time: 80},
+			Child:  1,
+		},
+	}
+
+	expected := []struct{ child, parent Node }{
+		{1, 0},
+		{2, 1},
+	}
+
+	treeID := "version"
+	d := CIDDescriptor{CID: cidtest.ID(), Position: 0, Size: 1}
+	for i := range providers {
+		t.Run(providers[i].name, func(t *testing.T) {
+			s := providers[i].construct(t)
+			for i := range ops {
+				require.NoError(t, s.TreeApply(d, treeID, &ops[i]))
+			}
+
+			for i := range expected {
+				_, parent, err := s.TreeGetMeta(d.CID, treeID, expected[i].child)
+				require.NoError(t, err)
+				require.Equal(t, expected[i].parent, parent)
+			}
+		})
+	}
+}
+
 func TestForest_ApplyRandom(t *testing.T) {
 	for i := range providers {
 		t.Run(providers[i].name, func(t *testing.T) {