Skip to content

Commit

Permalink
chore: tests for Lazy and moving out of unstable
Browse files Browse the repository at this point in the history
  • Loading branch information
ruseinov committed Dec 1, 2024
1 parent d7c16c8 commit 464d1c7
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 15 deletions.
4 changes: 0 additions & 4 deletions near-sdk/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,10 @@
//! place of a type [`Option<T>`](Option). Will only be loaded when interacted with and will
//! persist on [`Drop`].
#[cfg(feature = "unstable")]
mod lazy;
#[cfg(feature = "unstable")]
pub use lazy::Lazy;

#[cfg(feature = "unstable")]
mod lazy_option;
#[cfg(feature = "unstable")]
pub use lazy_option::LazyOption;

pub mod vec;
Expand Down
80 changes: 69 additions & 11 deletions near-sdk/tests/store_performance_tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// As wasm VM performance is tested, there is no need to test this on other types of OS.
// This test runs only on Linux, as it's much slower on OS X due to an interpreted VM.
#![cfg(target_os = "linux")]
// #![cfg(target_os = "linux")]

use near_account_id::AccountId;
use near_gas::NearGas;
Expand All @@ -25,6 +25,12 @@ pub enum Collection {
LookupSet,
TreeMap,
Vector,
LazyOption,
}

pub enum Contract {
StoreContract,
LazyContract,
}

