From 21aed89e88703eef1f437969d05fbab1c149b0eb Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 3 Mar 2019 12:17:14 -0800
Subject: [PATCH] Fix unnecessary pinning in `btreefile.LeafPage`

We pin an underlying page N times too many whenever we instantiate a new
LeafPage class, due to the constructor of the BTreeFilePageTuple. What we
want is a tuple that acts like a _weak_ reference to the underlying
page, and only make it a _strong_ reference whenever this tuple escapes
the scope of the btree-internal classes (e.g. in `BTreeFile.addTuple`).

Right now, I just add an `unpin()` call in the constructor of the Tuple,
and then pin it whenever I know that it escapes the scope of the btree
package. Maybe there's a more graceful way of doing it. I have no idea.
---
 .../nanodb/plannodes/MaterializeNode.java     |  2 +-
 .../storage/btreefile/BTreeFilePageTuple.java |  8 +++++++
 .../storage/btreefile/BTreeFileVerifier.java  |  4 ++--
 .../storage/btreefile/BTreeTupleFile.java     | 23 +++++++++++++++----
 4 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/src/main/java/edu/caltech/nanodb/plannodes/MaterializeNode.java b/src/main/java/edu/caltech/nanodb/plannodes/MaterializeNode.java
index 6768762..37c3aa4 100644
--- a/src/main/java/edu/caltech/nanodb/plannodes/MaterializeNode.java
+++ b/src/main/java/edu/caltech/nanodb/plannodes/MaterializeNode.java
@@ -112,7 +112,7 @@ public class MaterializeNode extends PlanNode {
                 if (tup != null) {
                     if (tup.isDiskBacked()) {
                         // Make an in-memory version of the tuple we can cache.
-                        Tuple copy = new TupleLiteral(tup);
+                        Tuple copy = TupleLiteral.fromTuple(tup);
                         tup.unpin();
                         tup = copy;
                     }
diff --git a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFilePageTuple.java b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFilePageTuple.java
index 751eef7..dc43379 100644
--- a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFilePageTuple.java
+++ b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFilePageTuple.java
@@ -58,6 +58,14 @@ public class BTreeFilePageTuple extends PageTuple {
                 "tupleIndex must be at least 0, got " + tupleIndex);
         }
 
+        unpin(); // TODO(michael): This is gross. I know.
+        // What we really want here is a tuple that acts like a "weak reference" to its
+        // underlying page. This is because we actually instantiate these tuples inside
+        // the page itself (well, inside of LeafPage), so we end up having the page
+        // pinned N times more than the actual references that escape outside of the
+        // internal BT data structures. We will re-pin the tuples in the outward-facing
+        // methods in `BTreeTupleFile`.
+
         this.tupleIndex = tupleIndex;
     }
 
diff --git a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFileVerifier.java b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFileVerifier.java
index 2892ef1..2d4fee5 100644
--- a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFileVerifier.java
+++ b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeFileVerifier.java
@@ -260,7 +260,7 @@ public class BTreeFileVerifier {
             ArrayList<TupleLiteral> keys = new ArrayList<>(numKeys);
             if (numKeys > 1) {
                 Tuple prevKey = inner.getKey(0);
-                keys.add(new TupleLiteral(prevKey));
+                keys.add(TupleLiteral.fromTuple(prevKey));
 
                 if (parentLeftKey != null) {
                     int cmp = TupleComparator.compareTuples(parentLeftKey, prevKey);
@@ -275,7 +275,7 @@ public class BTreeFileVerifier {
 
                 for (int k = 1; k < numKeys; k++) {
                     Tuple key = inner.getKey(k);
-                    keys.add(new TupleLiteral(key));
+                    keys.add(TupleLiteral.fromTuple(key));
 
                     int cmp = TupleComparator.compareTuples(prevKey, key);
                     if (cmp == 0) {
diff --git a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeTupleFile.java b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeTupleFile.java
index 01ab84d..b237bf8 100644
--- a/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeTupleFile.java
+++ b/src/main/java/edu/caltech/nanodb/storage/btreefile/BTreeTupleFile.java
@@ -190,6 +190,7 @@ public class BTreeTupleFile implements SequentialTupleFile {
         if (leaf != null && leaf.getNumTuples() > 0)
             tup = leaf.getTuple(0);
 
+        tup.pin();
         return tup;
     }
 
@@ -256,10 +257,16 @@ public class BTreeTupleFile implements SequentialTupleFile {
                         logger.error(String.format(
                             "Next leaf node %d has no entries?!", nextPageNo));
                     }
+
+                    dbPage.unpin();
                 }
             }
         }
 
+        if (nextTuple != null) {
+            nextTuple.pin();
+        }
+
         return nextTuple;
     }
 
@@ -281,8 +288,11 @@ public class BTreeTupleFile implements SequentialTupleFile {
         LeafPage leaf = new LeafPage(dbPage, schema);
         for (int i = 0; i < leaf.getNumTuples(); i++) {
             BTreeFilePageTuple tup = leaf.getTuple(i);
-            if (tup.getOffset() == fpOffset)
+
+            if (tup.getOffset() == fpOffset) {
+                tup.pin();
                 return tup;
+            }
 
             // Tuple offsets within a page will be monotonically increasing.
             if (tup.getOffset() > fpOffset)
@@ -320,6 +330,7 @@ public class BTreeTupleFile implements SequentialTupleFile {
 
                 if (cmp == 0) {
                     // Found it!
+                    tup.pin();
                     return tup;
                 }
                 else if (cmp > 0) {
@@ -372,8 +383,10 @@ public class BTreeTupleFile implements SequentialTupleFile {
             for (int i = 0; i < leaf.getNumTuples(); i++) {
                 BTreeFilePageTuple tup = leaf.getTuple(i);
                 int cmp = TupleComparator.comparePartialTuples(tup, searchKey);
-                if (cmp > 0)
+                if (cmp > 0) {
+                    tup.pin();
                     return tup;  // Found it!
+                }
             }
 
             leaf.getDBPage().unpin();
@@ -397,10 +410,12 @@ public class BTreeTupleFile implements SequentialTupleFile {
         if (tup instanceof TupleLiteral)
             tupLit = (TupleLiteral) tup;
         else
-            tupLit = new TupleLiteral(tup);
+            tupLit = TupleLiteral.fromTuple(tup);
         tupLit.setStorageSize(PageTuple.getTupleStorageSize(schema, tupLit));
 
-        return leafPageOps.addTuple(leaf, tupLit, pagePath);
+        BTreeFilePageTuple bTup = leafPageOps.addTuple(leaf, tupLit, pagePath);
+        bTup.pin();
+        return bTup;
     }
 
 
-- 
GitLab