feat: flexible canister http outcalls - #254
Conversation
|
🤖 Here's your preview: https://keluh-vqaaa-aaaam-ai7wa-cai.icp0.io |
Implement a new system API function allowing a canister to determine the size of its current subnet. This will be helpful for flexible outcalls, in which a canister can individually specify how many replicas should perform a request, and how many of them should produce a response. This only makes sense within the context of the own subnet size. Specified here (draft): dfinity/developer-docs#254 --------- Co-authored-by: Pierugo Pace <pierugo.pace@dfinity.org>
|
Flagging a downstream dependency, since this PR touches only the spec files ( Three non-spec pages encode the pricing-v1 assumptions this PR deprecates, and they will need updating in lockstep when v2 becomes the default. Listing them so they are not missed:
Line numbers are against For whoever picks this up: as of // rs/types/management_canister_types/src/http.rs
pub const DEFAULT_HTTP_OUTCALLS_PRICING_VERSION: u32 = PRICING_VERSION_LEGACY;
pub const ALLOWED_HTTP_OUTCALLS_PRICING_VERSIONS: &[u32] = &[PRICING_VERSION_LEGACY];The |
… default cost (#352) Closes #351. The issue reported two problems with how the HTTPS outcalls pages describe `max_response_bytes`. Both are confirmed against the [interface spec](https://github.com/dfinity/developer-docs/blob/main/docs/references/ic-interface-spec/management-canister.md) and fixed here, along with several further defects found while fixing them. ## What the issue reported **1. Wrong byte figure.** `2,097,152` → `2,000,000`. The spec: *"the default value of `2MB` (`2,000,000B`) is used as the limit."* Confirmed in the replica as `MAX_CANISTER_HTTP_RESPONSE_BYTES = 2_000_000`. **2. The limit is not body-scoped.** The spec defines the measured quantity as *"the total number of bytes representing the names and values of HTTP headers and the HTTP body."* Both pages now say headers plus body. **3. The transform bound** (raised in the issue body). `max_response_bytes` is enforced **twice**: on the raw response as it arrives, and again on the transform's output. A transform cannot rescue a response that already exceeded the cap, because the first check runs before the transform does; it only keeps the transform's own output within the cap. Stated in the guide's transform section, where a reader would form the "I'll strip headers to fit" plan, and in the concepts Limitations bullet. ## Additional defects found **4. The default-size cost was wrong on both pages.** Both said omitting `max_response_bytes` costs *~21.5 billion cycles*. The formula already published on `references/cycle-costs.md` gives: ``` 49_140_000 + 10_400 * 2_000_000 = 20_849_140_000 (~20.85 billion) ``` Corrected to ~20.85 billion in both places. 21.5B matches neither the decimal nor the binary reading, so it appears independently wrong rather than downstream of the byte-figure error. **5. `references/cycle-costs.md` said `max_response_bytes` defaults to "2 MiB".** Same decimal-vs-binary error, on the page the other two link to for exact pricing. Corrected, with the resulting cycle figure added. **6. Both pages claimed a single ~30 second timeout, and the guide said the call *traps*.** There are two timeouts and neither traps: | Trigger | Reject | Message | |---|---|---| | Remote server silent for 30s | `SysFatal` | `Timeout expired` | | Subnet produces no response within 60s | `SysTransient` | `Canister http request timed out` | Telling readers to expect a trap points them at the wrong error handling. **7. The Motoko cycle guidance was stale.** Both pages said *"In Motoko, cycles must be attached explicitly with `await (with cycles = ...)`"*. The `ic` package provides `Call.httpRequest`, which computes the exact cost via `ic0.cost_http_request` and attaches it, matching the Rust wrapper. The pages now also explain why a hand-picked margin is counterproductive: attached cycles are held for the duration of the call, so a margin caps outcall concurrency. ## Submodule bump Item 7 could not be fixed in prose alone, because the embedded Motoko snippets hardcoded `with cycles = 230_949_972_000`: correcting the text would have left the page contradicting its own code. That was fixed upstream first in dfinity/examples#1477, merged as `b4fe175`. `.sources/examples` is bumped `d4ea422` → `b4fe175` here, so the snippets now render `await Call.httpRequest(request)` and code and prose agree. The old pin predated the examples restructure, so all six `snippet=` paths moved and are updated: ``` send_http_{get,post}/src/send_http_{get,post}_backend/main.mo -> send_http_{get,post}/backend/main.mo send_http_{get,post}/src/send_http_{get,post}_backend/src/lib.rs -> send_http_{get,post}/backend/src/lib.rs ``` Region names (`transform`, `get_request`, `post_request`) are unchanged. Per `.agents/submodule-bumping.md`: `guides/backends/https-outcalls.mdx` is the only page using `CodeExample`, so no other page is affected by the moves, and `examples` tracks master so it carries no `.sources/VERSIONS` entry. ## Scope Kept deliberately tight per `CONTRIBUTING.md`: `concepts/` stays explanatory, and the spec's header limits (≤64 headers, ≤8 KiB per name or value, ≤48 KiB total) are **not** added. The issue marked them optional, and enumerating them duplicates content that belongs in the interface spec and the `https-outcalls` skill. ## Verification - `npm run validate`: no errors in the touched files. - `build_and_deploy`: passing against the new submodule. This is the meaningful check for the bump, since `remark-snippet` treats a missing file or region as a hard build error. - Before pushing the bump, all six file+region pairs were confirmed to resolve at `b4fe175` by replicating the plugin's extraction logic. ## Related - dfinity/icskills#361 carries the same corrections in the `https-outcalls` skill, including the reject-message set these pages do not enumerate. - #254 (flexible outcalls) will invalidate the v1 pricing assumptions on these pages when it lands: `max_response_bytes` is *ignored* under pricing v2, and `ic0.cost_http_request` is deprecated. Flagged there with the specific lines, including that `references/cycle-costs.md` needs both cost models rather than an edit in place. As of `dfinity/ic@339d220a83` v2 is still gated off, so the pages are correct today.
Summary
flexible_http_requestmanagement canister method: a variant ofhttp_requestwhere nodes return their individual HTTP responses to the caller instead of reaching consensus.Supports configurable replication via an optional replication argument (
min_responses,max_responses,total_requests). Returns a vector of responses or a structured error with a global error code and per-node resource reports.pricing_versionfield onhttp_request: adds an optionalpricing_versionfield (1or2). Version 1 is now deprecated; version 2 ignoresmax_response_bytesand refunds unused cycles based on actual execution cost.ic0.cost_http_request_v2System API: replaces the deprecatedic0.cost_http_request. Accepts a Candid-encoded parameter record to compute the cost of an outcall at pricing version2.ic0.subnet_self_node_countSystem API: returns the number of nodes currently on the subnet. Useful for callers computing valid replication bounds forflexible_http_request.