fn random_account_id(collection: Collection, seed: &str) -> AccountId {
Expand Down Expand Up @@ -53,9 +59,13 @@ async fn dev_generate(
Ok((account.into_result()?, collection))
}

async fn setup_worker() -> anyhow::Result<(Arc<Worker<Sandbox>>, AccountId)> {
async fn setup_worker(contract: Contract) -> anyhow::Result<(Arc<Worker<Sandbox>>, AccountId)> {
let contract_path = match contract {
Contract::StoreContract => "./tests/test-contracts/store",
Contract::LazyContract => "./tests/test-contracts/lazy",
};
let worker = Arc::new(near_workspaces::sandbox().await?);
let wasm = near_workspaces::compile_project("./tests/test-contracts/store").await?;
let wasm = near_workspaces::compile_project(contract_path).await?;
let contract = worker.dev_deploy(&wasm).await?;
let res = contract.call("new").max_gas().transact().await?;
assert!(res.is_success());
Expand All @@ -80,7 +90,7 @@ fn perform_asserts(total_gas: u64, col: &Collection) {

#[allow(unused)]
async fn setup_several(num: usize) -> anyhow::Result<(Vec<Account>, AccountId)> {
let (worker, contract_id) = setup_worker().await?;
let (worker, contract_id) = setup_worker(Contract::StoreContract).await?;
let mut accounts = Vec::new();

for acc_seed in 0..num {
Expand All @@ -92,8 +102,8 @@ async fn setup_several(num: usize) -> anyhow::Result<(Vec<Account>, AccountId)>
Ok((accounts, contract_id))
}

async fn setup() -> anyhow::Result<(Account, AccountId)> {
let (worker, contract_id) = setup_worker().await?;
async fn setup(contract: Contract) -> anyhow::Result<(Account, AccountId)> {
let (worker, contract_id) = setup_worker(contract).await?;

let (account, _) =
dev_generate(worker.clone(), Collection::IterableSet, "seed".to_string()).await?;
Expand All @@ -114,7 +124,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
Collection::Vector,
];

let (account, contract_id) = setup().await?;
let (account, contract_id) = setup(Contract::StoreContract).await?;
// insert test, max_iterations here is the number of elements to insert. It's used to measure
// relative performance.
for (col, max_iterations) in collection_types.map(|col| match col {
Expand All @@ -126,6 +136,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
Collection::LookupMap => (col, 650),
Collection::LookupSet => (col, 1020),
Collection::Vector => (col, 1080),
_ => (col, 0),
}) {
let total_gas = account
.call(&contract_id, "insert")
Expand All @@ -151,6 +162,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
Collection::LookupMap => (col, 520),
Collection::LookupSet => (col, 1050),
Collection::Vector => (col, 530),
_ => (col, 0),
}) {
let total_gas = account
.call(&contract_id, "remove")
Expand Down Expand Up @@ -181,7 +193,7 @@ async fn iter() -> anyhow::Result<()> {
];

let element_number = 100;
let (account, contract_id) = setup().await?;
let (account, contract_id) = setup(Contract::StoreContract).await?;

// pre-populate
for col in collection_types {
Expand Down Expand Up @@ -234,7 +246,7 @@ async fn random_access() -> anyhow::Result<()> {
Collection::Vector,
];
let element_number = 100;
let (account, contract_id) = setup().await?;
let (account, contract_id) = setup(Contract::StoreContract).await?;

// pre-populate
for col in collection_types {
Expand Down Expand Up @@ -297,7 +309,7 @@ async fn contains() -> anyhow::Result<()> {
];
// Each collection gets the same number of elements.
let element_number = 100;
let (account, contract_id) = setup().await?;
let (account, contract_id) = setup(Contract::StoreContract).await?;

// prepopulate
for col in collection_types {
Expand Down Expand Up @@ -344,7 +356,7 @@ async fn contains() -> anyhow::Result<()> {
async fn iterable_vs_unordered() -> anyhow::Result<()> {
let element_number = 300;
let deleted_element_number = 299;
let (account, contract_id) = setup().await?;
let (account, contract_id) = setup(Contract::StoreContract).await?;

// We only care about Unordered* and Iterable* collections.
let collection_types = &[
Expand Down Expand Up @@ -422,3 +434,49 @@ async fn iterable_vs_unordered() -> anyhow::Result<()> {

Ok(())
}

#[tokio::test]
async fn test_lazy() -> anyhow::Result<()> {
let (account, contract_id) = setup(Contract::LazyContract).await?;

let res = account
.call(&contract_id, "insert_delete")
.args_json((700,))
.max_gas()
.transact()
.await?
.unwrap();

perform_asserts(res.total_gas_burnt.as_gas(), &Collection::LazyOption);

let res = account
.call(&contract_id, "insert_delete_flush_once")
.args_json((1700,))
.max_gas()
.transact()
.await?
.unwrap();

perform_asserts(res.total_gas_burnt.as_gas(), &Collection::LazyOption);

let res = account
.call(&contract_id, "flush")
.args_json((2400000,))
.max_gas()
.transact()
.await?
.unwrap();

perform_asserts(res.total_gas_burnt.as_gas(), &Collection::LazyOption);

let res = account
.call(&contract_id, "insert_flush")
.args_json((1200,))
.max_gas()
.transact()
.await?
.unwrap();

perform_asserts(res.total_gas_burnt.as_gas(), &Collection::LazyOption);
Ok(())
}
12 changes: 12 additions & 0 deletions near-sdk/tests/test-contracts/lazy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lazy"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
near-sdk = { path = "../../../../near-sdk", features = ["default", "unstable"] }

[workspace]
74 changes: 74 additions & 0 deletions near-sdk/tests/test-contracts/lazy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::{env, near, store::LazyOption, PanicOnDefault};

#[derive(BorshSerialize, BorshDeserialize, Ord, PartialOrd, Eq, PartialEq, Clone)]
#[borsh(crate = "near_sdk::borsh")]
pub struct Insertable {
pub index: u32,
pub data: String,
pub is_valid: bool,
}

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct LazyContract {
pub lazy_opt: LazyOption<Insertable>,
}

#[near]
impl LazyContract {
#[init]
pub fn new() -> Self {
let lazy_opt = LazyOption::new(b"a", None);
Self { lazy_opt }
}

fn insertable(&self) -> Insertable {
Insertable { index: 0, data: "scatter cinnamon wheel useless please rough situate iron eager noise try evolve runway neglect onion".to_string(), is_valid: true }
}

/// This should only write to the underlying storage once.
#[payable]
pub fn flush(&mut self, iterations: usize) {
let insertable = self.insertable();
self.lazy_opt.set(Some(insertable));

for _ in 0..=iterations {
self.lazy_opt.flush();
}
}

/// This should write on each iteration.
#[payable]
pub fn insert_flush(&mut self, iterations: u32) {
let mut insertable = self.insertable();
for idx in 0..=iterations {
insertable.index = idx as u32;
self.lazy_opt.set(Some(insertable.clone()));
self.lazy_opt.flush();
}
}

/// This should write and delete on each iteration.
#[payable]
pub fn insert_delete(&mut self, iterations: u32) {
let insertable = self.insertable();
for _ in 0..=iterations {
self.lazy_opt.set(Some(insertable.clone()));
self.lazy_opt.flush();
self.lazy_opt.set(None);
self.lazy_opt.flush();
}
}

/// This should write once on each iteration.
#[payable]
pub fn insert_delete_flush_once(&mut self, iterations: u32) {
let insertable = self.insertable();
for _ in 0..=iterations {
self.lazy_opt.set(Some(insertable.clone()));
self.lazy_opt.set(None);
self.lazy_opt.flush();
}
}
}

0 comments on commit 464d1c7

Please sign in to comment.