diff --git a/src/storage/v2/delta.hpp b/src/storage/v2/delta.hpp
index 0bf12d1ef..81da955a6 100644
--- a/src/storage/v2/delta.hpp
+++ b/src/storage/v2/delta.hpp
@@ -36,54 +36,56 @@ class PreviousPtr {
     EDGE,
   };
 
-  void Set(Delta *delta) {
-    uintptr_t value = reinterpret_cast<uintptr_t>(delta);
-    CHECK((value & kMask) == 0) << "Invalid pointer!";
-    storage_ = value | kDelta;
-  }
+  struct Pointer {
+    explicit Pointer(Delta *delta) : type(Type::DELTA), delta(delta) {}
+    explicit Pointer(Vertex *vertex) : type(Type::VERTEX), vertex(vertex) {}
+    explicit Pointer(Edge *edge) : type(Type::EDGE), edge(edge) {}
 
-  void Set(Vertex *vertex) {
-    uintptr_t value = reinterpret_cast<uintptr_t>(vertex);
-    CHECK((value & kMask) == 0) << "Invalid pointer!";
-    storage_ = value | kVertex;
-  }
+    Type type;
+    Delta *delta{nullptr};
+    Vertex *vertex{nullptr};
+    Edge *edge{nullptr};
+  };
 
-  void Set(Edge *edge) {
-    uintptr_t value = reinterpret_cast<uintptr_t>(edge);
-    CHECK((value & kMask) == 0) << "Invalid pointer!";
-    storage_ = value | kEdge;
-  }
+  PreviousPtr() : storage_(0) {}
 
-  Type GetType() const {
-    uintptr_t type = storage_ & kMask;
+  PreviousPtr(const PreviousPtr &other) noexcept
+      : storage_(other.storage_.load(std::memory_order_acquire)) {}
+
+  Pointer Get() const {
+    uintptr_t value = storage_.load(std::memory_order_acquire);
+    uintptr_t type = value & kMask;
     if (type == kDelta) {
-      return Type::DELTA;
+      return Pointer{reinterpret_cast<Delta *>(value & ~kMask)};
     } else if (type == kVertex) {
-      return Type::VERTEX;
+      return Pointer{reinterpret_cast<Vertex *>(value & ~kMask)};
     } else if (type == kEdge) {
-      return Type::EDGE;
+      return Pointer{reinterpret_cast<Edge *>(value & ~kMask)};
     } else {
       LOG(FATAL) << "Invalid pointer type!";
     }
   }
 
-  Delta *GetDelta() const {
-    CHECK((storage_ & kMask) == kDelta) << "Can't convert pointer to delta!";
-    return reinterpret_cast<Delta *>(storage_ & ~kMask);
+  void Set(Delta *delta) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(delta);
+    CHECK((value & kMask) == 0) << "Invalid pointer!";
+    storage_.store(value | kDelta, std::memory_order_release);
   }
 
-  Vertex *GetVertex() const {
-    CHECK((storage_ & kMask) == kVertex) << "Can't convert pointer to vertex!";
-    return reinterpret_cast<Vertex *>(storage_ & ~kMask);
+  void Set(Vertex *vertex) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(vertex);
+    CHECK((value & kMask) == 0) << "Invalid pointer!";
+    storage_.store(value | kVertex, std::memory_order_release);
   }
 
-  Edge *GetEdge() const {
-    CHECK((storage_ & kMask) == kEdge) << "Can't convert pointer to edge!";
-    return reinterpret_cast<Edge *>(storage_ & ~kMask);
+  void Set(Edge *edge) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(edge);
+    CHECK((value & kMask) == 0) << "Invalid pointer!";
+    storage_.store(value | kEdge, std::memory_order_release);
   }
 
  private:
-  uintptr_t storage_{0};
+  std::atomic<uintptr_t> storage_;
 };
 
 struct Delta {
diff --git a/src/storage/v2/mvcc.hpp b/src/storage/v2/mvcc.hpp
index 855c156ee..14d7ebf41 100644
--- a/src/storage/v2/mvcc.hpp
+++ b/src/storage/v2/mvcc.hpp
@@ -85,7 +85,9 @@ inline void CreateAndLinkDelta(Transaction *transaction, TObj *object,
                                                  transaction->command_id);
 
   // The operations are written in such order so that both `next` and `prev`
-  // chains are valid at all times.
+  // chains are valid at all times. The chains must be valid at all times
+  // because garbage collection (which traverses the chains) is done
+  // concurrently (as well as other execution threads).
   delta->prev.Set(object);
   if (object->delta) {
     object->delta->prev.Set(delta);
diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp
index 32f49fca9..9e22a120b 100644
--- a/src/storage/v2/storage.cpp
+++ b/src/storage/v2/storage.cpp
@@ -307,9 +307,10 @@ void Storage::Accessor::Commit() {
 void Storage::Accessor::Abort() {
   CHECK(transaction_->is_active) << "The transaction is already terminated!";
   for (const auto &delta : transaction_->deltas) {
-    switch (delta.prev.GetType()) {
+    auto prev = delta.prev.Get();
+    switch (prev.type) {
       case PreviousPtr::Type::VERTEX: {
-        auto vertex = delta.prev.GetVertex();
+        auto vertex = prev.vertex;
         std::lock_guard<utils::SpinLock> guard(vertex->lock);
         Delta *current = vertex->delta;
         while (current != nullptr &&
@@ -409,7 +410,7 @@ void Storage::Accessor::Abort() {
         break;
       }
       case PreviousPtr::Type::EDGE: {
-        auto edge = delta.prev.GetEdge();
+        auto edge = prev.edge;
         std::lock_guard<utils::SpinLock> guard(edge->lock);
         Delta *current = edge->delta;
         while (current != nullptr &&