Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: read state with fresh expiry #938

Merged
merged 5 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

## Changed

- fix: recalculates body to use a fresh `Expiry` when polling for `read_state` requests. This prevents the request from exceeding the `maximum_ingress_expiry` when the replica is slow to respond.

## [2.1.2] - 2024-09-30
- fix: revert https://github.com/dfinity/agent-js/pull/923 allow option to set agent replica time

Expand Down
13 changes: 9 additions & 4 deletions packages/agent/src/agent/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,6 @@ export class HttpAgent implements Agent {

const requestId = await requestIdOf(request);

// TODO: remove this any. This can be a Signed or UnSigned request.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let transformedRequest: HttpAgentRequest = await this._transform({
request: {
Expand Down Expand Up @@ -943,9 +942,8 @@ export class HttpAgent implements Agent {
}
const sender = id?.getPrincipal() || Principal.anonymous();

// TODO: remove this any. This can be a Signed or UnSigned request.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformedRequest: any = await this._transform({
const transformedRequest = await this._transform({
request: {
method: 'POST',
headers: {
Expand Down Expand Up @@ -976,7 +974,14 @@ export class HttpAgent implements Agent {
const canister = typeof canisterId === 'string' ? Principal.fromText(canisterId) : canisterId;

const transformedRequest = request ?? (await this.createReadStateRequest(fields, identity));
const body = cbor.encode(transformedRequest.body);

// With read_state, we should always use a fresh expiry, even beyond the point where the initial request would have expired
const bodyWithAdjustedExpiry = {
...transformedRequest.body,
ingress_expiry: new Expiry(DEFAULT_INGRESS_EXPIRY_DELTA_IN_MSECS),
};

const body = cbor.encode(bodyWithAdjustedExpiry);

this.log.print(
`fetching "/api/v2/canister/${canister}/read_state" with request:`,
Expand Down
9 changes: 8 additions & 1 deletion packages/agent/src/polling/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Principal } from '@dfinity/principal';
import { Agent, RequestStatusResponseStatus } from '../agent';
import { Agent, Expiry, RequestStatusResponseStatus } from '../agent';
import { Certificate, CreateCertificateOptions, lookupResultToBuffer } from '../certificate';
import { RequestId } from '../request_id';
import { toHex } from '../utils/buffer';
Expand All @@ -14,6 +14,9 @@ export type PollStrategy = (
) => Promise<void>;
export type PollStrategyFactory = () => PollStrategy;

// Default delta for ingress expiry is 5 minutes.
const DEFAULT_INGRESS_EXPIRY_DELTA_IN_MSECS = 5 * 60 * 1000;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rust agent seems to use 3min. https://github.com/dfinity/agent-rs/blob/main/ic-agent/src/agent/agent_config.rs#L43

Do we wanna use 3min as the default in the agent-js as well?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, a constant with the same name and the same value already exists in agent/http/index.ts
https://github.com/dfinity/agent-js/pull/938/files#diff-1deee5c6f68eb82e2be634c32d050e818f857292ed0fa9c9e19026e2ef60869cR57

Copy link
Contributor Author

@krpeacock krpeacock Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Expiry class does additional rounding down, so it's closer to 4 minutes, generally. I would handle changing the default in a separate PR if we want to match agent-rs.

I can pull the constant out into a constants utility file, but I don't want to simply export it because that would put it into the @dfinity/agent library and it doesn't seem necessary


/**
* Polls the IC to check the status of the given request then
* returns the response bytes once the request has been processed.
Expand All @@ -38,6 +41,10 @@ export async function pollForResponse(
}> {
const path = [new TextEncoder().encode('request_status'), requestId];
const currentRequest = request ?? (await agent.createReadStateRequest?.({ paths: [path] }));

// Use a fresh expiry for the readState call.
currentRequest.body.content.ingress_expiry = new Expiry(DEFAULT_INGRESS_EXPIRY_DELTA_IN_MSECS);

const state = await agent.readState(canisterId, { paths: [path] }, undefined, currentRequest);
if (agent.rootKey == null) throw new Error('Agent root key not initialized before polling');
const cert = await Certificate.create({
Expand Down
Loading