diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp
index dda82bde5..e4d31844d 100644
--- a/src/storage/v2/storage.cpp
+++ b/src/storage/v2/storage.cpp
@@ -26,7 +26,8 @@ Storage::Accessor::Accessor(Storage *storage, uint64_t transaction_id,
     : storage_(storage),
       transaction_(transaction_id, start_timestamp),
       is_transaction_starter_(true),
-      is_transaction_active_(true) {}
+      is_transaction_active_(true),
+      storage_guard_(storage_->main_lock_) {}
 
 Storage::Accessor::Accessor(Accessor &&other) noexcept
     : storage_(other.storage_),
diff --git a/src/storage/v2/storage.hpp b/src/storage/v2/storage.hpp
index 504a8f9d7..5d2114ac4 100644
--- a/src/storage/v2/storage.hpp
+++ b/src/storage/v2/storage.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <optional>
+#include <shared_mutex>
 
 #include "storage/v2/commit_log.hpp"
 #include "storage/v2/edge.hpp"
@@ -11,6 +12,7 @@
 #include "storage/v2/transaction.hpp"
 #include "storage/v2/vertex.hpp"
 #include "storage/v2/vertex_accessor.hpp"
+#include "utils/rw_lock.hpp"
 #include "utils/scheduler.hpp"
 #include "utils/skip_list.hpp"
 
@@ -99,6 +101,8 @@ class Storage final {
     Transaction transaction_;
     bool is_transaction_starter_;
     bool is_transaction_active_;
+
+    std::shared_lock<utils::RWLock> storage_guard_;
   };
 
   Accessor Access();
@@ -106,6 +110,14 @@ class Storage final {
  private:
   void CollectGarbage();
 
+  // Main storage lock.
+  //
+  // Accessors take a shared lock when starting, so it is possible to block
+  // creation of new accessors by taking a unique lock. This is used when
+  // building a label-property index because it is much simpler to do when there
+  // are no parallel reads and writes.
+  utils::RWLock main_lock_{utils::RWLock::Priority::WRITE};
+
   // Main object storage
   utils::SkipList<storage::Vertex> vertices_;
   utils::SkipList<storage::Edge> edges_;