Skip to content

Commit a843e2c

Browse files
authored
Entity-listing indices tweaks (#926)
## Summary Addresses https://radixdlt.atlassian.net/browse/NODE-638 (i.e. all the comments on #922 (review)) ## Testing Just regression needs to pass.
2 parents cc58def + e8b08cc commit a843e2c

File tree

7 files changed

+128
-51
lines changed

7 files changed

+128
-51
lines changed

core-rust-bridge/src/main/java/com/radixdlt/testutil/InternalAddress.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static void registerCodec(CodecMap codecMap) {
8686

8787
public static InternalAddress create(byte[] addressBytes) {
8888
if (addressBytes.length != BYTE_LENGTH) {
89-
throw new IllegalArgumentException("Invalid ReNode ID length");
89+
throw new IllegalArgumentException("Invalid Entity ID length");
9090
}
9191
return new InternalAddress(addressBytes);
9292
}

core-rust/engine-state-api-server/src/engine_state_api/readers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use convert_case::{Case, Casing};
66
use itertools::Itertools;
77

88
use state_manager::store::traits::indices::{
9-
CreationId, EntityBlueprintId, EntityBlueprintIdV1, ReNodeListingIndex,
9+
CreationId, EntityBlueprintId, EntityBlueprintIdV1, EntityListingIndex,
1010
};
1111

1212
use super::*;
@@ -1199,7 +1199,7 @@ pub struct EntitySummary {
11991199
pub blueprint_id: Option<BlueprintId>, // only present for Object entities
12001200
}
12011201

1202-
impl<'s, S: ReNodeListingIndex> EngineEntityLister<'s, S> {
1202+
impl<'s, S: EntityListingIndex> EngineEntityLister<'s, S> {
12031203
/// Creates an instance reading from the given database.
12041204
pub fn new(database: &'s S) -> Self {
12051205
Self { database }

core-rust/state-manager/src/store/codecs.rs

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
* permissions under this License.
6363
*/
6464

65+
use std::mem::size_of;
6566
use std::ops::Range;
6667

6768
use crate::engine_prelude::*;
@@ -316,6 +317,22 @@ impl DbCodec<NodeId> for NodeIdDbCodec {
316317
#[derive(Default)]
317318
pub struct TypeAndCreationIndexKeyDbCodec {}
318319

320+
impl TypeAndCreationIndexKeyDbCodec {
321+
/// An extracted "how are parts encoded together" knowledge, to be shared with the
322+
/// [`BoundedDbCodec`] implementation.
323+
fn encode_parts(
324+
entity_byte: u8,
325+
state_version_bytes: &[u8; StateVersion::BYTE_LEN],
326+
index_within_txn_bytes: &[u8; size_of::<u32>()],
327+
) -> Vec<u8> {
328+
let mut bytes = Vec::new();
329+
bytes.push(entity_byte);
330+
bytes.extend_from_slice(state_version_bytes);
331+
bytes.extend_from_slice(index_within_txn_bytes);
332+
bytes
333+
}
334+
}
335+
319336
impl DbCodec<(EntityType, CreationId)> for TypeAndCreationIndexKeyDbCodec {
320337
fn encode(&self, value: &(EntityType, CreationId)) -> Vec<u8> {
321338
let (
@@ -325,11 +342,11 @@ impl DbCodec<(EntityType, CreationId)> for TypeAndCreationIndexKeyDbCodec {
325342
index_within_txn,
326343
},
327344
) = value;
328-
let mut bytes = Vec::new();
329-
bytes.push(*entity_type as u8);
330-
bytes.extend_from_slice(&state_version.to_be_bytes());
331-
bytes.extend_from_slice(&index_within_txn.to_be_bytes());
332-
bytes
345+
Self::encode_parts(
346+
*entity_type as u8,
347+
&state_version.to_be_bytes(),
348+
&index_within_txn.to_be_bytes(),
349+
)
333350
}
334351

335352
fn decode(&self, bytes: &[u8]) -> (EntityType, CreationId) {
@@ -350,6 +367,16 @@ impl DbCodec<(EntityType, CreationId)> for TypeAndCreationIndexKeyDbCodec {
350367
}
351368
}
352369

370+
impl BoundedDbCodec for TypeAndCreationIndexKeyDbCodec {
371+
fn upper_bound_encoding(&self) -> Vec<u8> {
372+
Self::encode_parts(
373+
u8::MAX,
374+
&[u8::MAX; StateVersion::BYTE_LEN],
375+
&[u8::MAX; size_of::<u32>()],
376+
)
377+
}
378+
}
379+
353380
impl GroupPreservingDbCodec for TypeAndCreationIndexKeyDbCodec {
354381
type Group = EntityType;
355382

@@ -372,6 +399,24 @@ impl IntraGroupOrderPreservingDbCodec<(EntityType, CreationId)> for TypeAndCreat
372399
#[derive(Default)]
373400
pub struct BlueprintAndCreationIndexKeyDbCodec {}
374401

402+
impl BlueprintAndCreationIndexKeyDbCodec {
403+
/// An extracted "how are parts encoded together" knowledge, to be shared with the
404+
/// [`BoundedDbCodec`] implementation.
405+
fn encode_parts(
406+
package_address_bytes: &[u8; NodeId::LENGTH],
407+
blueprint_name_hash_bytes: &[u8; Hash::LENGTH],
408+
state_version_bytes: &[u8; StateVersion::BYTE_LEN],
409+
index_within_txn_bytes: &[u8; size_of::<u32>()],
410+
) -> Vec<u8> {
411+
let mut bytes = Vec::new();
412+
bytes.extend_from_slice(package_address_bytes);
413+
bytes.extend_from_slice(blueprint_name_hash_bytes);
414+
bytes.extend_from_slice(state_version_bytes);
415+
bytes.extend_from_slice(index_within_txn_bytes);
416+
bytes
417+
}
418+
}
419+
375420
impl DbCodec<(PackageAddress, Hash, CreationId)> for BlueprintAndCreationIndexKeyDbCodec {
376421
fn encode(&self, value: &(PackageAddress, Hash, CreationId)) -> Vec<u8> {
377422
let (
@@ -382,12 +427,12 @@ impl DbCodec<(PackageAddress, Hash, CreationId)> for BlueprintAndCreationIndexKe
382427
index_within_txn,
383428
},
384429
) = value;
385-
let mut bytes = Vec::new();
386-
bytes.extend_from_slice(&package_address.as_node_id().0);
387-
bytes.extend_from_slice(&blueprint_name_hash.0);
388-
bytes.extend_from_slice(&state_version.to_be_bytes());
389-
bytes.extend_from_slice(&index_within_txn.to_be_bytes());
390-
bytes
430+
Self::encode_parts(
431+
&package_address.as_node_id().0,
432+
&blueprint_name_hash.0,
433+
&state_version.to_be_bytes(),
434+
&index_within_txn.to_be_bytes(),
435+
)
391436
}
392437

393438
fn decode(&self, bytes: &[u8]) -> (PackageAddress, Hash, CreationId) {
@@ -413,6 +458,17 @@ impl DbCodec<(PackageAddress, Hash, CreationId)> for BlueprintAndCreationIndexKe
413458
}
414459
}
415460

461+
impl BoundedDbCodec for BlueprintAndCreationIndexKeyDbCodec {
462+
fn upper_bound_encoding(&self) -> Vec<u8> {
463+
Self::encode_parts(
464+
&[u8::MAX; NodeId::LENGTH],
465+
&[u8::MAX; Hash::LENGTH],
466+
&[u8::MAX; StateVersion::BYTE_LEN],
467+
&[u8::MAX; size_of::<u32>()],
468+
)
469+
}
470+
}
471+
416472
impl GroupPreservingDbCodec for BlueprintAndCreationIndexKeyDbCodec {
417473
type Group = (PackageAddress, Hash);
418474

core-rust/state-manager/src/store/historical_state.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use substate_store_impls::state_tree::entity_tier::EntityTier;
6868
use crate::engine_prelude::*;
6969
use crate::query::StateManagerSubstateQueries;
7070
use crate::store::traits::*;
71-
use crate::traits::indices::{CreationId, EntityBlueprintId, ReNodeListingIndex};
71+
use crate::traits::indices::{CreationId, EntityBlueprintId, EntityListingIndex};
7272
use crate::{
7373
CommittedTransactionIdentifiers, LedgerStateSummary, ReadableRocks, StateManagerDatabase,
7474
StateVersion,
@@ -77,7 +77,7 @@ use crate::{
7777
/// An implementation of a [`SubstateDatabase`] viewed at a specific [`StateVersion`].
7878
///
7979
/// This database is backed by:
80-
/// - a [`ReadableTreeStore`] - a versioned source of ReNodes / Partitions / Substates metadata,
80+
/// - a [`ReadableTreeStore`] - a versioned source of Entities / Partitions / Substates metadata,
8181
/// - and a [`LeafSubstateValueStore`] - a store of Substate values' associated with their leafs.
8282
pub struct StateTreeBasedSubstateDatabase<'s, DS> {
8383
base_store: DS,
@@ -360,7 +360,7 @@ impl<'s, R: ReadableRocks + 's, DS: Deref<Target = StateManagerDatabase<R>>>
360360
}
361361
}
362362

363-
impl<'s, R: ReadableRocks + 's, DS: Deref<Target = StateManagerDatabase<R>>> ReNodeListingIndex
363+
impl<'s, R: ReadableRocks + 's, DS: Deref<Target = StateManagerDatabase<R>>> EntityListingIndex
364364
for VersionScopedDatabase<'s, DS>
365365
{
366366
fn get_created_entity_iter(

core-rust/state-manager/src/store/jmt_gc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ fn iterate_dfs_post_order<'s, S: ReadableTreeStore>(
214214
TreeNode::Null => {
215215
// A special case: this subtree is empty.
216216
// Note: at the moment of writing this, this case is impossible in practice: we do
217-
// not delete ReNode-Tier tree, and we also do not store empty lower-Tier trees
217+
// not delete Entity-Tier tree, and we also do not store empty lower-Tier trees
218218
// (i.e. we delete their higher-Tier leaf counterpart instead). However, we can
219219
// return a correct empty result here (in case the above assumptions ever change).
220220
Box::new(iter::empty())

core-rust/state-manager/src/store/rocks_db.rs

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ use crate::store::traits::gc::{
9494
LedgerProofsGcProgress, LedgerProofsGcStore, StateTreeGcStore, VersionedLedgerProofsGcProgress,
9595
};
9696
use crate::store::traits::indices::{
97-
CreationId, EntityBlueprintId, ObjectBlueprintName, ObjectBlueprintNameV1, ReNodeListingIndex,
97+
CreationId, EntityBlueprintId, EntityListingIndex, ObjectBlueprintName, ObjectBlueprintNameV1,
9898
VersionedEntityBlueprintId, VersionedObjectBlueprintName,
9999
};
100100
use crate::store::traits::measurement::{CategoryDbVolumeStatistic, MeasurableDatabase};
@@ -799,10 +799,7 @@ impl ActualStateManagerDatabase {
799799
state_manager_database.catchup_account_change_index();
800800
state_manager_database.restore_december_2023_lost_substates(network);
801801
state_manager_database.ensure_historical_substate_values();
802-
803-
if state_manager_database.config.enable_entity_listing_indices {
804-
state_manager_database.catchup_entity_listing_indices()
805-
}
802+
state_manager_database.ensure_entity_listing_indices();
806803

807804
Ok(state_manager_database)
808805
}
@@ -2067,23 +2064,33 @@ impl<R: WriteableRocks> StateManagerDatabase<R> {
20672064
substate_changes: &BySubstate<SubstateChangeAction>,
20682065
) {
20692066
for (index_within_txn, node_id) in substate_changes.iter_node_ids().enumerate() {
2070-
let type_info_creation = substate_changes.get(
2067+
let type_info_change = substate_changes.get(
20712068
node_id,
20722069
&TYPE_INFO_FIELD_PARTITION,
20732070
&TypeInfoField::TypeInfo.into(),
20742071
);
2075-
let Some(type_info_creation) = type_info_creation else {
2072+
let Some(type_info_change) = type_info_change else {
20762073
continue;
20772074
};
2078-
let SubstateChangeAction::Create { new } = type_info_creation else {
2079-
panic!(
2080-
"type info substate should be immutable: {:?}",
2081-
type_info_creation
2082-
);
2075+
let created_type_info_value = match type_info_change {
2076+
SubstateChangeAction::Create { new } => new,
2077+
SubstateChangeAction::Update { .. } => {
2078+
// Even if TypeInfo is updated (e.g. its blueprint version bumped), the fields
2079+
// that we care about (package address and blueprint name) are effectively
2080+
// immutable - we can thus safely ignore all updates to this substate.
2081+
continue;
2082+
}
2083+
SubstateChangeAction::Delete { .. } => {
2084+
panic!(
2085+
"type info substate should not be deleted: {:?}",
2086+
type_info_change
2087+
)
2088+
}
20832089
};
2084-
let type_info = scrypto_decode::<TypeInfoSubstate>(new).expect("decode type info");
2090+
let type_info = scrypto_decode::<TypeInfoSubstate>(created_type_info_value)
2091+
.expect("decode type info");
20852092

2086-
let entity_type = node_id.entity_type().expect("type of upserted ReNode");
2093+
let entity_type = node_id.entity_type().expect("type of upserted Entity");
20872094
let creation_id = CreationId::new(state_version, index_within_txn);
20882095

20892096
match type_info {
@@ -2119,18 +2126,33 @@ impl<R: WriteableRocks> StateManagerDatabase<R> {
21192126
}
21202127
}
21212128

2122-
fn catchup_entity_listing_indices(&self) {
2129+
fn ensure_entity_listing_indices(&self) {
21232130
const TXN_FLUSH_INTERVAL: u64 = 10_000;
2131+
const PROGRESS_LOG_INTERVAL: u64 = 1_000_000;
21242132

2125-
info!("ReNode listing indices are enabled.");
21262133
let db_context = self.open_rw_context();
2127-
let catchup_from_version = db_context
2134+
2135+
if !self.config.enable_entity_listing_indices {
2136+
info!("Entity listing indices are disabled.");
2137+
// We remove the indices' data and metadata in a single, cheap write batch:
2138+
db_context.cf(TypeAndCreationIndexedEntitiesCf).delete_all();
2139+
db_context
2140+
.cf(BlueprintAndCreationIndexedObjectsCf)
2141+
.delete_all();
2142+
db_context
2143+
.cf(ExtensionsDataCf)
2144+
.delete(&ExtensionsDataKey::EntityListingIndicesLastProcessedStateVersion);
2145+
info!("Deleted entity listing indices.");
2146+
return;
2147+
}
2148+
2149+
info!("Entity listing indices are enabled.");
2150+
let last_processed_state_version = db_context
21282151
.cf(ExtensionsDataCf)
21292152
.get(&ExtensionsDataKey::EntityListingIndicesLastProcessedStateVersion)
21302153
.map(StateVersion::from_be_bytes)
2131-
.unwrap_or(StateVersion::pre_genesis())
2132-
.next()
2133-
.expect("next version");
2154+
.unwrap_or(StateVersion::pre_genesis());
2155+
let catchup_from_version = last_processed_state_version.next().expect("next version");
21342156

21352157
let mut receipts_iter = db_context
21362158
.cf(TransactionReceiptsCf)
@@ -2144,18 +2166,17 @@ impl<R: WriteableRocks> StateManagerDatabase<R> {
21442166
&receipt.state_changes.substate_level_changes,
21452167
);
21462168
if state_version.number() % TXN_FLUSH_INTERVAL == 0 || receipts_iter.peek().is_none() {
2147-
info!(
2148-
"ReNode listing indices updated to {}; flushing...",
2149-
state_version
2150-
);
2169+
if state_version.number() % PROGRESS_LOG_INTERVAL == 0 {
2170+
info!("Entity listing indices updated to {}", state_version);
2171+
}
21512172
db_context.cf(ExtensionsDataCf).put(
21522173
&ExtensionsDataKey::EntityListingIndicesLastProcessedStateVersion,
21532174
&state_version.to_be_bytes().to_vec(),
21542175
);
21552176
db_context.flush();
21562177
}
21572178
}
2158-
info!("ReNode listing indices are caught up.");
2179+
info!("Caught up Entity listing indices.");
21592180
}
21602181
}
21612182

@@ -2315,7 +2336,7 @@ impl<R: ReadableRocks> IterableAccountChangeIndex for StateManagerDatabase<R> {
23152336
}
23162337
}
23172338

2318-
impl<R: ReadableRocks> ReNodeListingIndex for StateManagerDatabase<R> {
2339+
impl<R: ReadableRocks> EntityListingIndex for StateManagerDatabase<R> {
23192340
fn get_created_entity_iter(
23202341
&self,
23212342
entity_type: EntityType,

core-rust/state-manager/src/store/traits.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ pub mod indices {
658658
use super::*;
659659
use std::ops::Range;
660660

661-
pub trait ReNodeListingIndex {
661+
pub trait EntityListingIndex {
662662
fn get_created_entity_iter(
663663
&self,
664664
entity_type: EntityType,
@@ -672,14 +672,14 @@ pub mod indices {
672672
) -> Box<dyn Iterator<Item = (CreationId, EntityBlueprintId)> + '_>;
673673
}
674674

675-
/// A unique ID of a ReNode, based on creation order.
675+
/// A unique ID of an Entity, based on creation order.
676676
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Sbor)]
677677
pub struct CreationId {
678-
/// State version of the transaction which created the ReNode (i.e. which created the first
679-
/// substate under this ReNode).
678+
/// State version of the transaction which created the Entity (i.e. which created the first
679+
/// Substate under this Entity).
680680
pub state_version: StateVersion,
681681

682-
/// An index in a list of ReNodes created by a single transaction.
682+
/// An index in a list of Entities created by a single transaction.
683683
pub index_within_txn: u32,
684684
}
685685

@@ -717,7 +717,7 @@ pub mod indices {
717717
}
718718

719719
/// An entity's ID and its blueprint reference.
720-
/// This is a "technical" structure stored in one of the ReNode-listing indices.
720+
/// This is a "technical" structure stored in one of the Entity-listing indices.
721721
#[derive(Debug, Clone, ScryptoCategorize, ScryptoEncode, ScryptoDecode)]
722722
pub struct EntityBlueprintIdV1 {
723723
/// Node ID.
@@ -751,7 +751,7 @@ pub mod indices {
751751
}
752752

753753
/// An Object's ID and its blueprint name.
754-
/// This is a "technical" structure stored in one of the ReNode-listing indices.
754+
/// This is a "technical" structure stored in one of the Entity-listing indices.
755755
#[derive(Debug, Clone, ScryptoCategorize, ScryptoEncode, ScryptoDecode)]
756756
pub struct ObjectBlueprintNameV1 {
757757
/// Node ID - guaranteed to *not* be a Key-Value Store.

0 commit comments

Comments
 (0)