Skip to content

Commit 746bea2

Browse files
make parallel calls (#96)
* make parallel calls * readme * bump version
1 parent 4a5555e commit 746bea2

File tree

8 files changed

+404
-197
lines changed

8 files changed

+404
-197
lines changed

Cargo.lock

Lines changed: 292 additions & 188 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ic-repl"
3-
version = "0.7.4"
3+
version = "0.7.5"
44
authors = ["DFINITY Team"]
55
edition = "2021"
66
default-run = "ic-repl"
@@ -47,4 +47,5 @@ qrcode = "0.13"
4747
image = { version = "0.24", default-features = false, features = ["png"] }
4848
libflate = "2.0"
4949
base64 = "0.21"
50+
futures = "0.3.30"
5051

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ic-repl [--replica [local|ic|url] | --offline [--format [json|ascii|png]]] --con
2323
| <var> <transformer>* // variable with optional transformers
2424
| fail <exp> // convert error message as text
2525
| call (as <name>)? <name> . <name> (( <exp>,* ))? // call a canister method, and store the result as a single value
26+
| par_call [ (<name> . <name> (( <exp>,* ))),* ] // make concurrent canister calls, and store the result as a tuple record
2627
| encode (<name> . <name>)? (( <exp>,* ))? // encode candid arguments as a blob value. canister.__init_args represents init args
2728
| decode (as <name> . <name>)? <exp> // decode blob as candid values
2829
| <id> ( <exp>,* ) // function application
@@ -166,11 +167,12 @@ function deploy(wasm) {
166167
167168
identity alice;
168169
let id = deploy(file("greet.wasm"));
169-
let status = call ic.canister_status(id);
170+
let canister = id.canister_id;
171+
let res = par_call [ic.canister_status(id), canister.greet("test")];
172+
let status = res[0];
170173
assert status.settings ~= record { controllers = vec { alice } };
171174
assert status.module_hash? == blob "...";
172-
let canister = id.canister_id;
173-
call canister.greet("test");
175+
assert res[1] == "Hello, test!";
174176
```
175177

176178
### wallet.sh

examples/install.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ function deploy(wasm) {
1616

1717
identity alice;
1818
let id = deploy(file("greet.wasm"));
19-
let status = call ic.canister_status(id);
19+
let canister = id.canister_id;
20+
let res = par_call [ic.canister_status(id), canister.greet("test")];
21+
let status = res[0];
2022
assert status.settings ~= record { controllers = vec { alice } };
2123
assert status.module_hash? == blob "\ab\a7h\8cH\e0]\e7W]\8b\07\92\ac\9fH\95\7f\f4\97\d0\efX\c4~\0d\83\91\01<\da\1d";
22-
let canister = id.canister_id;
23-
call canister.greet("test");
24-
assert _ == "Hello, test!";
24+
assert res[1] == "Hello, test!";
2525
call ic.stop_canister(id);
2626
call ic.delete_canister(id);

src/exp.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use candid::{
1313
utils::check_unique,
1414
Principal, TypeEnv,
1515
};
16+
use futures::future::try_join_all;
1617
use std::collections::BTreeMap;
1718

1819
#[derive(Debug, Clone)]
@@ -24,6 +25,9 @@ pub enum Exp {
2425
args: Option<Vec<Exp>>,
2526
mode: CallMode,
2627
},
28+
ParCall {
29+
calls: Vec<FuncCall>,
30+
},
2731
Decode {
2832
method: Option<Method>,
2933
blob: Box<Exp>,
@@ -57,12 +61,18 @@ pub enum CallMode {
5761
Proxy(String),
5862
}
5963
#[derive(Debug, Clone)]
64+
pub struct FuncCall {
65+
pub method: Method,
66+
pub args: Vec<Exp>,
67+
}
68+
#[derive(Debug, Clone)]
6069
pub struct Field {
6170
pub id: Label,
6271
pub val: Exp,
6372
}
6473
impl Exp {
6574
pub fn is_call(&self) -> bool {
75+
// Used to decide if we want to report profiling numbers. Ignore par_call for now
6676
matches!(
6777
self,
6878
Exp::Call {
@@ -568,6 +578,42 @@ impl Exp {
568578
};
569579
args_to_value(args)
570580
}
581+
Exp::ParCall { calls } => {
582+
let mut futures = Vec::with_capacity(calls.len());
583+
for call in calls {
584+
let mut args = Vec::with_capacity(call.args.len());
585+
for arg in call.args.into_iter() {
586+
args.push(arg.eval(helper)?);
587+
}
588+
let args = IDLArgs { args };
589+
let info = call.method.get_info(helper, false)?;
590+
let bytes = if let Some((env, func)) = &info.signature {
591+
args.to_bytes_with_types(env, &func.args)?
592+
} else {
593+
args.to_bytes()?
594+
};
595+
let method = &call.method.method;
596+
let effective_id = get_effective_canister_id(info.canister_id, method, &bytes)?;
597+
let mut builder = helper.agent.update(&info.canister_id, method);
598+
builder = builder
599+
.with_arg(bytes)
600+
.with_effective_canister_id(effective_id);
601+
let call_future = async move {
602+
let res = builder.call_and_wait().await?;
603+
if let Some((env, func)) = &info.signature {
604+
Ok(IDLArgs::from_bytes_with_types(&res, env, &func.rets)?)
605+
} else {
606+
Ok(IDLArgs::from_bytes(&res)?)
607+
}
608+
};
609+
futures.push(call_future);
610+
}
611+
let res = parallel_calls(futures)?;
612+
let res = IDLArgs {
613+
args: res.into_iter().map(args_to_value).collect(),
614+
};
615+
args_to_value(res)
616+
}
571617
Exp::Call { method, args, mode } => {
572618
let args = if let Some(args) = args {
573619
let mut res = Vec::with_capacity(args.len());
@@ -866,6 +912,13 @@ pub fn apply_func(helper: &MyHelper, func: &str, args: Vec<IDLValue>) -> Result<
866912
}
867913
}
868914
}
915+
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
916+
async fn parallel_calls(
917+
futures: Vec<impl std::future::Future<Output = anyhow::Result<IDLArgs>>>,
918+
) -> anyhow::Result<Vec<IDLArgs>> {
919+
let res = try_join_all(futures).await?;
920+
Ok(res)
921+
}
869922
#[tokio::main]
870923
async fn call(
871924
helper: &MyHelper,

src/grammar.lalrpop

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::exp::{Field, Exp, Method, CallMode};
1+
use super::exp::{Field, Exp, Method, CallMode, FuncCall};
22
use super::selector::Selector;
33
use candid_parser::types::{IDLType, TypeField, PrimType, FuncType, Binding};
44
use candid::utils::check_unique;
@@ -34,6 +34,7 @@ extern {
3434
"load" => Token::Load,
3535
"principal" => Token::Principal,
3636
"call" => Token::Call,
37+
"par_call" => Token::ParCall,
3738
"encode" => Token::Encode,
3839
"decode" => Token::Decode,
3940
"as" => Token::As,
@@ -101,11 +102,13 @@ pub Exp: Exp = {
101102
Variable => <>,
102103
"fail" <Exp> => Exp::Fail(Box::new(<>)),
103104
"call" <method:Method> <args:Exps?> => Exp::Call{method:Some(method), args, mode: CallMode::Call},
105+
"par_call" "[" <calls:SepBy<FuncCall, ",">> "]" => Exp::ParCall { calls },
104106
"call" "as" <proxy:Name> <method:Method> <args:Exps?> => Exp::Call{method:Some(method), args, mode: CallMode::Proxy(proxy)},
105107
"encode" <method:Method?> <args:Exps?> => Exp::Call{method, args, mode: CallMode::Encode},
106108
"decode" <method:("as" <Method>)?> <blob:Exp> => Exp::Decode{method, blob:Box::new(blob)},
107109
<func:"id"> "(" <args:SepBy<Exp, ",">> ")" => Exp::Apply(func, args),
108110
}
111+
FuncCall: FuncCall = <method:Method> <args:Exps> => FuncCall { method, args };
109112
Variable: Exp = <v:"id"> <path:(<Selector>)*> => Exp::Path(v, path);
110113
Selector: Selector = {
111114
"?" => Selector::Option,

src/ic.did

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ type change_details = variant {
4545
mode : variant { install; reinstall; upgrade };
4646
module_hash : blob;
4747
};
48+
load_snapshot : record {
49+
canister_version : nat64;
50+
taken_at_timestamp : nat64;
51+
};
4852
controllers_change : record {
4953
controllers : vec principal;
5054
};
@@ -341,6 +345,38 @@ type bitcoin_get_balance_query_result = satoshi;
341345

342346
type bitcoin_get_current_fee_percentiles_result = vec millisatoshi_per_byte;
343347

348+
type snapshot_id = blob;
349+
350+
type snapshot = record {
351+
id : snapshot_id;
352+
taken_at_timestamp : nat64;
353+
total_size : nat64;
354+
};
355+
356+
type take_canister_snapshot_args = record {
357+
canister_id : canister_id;
358+
replace_snapshot : opt snapshot_id;
359+
};
360+
361+
type take_canister_snapshot_result = snapshot;
362+
363+
type load_canister_snapshot_args = record {
364+
canister_id : canister_id;
365+
snapshot_id : snapshot_id;
366+
sender_canister_version : opt nat64;
367+
};
368+
369+
type list_canister_snapshots_args = record {
370+
canister_id : canister_id;
371+
};
372+
373+
type list_canister_snapshots_result = vec snapshot;
374+
375+
type delete_canister_snapshot_args = record {
376+
canister_id : canister_id;
377+
snapshot_id : snapshot_id;
378+
};
379+
344380
type fetch_canister_logs_args = record {
345381
canister_id : canister_id;
346382
};
@@ -392,6 +428,12 @@ service ic : {
392428
provisional_create_canister_with_cycles : (provisional_create_canister_with_cycles_args) -> (provisional_create_canister_with_cycles_result);
393429
provisional_top_up_canister : (provisional_top_up_canister_args) -> ();
394430

431+
// Canister snapshots
432+
take_canister_snapshot : (take_canister_snapshot_args) -> (take_canister_snapshot_result);
433+
load_canister_snapshot : (load_canister_snapshot_args) -> ();
434+
list_canister_snapshots : (list_canister_snapshots_args) -> (list_canister_snapshots_result);
435+
delete_canister_snapshot : (delete_canister_snapshot_args) -> ();
436+
395437
// canister logging
396438
fetch_canister_logs : (fetch_canister_logs_args) -> (fetch_canister_logs_result) query;
397439
};

src/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub enum Token {
6363
Fail,
6464
#[token("call")]
6565
Call,
66+
#[token("par_call")]
67+
ParCall,
6668
#[token("encode")]
6769
Encode,
6870
#[token("decode")]

0 commit comments

Comments
 (0)