|
1 | | -#[cfg_attr( |
2 | | - feature = "tracing-instrumentation", |
3 | | - tracing::instrument(skip(db_manager)) |
4 | | -)] |
| 1 | +use crate::{config::ServerContext, modules::queries::methods::database_view_state}; |
| 2 | +use actix_web::web::Data; |
| 3 | + |
| 4 | +#[cfg_attr(feature = "tracing-instrumentation", tracing::instrument(skip(data)))] |
5 | 5 | pub async fn get_state_from_db_paginated( |
6 | | - db_manager: &std::sync::Arc<Box<dyn database::ReaderDbManager + Sync + Send + 'static>>, |
| 6 | + data: &Data<ServerContext>, |
7 | 7 | account_id: &near_primitives::types::AccountId, |
8 | | - block_height: near_primitives::types::BlockHeight, |
| 8 | + block: &near_primitives::views::BlockView, |
9 | 9 | page_token: database::PageToken, |
10 | 10 | ) -> Result<crate::modules::state::PageStateValues, near_jsonrpc::primitives::errors::RpcError> { |
11 | 11 | tracing::debug!( |
12 | 12 | "`get_state_from_db_paginated` call. AccountId {}, block {}, page_token {:?}", |
13 | 13 | account_id, |
14 | | - block_height, |
| 14 | + block.header.height, |
15 | 15 | page_token, |
16 | 16 | ); |
17 | 17 |
|
18 | | - let (values, next_page_token) = db_manager |
19 | | - .get_state_by_page(account_id, block_height, page_token, "view_state_paginated") |
| 18 | + let account = data |
| 19 | + .db_manager |
| 20 | + .get_account(account_id, block.header.height, "query_view_state") |
| 21 | + .await |
| 22 | + .map_err( |
| 23 | + |_err| near_jsonrpc::primitives::types::query::RpcQueryError::UnknownAccount { |
| 24 | + requested_account_id: account_id.clone(), |
| 25 | + block_height: block.header.height, |
| 26 | + block_hash: block.header.hash, |
| 27 | + }, |
| 28 | + )?; |
| 29 | + |
| 30 | + // Calculate the state size excluding the contract code size to check if it's too large to fetch. |
| 31 | + // The state size is the storage usage minus the code size. |
| 32 | + // more details: nearcore/runtime/runtime/src/state_viewer/mod.rs:150 |
| 33 | + let code_len = data |
| 34 | + .db_manager |
| 35 | + .get_contract_code(account_id, block.header.height, "query_view_state") |
20 | 36 | .await |
21 | | - .map_err(|err| { |
22 | | - near_jsonrpc::primitives::errors::RpcError::new_internal_error( |
23 | | - Some(serde_json::Value::String(err.to_string())), |
24 | | - "Failed to get page state from DB. Please try again!".to_string(), |
| 37 | + .map(|code| code.data.len() as u64) |
| 38 | + .unwrap_or_default(); |
| 39 | + |
| 40 | + let state_size = account.data.storage_usage().saturating_sub(code_len); |
| 41 | + let (values, next_page_token) = if state_size <= 1_000_000 { |
| 42 | + let values = database_view_state(data, block, account_id, &[]).await?; |
| 43 | + (values, None) |
| 44 | + } else { |
| 45 | + // If the state size is too large, we try to fetch the state in pages. |
| 46 | + // This is a fallback mechanism to avoid fetching too much data at once. |
| 47 | + let (raw_values, next_page_token) = data |
| 48 | + .db_manager |
| 49 | + .get_state_by_page( |
| 50 | + account_id, |
| 51 | + block.header.height, |
| 52 | + database::PageToken::default(), |
| 53 | + "view_state_paginated", |
25 | 54 | ) |
26 | | - })?; |
27 | | - Ok(crate::modules::state::PageStateValues { |
28 | | - values: values |
| 55 | + .await |
| 56 | + .map_err(|err| { |
| 57 | + near_jsonrpc::primitives::errors::RpcError::new_internal_error( |
| 58 | + Some(serde_json::Value::String(err.to_string())), |
| 59 | + "Failed to get page state from DB. Please try again!".to_string(), |
| 60 | + ) |
| 61 | + })?; |
| 62 | + let values = raw_values |
29 | 63 | .into_iter() |
30 | 64 | .map(|(k, v)| near_primitives::views::StateItem { |
31 | 65 | key: k.into(), |
32 | 66 | value: v.into(), |
33 | 67 | }) |
34 | | - .collect(), |
| 68 | + .collect(); |
| 69 | + (values, next_page_token) |
| 70 | + }; |
| 71 | + |
| 72 | + Ok(crate::modules::state::PageStateValues { |
| 73 | + values, |
35 | 74 | next_page_token, |
36 | 75 | }) |
37 | 76 | } |
0 commit comments