ceremonyclient/hypergraph/vertex_data.go

153 lines
4.9 KiB
Go

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 }