Skip to content

Commit 89844fb

Browse files
authored
Fix parquet default processor and clean up a bit (#480)
* - removed table_metadata from parquet, which is a problematic in terms of not being sent to channel [todo: rootcause] - added fields to logs for easier debugging - moved parquet periodic upload logic before looping structs. - made parquet gap detector logic simpler. * fix events * fix token_v2 version tracking
1 parent 7069b0c commit 89844fb

File tree

7 files changed

+72
-117
lines changed

7 files changed

+72
-117
lines changed

rust/processor/src/bq_analytics/generic_parquet_processor.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ where
131131
) -> Result<()> {
132132
let parquet_structs = changes.data;
133133
let processor_name = self.processor_name.clone();
134+
135+
if self.last_upload_time.elapsed() >= self.upload_interval {
136+
info!(
137+
"Time has elapsed more than {} since last upload for {}",
138+
self.upload_interval.as_secs(),
139+
ParquetType::TABLE_NAME
140+
);
141+
if let Err(e) = self.upload_buffer(gcs_client).await {
142+
error!("Failed to upload buffer: {}", e);
143+
return Err(e);
144+
}
145+
self.last_upload_time = Instant::now();
146+
}
147+
134148
for parquet_struct in parquet_structs {
135149
let size_of_struct = allocative::size_of_unique(&parquet_struct);
136150
PARQUET_STRUCT_SIZE
@@ -154,19 +168,6 @@ where
154168
}
155169
}
156170

