Add storage side capabilites to retrieve metadata

In order to get the required metadata in constant time we need to keep
track of the node labels and edge types that were ever present in the
database. This is done by the two axuiliary datastructures that are
present in the storage instances. The ability to get this metadata is
propagated to the DBAccessor class, which the query modules can interact
with.
This commit is contained in:
gvolfing 2023-11-06 12:37:48 +01:00
parent 16b8c7b27c
commit 50c485fe40
6 changed files with 46 additions and 0 deletions

View File

@ -597,6 +597,13 @@ class DbAccessor final {
return accessor_->ApproximateVertexCount(label, property, lower, upper);
}
std::vector<std::string> ListAllPossiblyPresentVertexLabels() const {
return accessor_->ListAllPossiblyPresentVertexLabels();
}
std::vector<std::string> ListAllPossiblyPresentEdgeTypes() const {
return accessor_->ListAllPossiblyPresentEdgeTypes();
}
storage::IndicesInfo ListAllIndices() const { return accessor_->ListAllIndices(); }
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }

View File

@ -944,6 +944,7 @@ Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from,
transaction_.manyDeltasCache.Invalidate(from_vertex, edge_type, EdgeDirection::OUT);
transaction_.manyDeltasCache.Invalidate(to_vertex, edge_type, EdgeDirection::IN);
storage_->stored_edge_types_.insert(edge_type);
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, storage_, &transaction_);

View File

@ -331,6 +331,7 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
if (to_vertex->deleted) return Error::DELETED_OBJECT;
}
storage_->stored_edge_types_.insert(edge_type);
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
auto gid = storage::Gid::FromUint(mem_storage->edge_id_.fetch_add(1, std::memory_order_acq_rel));
EdgeRef edge(gid);
@ -395,6 +396,8 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdgeEx(VertexAcces
if (to_vertex->deleted) return Error::DELETED_OBJECT;
}
storage_->stored_edge_types_.insert(edge_type);
// NOTE: When we update the next `edge_id_` here we perform a RMW
// (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
// because this function is only called from the replication delta applier

View File

@ -122,6 +122,24 @@ std::optional<uint64_t> Storage::Accessor::GetTransactionId() const {
return {};
}
std::vector<std::string> Storage::Accessor::ListAllPossiblyPresentVertexLabels() const {
std::vector<std::string> vertex_labels;
vertex_labels.reserve(storage_->stored_node_labels_.size());
for (const auto label : storage_->stored_node_labels_) {
vertex_labels.emplace_back(LabelToName(label));
}
return vertex_labels;
}
std::vector<std::string> Storage::Accessor::ListAllPossiblyPresentEdgeTypes() const {
std::vector<std::string> edge_types;
edge_types.reserve(storage_->stored_edge_types_.size());
for (const auto edge_type : storage_->stored_edge_types_) {
edge_types.emplace_back(EdgeTypeToName(edge_type));
}
return edge_types;
}
void Storage::Accessor::AdvanceCommand() {
transaction_.manyDeltasCache.Clear(); // TODO: Just invalidate the View::OLD cache, NEW should still be fine
++transaction_.command_id;

View File

@ -237,6 +237,10 @@ class Storage {
const std::string &id() const { return storage_->id(); }
std::vector<std::string> ListAllPossiblyPresentVertexLabels() const;
std::vector<std::string> ListAllPossiblyPresentEdgeTypes() const;
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label) = 0;
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) = 0;
@ -384,6 +388,18 @@ class Storage {
Indices indices_;
Constraints constraints_;
// Datastructures to provide fast retrieval of node-label and
// edge-type related metadata.
// Currently we should not remove any node-labels or edge-types even
// if the set of given types are currently not present in the
// database. This metadata is usually used by client side
// applications that want to be aware of the kind of data that *may*
// be present in the database.
// TODO(gvolfing): check if this would be faster with flat_maps.
std::unordered_set<LabelId> stored_node_labels_;
std::unordered_set<EdgeTypeId> stored_edge_types_;
std::atomic<uint64_t> vertex_id_{0};
std::atomic<uint64_t> edge_id_{0};
const std::string id_; //!< High-level assigned ID

View File

@ -109,6 +109,7 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
vertex_->labels.push_back(label);
storage_->stored_node_labels_.insert(label);
/// TODO: some by pointers, some by reference => not good, make it better
storage_->constraints_.unique_constraints_->UpdateOnAddLabel(label, *vertex_, transaction_->start_timestamp);