-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat!: dont batch queries #65
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanation in the PR body
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} catch (e: any) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's do our best to avoid lint suppressions. isn't it unknown?
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
} catch (e: any) { | |
} catch (e: unknown) { |
Then onError
reports that it takes only an Error
. If that's the case, then here we should check that it's really an Error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yea that works, done.
isQueryInProgress = false; | ||
queueNextQuery(); | ||
const { value, blockHeight } = response; | ||
const lastValue = latestValueCache.get(pathKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use of the lastValue
tuple is a little confusing. In fact, there's a bug in using the [0] (identifier) for both comparisons.
To avoid that please destructure instead.
const lastValue = latestValueCache.get(pathKey); | |
// 'identifier' is block height because this is a *data* query | |
const [ latestValueHeight, latestValue ] = latestValueCache.get(pathKey) || []; |
Alternately, make a getLatestDataValue()
helper that returns { value, blockHeight }
like the response does. Then the condition could be like:
if (response.blockHeight === cached.blockHeight || (response.blockHeight === undefined && response.value === cached.value))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just made a type for LatestValueIdentifier
with a doc comment to hopefully clarify, and used the array destructuring you suggested
|
||
// Assumes argument is an unserialized presence. | ||
const presenceToSlot = (o: unknown) => marshaller.toCapData(o).slots[0]; | ||
|
||
const queryBoardAux = <T>(boardObjects: unknown[]) => { | ||
const queryBoardAux = (boardObjects: unknown[]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why lose the generic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just added back, but I had to make vstorageQuery
generic as well
packages/rpc/src/vstorageQuery.ts
Outdated
node: string, | ||
unmarshal: FromCapData<string>, | ||
path: [AgoricChainStoragePathKind, string], | ||
) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
) => { | |
): { blockHeight?: number, value: unknown } => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (except had to make value
a generic and the return value a Promise
)
@@ -1,11 +1,12 @@ | |||
/* eslint-disable no-use-before-define */ | |||
/* eslint-disable import/extensions */ | |||
import { expect, it, describe, beforeEach, vi, afterEach } from 'vitest'; | |||
// eslint-disable-next-line import/no-extraneous-dependencies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test
is part of devDependencies so the lint rules should allow it. You may need to tell the eslintconfig that test
allows devDependencies: https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-extraneous-dependencies.md#options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like it works now, I think my vscode linter was just deceiving me or not picking up the package.json
changes
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
new Promise<any[]>(res => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this work?
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
new Promise<any[]>(res => { | |
new Promise<unknown[]>(res => { |
not a blocker since this is a test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm now it does, just changed. Before I was getting some issue with 'axios-mock-adapter' expecting Promise<any>
4a1e77a
to
dc138a6
Compare
if (e instanceof Error) { | ||
onError && onError(e); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider one conditional expression,
if (e instanceof Error) { | |
onError && onError(e); | |
} | |
if (onError && e instanceof Error) { | |
onError(e); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
latestValue && | ||
(blockHeight === latestValueIdentifier || | ||
// Blockheight is undefined so fallback to using the stringified value | ||
// as the identifier, as is the case for `children` queries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helpful comment
packages/rpc/src/vstorageQuery.ts
Outdated
export const vstorageQuery = async <T>( | ||
node: string, | ||
unmarshal: FromCapData<string>, | ||
path: [AgoricChainStoragePathKind, string], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider naming these so they're clearer below
path: [AgoricChainStoragePathKind, string], | |
[pathKind, path]: [AgoricChainStoragePathKind, string], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
packages/rpc/src/vstorageQuery.ts
Outdated
const latestValue = | ||
typeof data.values !== 'undefined' | ||
? parseIfJSON(data.values[data.values.length - 1]) | ||
: parseIfJSON(data.value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
superfluous negation makes it harder to read
const latestValue = | |
typeof data.values !== 'undefined' | |
? parseIfJSON(data.values[data.values.length - 1]) | |
: parseIfJSON(data.value); | |
const latestValue = | |
typeof data.values === 'undefined' | |
? parseIfJSON(data.value); | |
: parseIfJSON(data.values[data.values.length - 1]) |
ditto below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here and below
dc138a6
to
56246cd
Compare
refs Agoric/agoric-sdk#8505
We've already switched from using the batch RPC API to JSON REST API in #55. But, we were still using a
Promise.all()
with the new interface, effectively batching those queries together. This PR makes each vstorage path poll asynchronously on separate timers.Importantly, this changes the error handling to be more manageable. Instead of having a separate error handler for each
watchLatest
, it just uses a singleonError
callback given tochainStorageWatcher
constructor. The new JSON API doesn't include an optional error message+code in response objects--it's limited to just http-level error codes, and in the case of missing vstorage data, an emptyvalue
in the response. This allows us to use axios to automatically retry on 500-level errors a couple times before invokingonError
.This updated error handling allows for clients to more easily manage faulty RPC nodes. Instead of adding an error handler to every place
watchLatest
is used, and use the result to decipher whether the vstorage data is missing or the RPC node is failing, it can just use the singleonError
callback on thechainStorageWatcher
, and trust that thechainStorageWatcher
already retried a couple times. This gives the client a reliable way to know when to notify the user of an RPC outage and/or prompt them to select a different node (assuming a node selector is available).For
queryOnce
, it also no longer batches with other requests, and uses axios to retry before throwing an error, more similar to a regularfetch
request.Added some test cases for the updated error handling.