157-
if self.last_upload_time.elapsed() >= self.upload_interval {
158-
info!(
159-
"Time has elapsed more than {} since last upload for {}",
160-
self.upload_interval.as_secs(),
161-
ParquetType::TABLE_NAME
162-
);
163-
if let Err(e) = self.upload_buffer(gcs_client).await {
164-
error!("Failed to upload buffer: {}", e);
165-
return Err(e);
166-
}
167-
self.last_upload_time = Instant::now();
168-
}
169-
170171
PARQUET_HANDLER_CURRENT_BUFFER_SIZE
171172
.with_label_values(&[&self.processor_name, ParquetType::TABLE_NAME])
172173
.set(self.buffer_size_bytes as i64);
@@ -252,7 +253,12 @@ where
252253
parquet_processed_structs: Some(parquet_processed_transactions),
253254
table_name: ParquetType::TABLE_NAME.to_string(),
254255
};
255-
256+
info!(
257+
table_name = ParquetType::TABLE_NAME,
258+
start_version = start_version,
259+
end_version = end_version,
260+
"Uploaded parquet to GCS and sending result to gap detector."
261+
);
256262
self.gap_detector_sender
257263
.send(ProcessingResult::ParquetProcessingResult(
258264
parquet_processing_result,

rust/processor/src/gap_detectors/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,6 @@ pub async fn create_gap_detector_status_tracker_loop(
125125
}
126126
},
127127
Ok(ProcessingResult::ParquetProcessingResult(result)) => {
128-
tracing::info!(
129-
processor_name,
130-
service_type = PROCESSOR_SERVICE_TYPE,
131-
"[ParquetGapDetector] received parquet gap detector task",
132-
);
133128
match gap_detector
134129
.process_versions(ProcessingResult::ParquetProcessingResult(result))
135130
{

rust/processor/src/gap_detectors/parquet_gap_detector.rs

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::gap_detectors::{GapDetectorResult, GapDetectorTrait, ProcessingResult
55
use ahash::{AHashMap, AHashSet};
66
use anyhow::Result;
77
use std::{
8-
cmp::{max, min},
8+
cmp::max,
99
sync::{Arc, Mutex},
1010
};
1111
use tracing::{debug, info};
@@ -64,62 +64,55 @@ impl ParquetFileGapDetectorInner {
6464
}
6565
}
6666
self.max_version = max(self.max_version, end_version);
67-
68-
// b/c of the case where file gets uploaded first, we should check if we have to update last_success_version for this processor
69-
self.update_next_version_to_process(
70-
min(self.next_version_to_process, end_version),
71-
"all_table",
72-
);
7367
}
7468

75-
/// This function updates the next version to process based on the current version counters.
76-
/// It will increment the next version to process if the current version is fully processed.
77-
/// It will also remove the version from the version counters if it is fully processed.
78-
/// what it means to be fully processed is that all the structs for that version processed, i.e. count = 0.
79-
/// Note that for tables other than transactions, it won't be always the latest txn version since we update this value with
80-
/// Thus we will keep the latest version_to_process in the db with the min(max version of latest table files per processor)
81-
/// that has been uploaded to GCS. so whenever we restart the processor, it may generate some duplicates rows, and we are okay with that.
69+
/// This function updates the `next_version_to_process` based on the current version counters.
70+
/// It increments the `next_version_to_process` if the current version is fully processed, which means
71+
/// that all the structs for that version have been processed, i.e., `count = 0`.
72+
/// If a version is fully processed, it removes the version from the version counters and adds it to the `seen_versions`.
73+
/// For tables other than transactions, the latest version to process may not always be the most recent transaction version
74+
/// since this value is updated based on the minimum of the maximum versions of the latest table files per processor
75+
/// that have been uploaded to GCS. Therefore, when the processor restarts, some duplicate rows may be generated, which is acceptable.
76+
/// The function also ensures that the current version starts checking from the `next_version_to_process`
77+
/// value stored in the database. While there might be potential performance improvements,
78+
/// the current implementation prioritizes data integrity.
79+
/// The function also handles cases where a version is already processed or where no struct count
80+
/// is found for a version, providing appropriate logging for these scenarios.
8281
pub fn update_next_version_to_process(&mut self, end_version: i64, table_name: &str) {
8382
// this has to start checking with this value all the time, since this is the value that will be stored in the db as well.
8483
// maybe there could be an improvement to be more performant. but priortizing the data integrity as of now.
8584
let mut current_version = self.next_version_to_process;
8685

8786
while current_version <= end_version {
88-
#[allow(clippy::collapsible_else_if)]
89-
if self.version_counters.contains_key(&current_version) {
90-
while let Some(&count) = self.version_counters.get(&current_version) {
91-
if current_version > end_version {
92-
// we shouldn't update further b/c we haven't uploaded the files containing versions after end_version.
93-
break;
94-
}
95-
if count == 0 {
96-
self.version_counters.remove(&current_version);
97-
self.seen_versions.insert(current_version); // seen_version holds the txns version that we have processed already
98-
current_version += 1;
99-
self.next_version_to_process += 1;
100-
} else {
101-
break;
102-
}
103-
}
104-
} else {
105-
if self.seen_versions.contains(&current_version) {
106-
debug!(
107-
"Version {} already processed, skipping and current next_version {} ",
108-
current_version, self.next_version_to_process
109-
);
110-
self.next_version_to_process =
111-
max(self.next_version_to_process, current_version + 1);
87+
// If the current version has a struct count entry
88+
if let Some(&count) = self.version_counters.get(&current_version) {
89+
if count == 0 {
90+
self.version_counters.remove(&current_version);
91+
self.seen_versions.insert(current_version);
92+
self.next_version_to_process += 1;
11293
} else {
113-
// this is the case where we haven't updated the map yet, while the file gets uploaded first. the bigger file size we will have,
114-
// the less chance we will see this as upload takes longer time. And map population is done before the upload.
115-
debug!(
116-
current_version = current_version,
117-
"No struct count found for version. This shouldn't happen b/c we already added default count for this version."
118-
);
94+
// Stop processing if the version is not yet complete
95+
break;
11996
}
97+
} else if self.seen_versions.contains(&current_version) {
98+
// If the version is already seen and processed
99+
debug!(
100+
"Version {} already processed, skipping and current next_version {} ",
101+
current_version, self.next_version_to_process
102+
);
103+
self.next_version_to_process =
104+
max(self.next_version_to_process, current_version + 1);
105+
} else {
106+
// If the version is neither in seen_versions nor version_counters
107+
debug!(
108+
current_version = current_version,
109+
"No struct count found for version. This shouldn't happen b/c we already added default count for this version."
110+
);
120111
}
112+
121113
current_version += 1;
122114
}
115+
123116
debug!(
124117
next_version_to_process = self.next_version_to_process,
125118
table_name = table_name,
@@ -155,7 +148,8 @@ impl GapDetectorTrait for ParquetFileGapDetectorInner {
155148
info!(
156149
start_version = result.start_version,
157150
end_version = result.end_version,
158-
"Parquet file has been uploaded."
151+
table_name = &result.table_name,
152+
"[Parquet Gap Detector] Processing versions after parquet file upload."
159153
);
160154

161155
for (version, count) in parquet_processed_structs.iter() {
@@ -166,7 +160,6 @@ impl GapDetectorTrait for ParquetFileGapDetectorInner {
166160
self.version_counters.insert(*version, -count);
167161
}
168162
}
169-
170163
self.update_next_version_to_process(result.end_version, &result.table_name);
171164

172165
Ok(GapDetectorResult::ParquetFileGapDetectorResult(

rust/processor/src/processors/parquet_processors/parquet_default_processor.rs

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
db::common::models::default_models::{
1010
parquet_move_modules::MoveModule,
1111
parquet_move_resources::MoveResource,
12-
parquet_move_tables::{TableItem, TableMetadata},
12+
parquet_move_tables::TableItem,
1313
parquet_transactions::{Transaction as ParquetTransaction, TransactionModel},
1414
parquet_write_set_changes::{WriteSetChangeDetail, WriteSetChangeModel},
1515
},
@@ -51,7 +51,6 @@ pub struct ParquetDefaultProcessor {
5151
wsc_sender: AsyncSender<ParquetDataGeneric<WriteSetChangeModel>>,
5252
table_item_sender: AsyncSender<ParquetDataGeneric<TableItem>>,
5353
move_module_sender: AsyncSender<ParquetDataGeneric<MoveModule>>,
54-
table_metadata_sender: AsyncSender<ParquetDataGeneric<TableMetadata>>,
5554
}
5655

5756
// TODO: Since each table item has different size allocated, the pace of being backfilled to PQ varies a lot.
@@ -113,24 +112,13 @@ impl ParquetDefaultProcessor {
113112
config.parquet_upload_interval_in_secs(),
114113
);
115114

116-
let table_metadata_sender = create_parquet_handler_loop::<TableMetadata>(
117-
new_gap_detector_sender.clone(),
118-
ProcessorName::ParquetDefaultProcessor.into(),
119-
config.bucket_name.clone(),
120-
config.bucket_root.clone(),
121-
config.parquet_handler_response_channel_size,
122-
config.max_buffer_size,
123-
config.parquet_upload_interval_in_secs(),
124-
);
125-
126115
Self {
127116
connection_pool,
128117
transaction_sender,
129118
move_resource_sender,
130119
wsc_sender,
131120
table_item_sender,
132121
move_module_sender,
133-
table_metadata_sender,
134122
}
135123
}
136124
}
@@ -139,13 +127,12 @@ impl Debug for ParquetDefaultProcessor {
139127
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
140128
write!(
141129
f,
142-
"ParquetProcessor {{ capacity of trnasactions channel: {:?}, capacity of move resource channel: {:?}, capacity of wsc channel: {:?}, capacity of table items channel: {:?}, capacity of move_module channel: {:?}, capacity of table_metadata channel: {:?} }}",
130+
"ParquetProcessor {{ capacity of trnasactions channel: {:?}, capacity of move resource channel: {:?}, capacity of wsc channel: {:?}, capacity of table items channel: {:?}, capacity of move_module channel: {:?}}}",
143131
&self.transaction_sender.capacity(),
144132
&self.move_resource_sender.capacity(),
145133
&self.wsc_sender.capacity(),
146134
&self.table_item_sender.capacity(),
147135
&self.move_module_sender.capacity(),
148-
&self.table_metadata_sender.capacity(),
149136
)
150137
}
151138
}
@@ -166,14 +153,7 @@ impl ProcessorTrait for ParquetDefaultProcessor {
166153
let last_transaction_timestamp = transactions.last().unwrap().timestamp.clone();
167154

168155
let (
169-
(
170-
move_resources,
171-
write_set_changes,
172-
transactions,
173-
table_items,
174-
move_modules,
175-
table_metadata,
176-
),
156+
(move_resources, write_set_changes, transactions, table_items, move_modules),
177157
transaction_version_to_struct_count,
178158
) = tokio::task::spawn_blocking(move || process_transactions(transactions))
179159
.await
@@ -216,15 +196,6 @@ impl ProcessorTrait for ParquetDefaultProcessor {
216196
.await
217197
.map_err(|e| anyhow!("Failed to send to parquet manager: {}", e))?;
218198

219-
let tm_parquet_data = ParquetDataGeneric {
220-
data: table_metadata,
221-
};
222-
223-
self.table_metadata_sender
224-
.send(tm_parquet_data)
225-
.await
226-
.map_err(|e| anyhow!("Failed to send to parquet manager: {}", e))?;
227-
228199
Ok(ProcessingResult::ParquetProcessingResult(
229200
ParquetProcessingResult {
230201
start_version: start_version as i64,
@@ -251,7 +222,6 @@ pub fn process_transactions(
251222
Vec<TransactionModel>,
252223
Vec<TableItem>,
253224
Vec<MoveModule>,
254-
Vec<TableMetadata>,
255225
),
256226
AHashMap<i64, i64>,
257227
) {
@@ -265,7 +235,6 @@ pub fn process_transactions(
265235
let mut move_modules = vec![];
266236
let mut move_resources = vec![];
267237
let mut table_items = vec![];
268-
let mut table_metadata: AHashMap<String, TableMetadata> = AHashMap::new();
269238

270239
for detail in wsc_details {
271240
match detail {
@@ -280,38 +249,28 @@ pub fn process_transactions(
280249
WriteSetChangeDetail::Resource(resource) => {
281250
transaction_version_to_struct_count
282251
.entry(resource.txn_version)
283-
.and_modify(|e| *e += 1);
252+
.and_modify(|e| *e += 1)
253+
.or_insert(1);
284254
move_resources.push(resource);
285255
},
286-
WriteSetChangeDetail::Table(item, _current_item, metadata) => {
256+
WriteSetChangeDetail::Table(item, _current_item, _) => {
287257
let txn_version = item.txn_version;
288258
transaction_version_to_struct_count
289259
.entry(txn_version)
290-
.and_modify(|e| *e += 1);
260+
.and_modify(|e| *e += 1)
261+
.or_insert(1);
291262
table_items.push(item);
292-
293-
if let Some(meta) = metadata {
294-
table_metadata.insert(meta.handle.clone(), meta);
295-
transaction_version_to_struct_count
296-
.entry(txn_version)
297-
.and_modify(|e| *e += 1);
298-
}
299263
},
300264
}
301265
}
302266

303-
let mut table_metadata = table_metadata.into_values().collect::<Vec<TableMetadata>>();
304-
// Sort by PK
305-
table_metadata.sort_by(|a, b| a.handle.cmp(&b.handle));
306-
307267
(
308268
(
309269
move_resources,
310270
write_set_changes,
311271
txns,
312272
table_items,
313273
move_modules,
314-
table_metadata,
315274
),
316275
transaction_version_to_struct_count,
317276
)

rust/processor/src/processors/parquet_processors/parquet_events_processor.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ impl ProcessorTrait for ParquetEventsProcessor {
141141
);
142142
transaction_version_to_struct_count
143143
.entry(txn_version)
144-
.and_modify(|e| *e += txn_events.len() as i64);
144+
.and_modify(|e| *e += txn_events.len() as i64)
145+
.or_insert(txn_events.len() as i64);
146+
145147
events.extend(txn_events);
146148
}
147149

rust/processor/src/processors/parquet_processors/parquet_fungible_asset_processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl Debug for ParquetFungibleAssetProcessor {
9292
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9393
write!(
9494
f,
95-
"ParquetFungibleAssetProcessor {{ capacity of tsi channel: {:?}, capacity of es channel: {:?}}}",
95+
"ParquetFungibleAssetProcessor {{ capacity of coin_supply channel: {:?}, capacity of fungible_asset_balances channel: {:?}}}",
9696
&self.coin_supply_sender.capacity(),
9797
&self.fungible_asset_balances_sender.capacity(),
9898
)

rust/processor/src/processors/parquet_processors/parquet_token_v2_processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ async fn parse_v2_token(
427427
transaction_version_to_struct_count
428428
.entry(txn_version)
429429
.and_modify(|e| *e += ownerships.len() as i64 + 1)
430-
.or_insert(1);
430+
.or_insert(ownerships.len() as i64 + 1);
431431
token_ownerships_v2.append(&mut ownerships);
432432
token_datas_v2.push(token_data);
433433
}

0 commit comments

Comments
 (0)