package hypergraph import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "source.quilibrium.com/quilibrium/monorepo/types/hypergraph" "source.quilibrium.com/quilibrium/monorepo/types/tries" "source.quilibrium.com/quilibrium/monorepo/utils/p2p" ) func (hg *HypergraphCRDT) GetVertexDataIterator( domain [32]byte, ) tries.VertexDataIterator { shardKey := tries.ShardKey{ L1: [3]byte(p2p.GetBloomFilterIndices(domain[:], 256, 3)), L2: [32]byte(append([]byte{}, domain[:]...)), } iter, err := hg.store.GetVertexDataIterator(shardKey) if err != nil { // Return a no-op iterator on error return &noOpVertexDataIterator{} } return iter } // GetVertexData retrieves the data tree associated with a vertex. Returns // ErrRemoved if the vertex has been removed. func (hg *HypergraphCRDT) GetVertexData(id [64]byte) ( *tries.VectorCommitmentTree, error, ) { timer := prometheus.NewTimer(GetDuration.WithLabelValues("vertex_data")) defer timer.ObserveDuration() shardKey := tries.ShardKey{ L1: [3]byte(p2p.GetBloomFilterIndices(id[:32], 256, 3)), L2: [32]byte(append([]byte{}, id[:32]...)), } // We need to verify it hasn't been removed, because vertex data reaping is // not instantaneous _, removeSet := hg.getOrCreateIdSet( shardKey, hg.vertexAdds, hg.vertexRemoves, hypergraph.VertexAtomType, ) if removeSet.Has(id) { GetVertexDataTotal.WithLabelValues("removed").Inc() ErrorsTotal.WithLabelValues("get_vertex_data", "removed").Inc() return nil, errors.Wrap(hypergraph.ErrRemoved, "get vertex data") } tree, err := hg.store.LoadVertexTree(id[:]) if err != nil { GetVertexDataTotal.WithLabelValues("error").Inc() ErrorsTotal.WithLabelValues("get_vertex_data", "load_error").Inc() return nil, err } GetVertexDataTotal.WithLabelValues("success").Inc() return tree, nil } // SetVertexData associates a data tree with a vertex. The data is stored // separately from the vertex atom itself. func (hg *HypergraphCRDT) SetVertexData( txn tries.TreeBackingStoreTransaction, id [64]byte, data *tries.VectorCommitmentTree, ) error { err := hg.store.SaveVertexTree(txn, id[:], data) if err != nil { VertexDataSetTotal.WithLabelValues("error").Inc() ErrorsTotal.WithLabelValues("set_vertex_data", "save_error").Inc() return err } VertexDataSetTotal.WithLabelValues("success").Inc() return nil } // MarkVertexDataForDeletion marks vertex data for future deletion. The data // will be pruned after the specified timestamp. func (hg *HypergraphCRDT) MarkVertexDataForDeletion( txn tries.TreeBackingStoreTransaction, deleteAt int64, id [64]byte, ) error { err := hg.store.TombstoneVertexTree(txn, deleteAt, id[:]) if err != nil { VertexDataTombstoneTotal.WithLabelValues("error").Inc() ErrorsTotal.WithLabelValues( "tombstone_vertex_data", "tombstone_error", ).Inc() return err } VertexDataTombstoneTotal.WithLabelValues("success").Inc() return nil } // UnmarkVertexDataForDeletion cancels a scheduled deletion of vertex data. This // prevents the data from being pruned at the specified timestamp. func (hg *HypergraphCRDT) UnmarkVertexDataForDeletion( txn tries.TreeBackingStoreTransaction, deleteAt int64, id [64]byte, ) error { err := hg.store.UndoTombstoneVertexTree(txn, deleteAt, id[:]) if err != nil { VertexDataUndoTombstoneTotal.WithLabelValues("error").Inc() ErrorsTotal.WithLabelValues( "undo_tombstone_vertex_data", "undo_error", ).Inc() return err } VertexDataUndoTombstoneTotal.WithLabelValues("success").Inc() return nil } // RunVertexDataPruning removes vertex data marked for deletion with a time // prior to the time this method is invoked. This should be called periodically // to clean up tombstoned data. func (hg *HypergraphCRDT) RunVertexDataPruning( txn tries.TreeBackingStoreTransaction, ) error { timer := prometheus.NewTimer(VertexDataPruningDuration) defer timer.ObserveDuration() err := hg.store.ReapVertexTrees(txn) if err != nil { VertexDataPruningTotal.WithLabelValues("error").Inc() ErrorsTotal.WithLabelValues("prune_vertex_data", "reap_error").Inc() return err } VertexDataPruningTotal.WithLabelValues("success").Inc() return nil } // noOpVertexDataIterator is a no-op implementation of VertexDataIterator // used when an error occurs creating the real iterator type noOpVertexDataIterator struct{} func (n *noOpVertexDataIterator) Key() []byte { return nil } func (n *noOpVertexDataIterator) First() bool { return false } func (n *noOpVertexDataIterator) Next() bool { return false } func (n *noOpVertexDataIterator) Prev() bool { return false } func (n *noOpVertexDataIterator) Valid() bool { return false } func (n *noOpVertexDataIterator) Value() *tries.VectorCommitmentTree { return nil } func (n *noOpVertexDataIterator) Close() error { return nil } func (n *noOpVertexDataIterator) Last() bool { return false }