Skip to content

Commit a2ce110

Browse files
committed
feat: ids_data_usage
1 parent acdcffc commit a2ce110

File tree

6 files changed

+407
-11
lines changed

6 files changed

+407
-11
lines changed

crates/rostra-client-db/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ impl Database {
500500
let mut content_rc_table = tx.open_table(&content_rc::TABLE)?;
501501
let mut events_content_missing_table =
502502
tx.open_table(&tables::events_content_missing::TABLE)?;
503+
let mut ids_data_usage_table = tx.open_table(&ids_data_usage::TABLE)?;
503504

504505
debug_assert!(Database::has_event_tx(
505506
event_content.event.event_id,
@@ -563,6 +564,13 @@ impl Database {
563564
// Increment RC for this event claiming the content
564565
Database::increment_content_rc_tx(content_hash, &mut content_rc_table)?;
565566

567+
// Track content size for the author
568+
Database::increment_content_size_tx(
569+
event_content.author(),
570+
event_content.content_len(),
571+
&mut ids_data_usage_table,
572+
)?;
573+
566574
// Mark per-event state as available (this event now holds an RC)
567575
events_content_state_table.insert(
568576
&event_content.event_id().to_short(),

crates/rostra-client-db/src/migration_ops.rs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use tracing::{debug, info};
77
use crate::ids::IdsFolloweesRecordV0;
88
use crate::{
99
ContentStoreRecordOwned, Database, DbResult, DbVersionTooHighSnafu, EventContentStateNew,
10-
IdSocialProfileRecord, IdsFolloweesRecord, LOG_TARGET, Latest, SocialPostRecord,
11-
WriteTransactionCtx, content_rc, content_store, db_version, events, events_by_time,
12-
events_content, events_content_missing, events_content_state, events_heads, events_missing,
13-
events_self, events_singletons, events_singletons_new, ids_followees, ids_followees_v0,
14-
ids_followers, ids_full, ids_personas, ids_self, ids_unfollowed, social_posts,
15-
social_posts_by_time, social_posts_reactions, social_posts_replies, social_posts_v0,
16-
social_profiles, social_profiles_v0,
10+
IdSocialProfileRecord, IdsDataUsageRecord, IdsFolloweesRecord, LOG_TARGET, Latest,
11+
SocialPostRecord, WriteTransactionCtx, content_rc, content_store, db_version, events,
12+
events_by_time, events_content, events_content_missing, events_content_state, events_heads,
13+
events_missing, events_self, events_singletons, events_singletons_new, ids_data_usage,
14+
ids_followees, ids_followees_v0, ids_followers, ids_full, ids_personas, ids_self,
15+
ids_unfollowed, social_posts, social_posts_by_time, social_posts_reactions,
16+
social_posts_replies, social_posts_v0, social_profiles, social_profiles_v0,
1717
};
1818

1919
impl Database {
@@ -26,6 +26,7 @@ impl Database {
2626
tx.open_table(&ids_followees::TABLE)?;
2727
tx.open_table(&ids_unfollowed::TABLE)?;
2828
tx.open_table(&ids_personas::TABLE)?;
29+
tx.open_table(&ids_data_usage::TABLE)?;
2930

3031
tx.open_table(&events::TABLE)?;
3132
tx.open_table(&events_singletons::TABLE)?;
@@ -50,7 +51,7 @@ impl Database {
5051
}
5152

5253
pub(crate) fn handle_db_ver_migrations(dbtx: &WriteTransactionCtx) -> DbResult<()> {
53-
const DB_VER: u64 = 4;
54+
const DB_VER: u64 = 5;
5455

5556
let mut table_db_ver = dbtx.open_table(&db_version::TABLE)?;
5657

@@ -76,6 +77,7 @@ impl Database {
7677
1 => Self::migrate_v1(dbtx)?,
7778
2 => Self::migrate_v2(dbtx)?,
7879
3 => Self::migrate_v3(dbtx)?,
80+
4 => Self::migrate_v4(dbtx)?,
7981
DB_VER => { /* ensures we didn't forget to increment DB_VER */ }
8082
x => panic!("Unexpected db ver: {x}"),
8183
}
@@ -316,4 +318,60 @@ impl Database {
316318

317319
Ok(())
318320
}
321+
322+
/// Migration v4: Calculate initial data usage per identity.
323+
///
324+
/// Iterates all events to calculate:
325+
/// 1. metadata_size: count of events × EVENT_METADATA_SIZE (192 bytes)
326+
/// 2. content_size: sum of content_len for events in Available state
327+
pub(crate) fn migrate_v4(dbtx: &WriteTransactionCtx) -> DbResult<()> {
328+
use std::collections::HashMap;
329+
330+
use rostra_core::event::EventExt as _;
331+
332+
/// Size of event metadata in bytes (Event struct + signature).
333+
/// See rostra_core::event::Event documentation.
334+
const EVENT_METADATA_SIZE: u64 = 192;
335+
336+
let events_table = dbtx.open_table(&events::TABLE)?;
337+
let state_table = dbtx.open_table(&events_content_state::TABLE)?;
338+
let mut usage_table = dbtx.open_table(&ids_data_usage::TABLE)?;
339+
340+
// Collect usage per author
341+
let mut usage_map: HashMap<rostra_core::id::RostraId, IdsDataUsageRecord> = HashMap::new();
342+
343+
for entry in events_table.range(..)? {
344+
let (event_id, record) = entry?;
345+
let event_id = event_id.value();
346+
let record = record.value();
347+
let author = record.author();
348+
349+
let usage = usage_map.entry(author).or_default();
350+
351+
// Every event contributes to metadata size
352+
usage.metadata_size += EVENT_METADATA_SIZE;
353+
354+
// Content only counts if state is Available
355+
if let Some(state) = state_table.get(&event_id)?.map(|g| g.value()) {
356+
if matches!(state, EventContentStateNew::Available) {
357+
usage.content_size += u64::from(record.content_len());
358+
}
359+
}
360+
}
361+
362+
// Write aggregated usage to table
363+
let mut count = 0u64;
364+
for (author, usage) in usage_map {
365+
usage_table.insert(&author, &usage)?;
366+
count += 1;
367+
}
368+
369+
info!(
370+
target: LOG_TARGET,
371+
"Calculated data usage for {} identities",
372+
count
373+
);
374+
375+
Ok(())
376+
}
319377
}

crates/rostra-client-db/src/process_event_ops.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::process_event_content_ops::ProcessEventError;
88
use crate::{
99
Database, DbResult, InsertEventOutcome, LOG_TARGET, ProcessEventState, WriteTransactionCtx,
1010
content_rc, content_store, events, events_by_time, events_content_missing,
11-
events_content_state, events_heads, events_missing, ids_full,
11+
events_content_state, events_heads, events_missing, ids_data_usage, ids_full,
1212
};
1313

1414
impl Database {
@@ -26,6 +26,7 @@ impl Database {
2626
let mut events_heads_tbl = tx.open_table(&events_heads::TABLE)?;
2727
let mut events_by_time_tbl = tx.open_table(&events_by_time::TABLE)?;
2828
let mut ids_full_tbl = tx.open_table(&ids_full::TABLE)?;
29+
let mut ids_data_usage_tbl = tx.open_table(&ids_data_usage::TABLE)?;
2930

3031
let insert_event_outcome = Database::insert_event_tx(
3132
*event,
@@ -38,6 +39,7 @@ impl Database {
3839
&content_store_tbl,
3940
&mut content_rc_tbl,
4041
&mut events_content_missing_tbl,
42+
Some(&mut ids_data_usage_tbl),
4143
)?;
4244

4345
if let InsertEventOutcome::Inserted {
@@ -124,6 +126,7 @@ impl Database {
124126
&mut events_content_state_tbl,
125127
&mut content_rc_tbl,
126128
&mut events_content_missing_tbl,
129+
Some((event.author(), event.content_len(), &mut ids_data_usage_tbl)),
127130
)? {
128131
ProcessEventState::Pruned
129132
} else {

crates/rostra-client-db/src/tables.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,33 @@ def_table! {
148148
ids_personas: (RostraId, PersonaId) => IdsPersonaRecord
149149
}
150150

151+
def_table! {
152+
/// Aggregate data usage per identity.
153+
///
154+
/// Tracks the total storage used by each identity's events and content.
155+
/// Updated incrementally as events are added and content state changes.
156+
ids_data_usage: RostraId => IdsDataUsageRecord
157+
}
158+
159+
/// Aggregate data usage record for an identity.
160+
///
161+
/// Tracks both event metadata size (fixed per event) and content size
162+
/// (variable, based on actual content stored).
163+
#[derive(Debug, Encode, Decode, Clone, Copy, Default, Serialize)]
164+
pub struct IdsDataUsageRecord {
165+
/// Total size of event metadata (envelopes) in bytes.
166+
///
167+
/// Each event contributes a fixed size (Event struct + signature = 192
168+
/// bytes).
169+
pub metadata_size: u64,
170+
171+
/// Total size of content (payloads) in bytes.
172+
///
173+
/// Only content in the `Available` state is counted. Pruned/Deleted content
174+
/// is not included (as it doesn't consume storage).
175+
pub content_size: u64,
176+
}
177+
151178
// ============================================================================
152179
// EVENT TABLES
153180
// ============================================================================

0 commit comments

Comments
 (0)