Adjust max batch size calc (w/ proofSize & older) (#8920)
* Adjust max batch size calc (w/ proofSize & older) * Cleanups * Adjust fill ratio * Adjust, only check refTime * Comments * Clarify ratio further * Extract getKnown * Split size calc into function
This commit is contained in:
parent
285ae9de6f
commit
0cdc49c4be
@ -7,7 +7,7 @@ import type { DeriveAccountFlags, DeriveAccountRegistration } from '@polkadot/ap
|
||||
import type { DisplayedJudgement } from '@polkadot/react-components/types';
|
||||
import type { AccountId, Balance, BlockNumber, Call, Exposure, Hash, RewardDestination, SessionIndex, StakingLedger, ValidatorPrefs } from '@polkadot/types/interfaces';
|
||||
import type { PalletPreimageRequestStatus } from '@polkadot/types/lookup';
|
||||
import type { IExtrinsic, Registry } from '@polkadot/types/types';
|
||||
import type { ICompact, IExtrinsic, INumber, Registry } from '@polkadot/types/types';
|
||||
import type { KeyringJson$Meta } from '@polkadot/ui-keyring/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
@ -196,3 +196,13 @@ export interface PreimageBytes {
|
||||
export interface Preimage extends PreimageBytes, PreimageStatus {
|
||||
// just the interfaces above
|
||||
}
|
||||
|
||||
export interface V2WeightConstruct {
|
||||
refTime: BN | ICompact<INumber>;
|
||||
proofSize?: BN | ICompact<INumber>;
|
||||
}
|
||||
|
||||
export interface WeightResult {
|
||||
v1Weight: BN;
|
||||
v2Weight: V2WeightConstruct;
|
||||
}
|
||||
|
||||
@ -4,17 +4,135 @@
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { Weight } from '@polkadot/types/interfaces';
|
||||
import type { BatchOptions, BatchType } from './types.js';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { BatchOptions, BatchType, WeightResult } from './types.js';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { isFunction, nextTick } from '@polkadot/util';
|
||||
import { BN_HUNDRED, BN_ZERO, bnMax, bnMin, bnToBn, isCompact, isFunction, nextTick } from '@polkadot/util';
|
||||
|
||||
import { createNamedHook } from './createNamedHook.js';
|
||||
import { useAccounts } from './useAccounts.js';
|
||||
import { useApi } from './useApi.js';
|
||||
import { convertWeight } from './useWeight.js';
|
||||
|
||||
interface BNWeight {
|
||||
proofSize: BN;
|
||||
refTime: BN;
|
||||
}
|
||||
|
||||
type WeightSimple = WeightResult['v2Weight'] | BNWeight;
|
||||
|
||||
interface Known {
|
||||
baseExtrinsic: BNWeight | null;
|
||||
maxBlock: BNWeight;
|
||||
maxExtrinsic: BNWeight | null;
|
||||
}
|
||||
|
||||
// converts a weight construct to only contain BN values
|
||||
function bnWeight (a: WeightSimple): BNWeight {
|
||||
return {
|
||||
proofSize: a.proofSize
|
||||
? bnToBn(
|
||||
isCompact(a.proofSize)
|
||||
? a.proofSize.unwrap()
|
||||
: a.proofSize
|
||||
)
|
||||
: BN_ZERO,
|
||||
refTime: bnToBn(
|
||||
isCompact(a.refTime)
|
||||
? a.refTime.unwrap()
|
||||
: a.refTime
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// subtract 2 BN-only weight values
|
||||
function weightSub (_a: WeightSimple, _b: WeightSimple): BNWeight {
|
||||
const a = bnWeight(_a);
|
||||
const b = bnWeight(_b);
|
||||
|
||||
return {
|
||||
proofSize: bnMax(BN_ZERO, a.proofSize.sub(b.proofSize)),
|
||||
refTime: bnMax(BN_ZERO, a.refTime.sub(b.refTime))
|
||||
};
|
||||
}
|
||||
|
||||
// divide 2 BN-only weight values
|
||||
function weightDiv (_a: WeightSimple, _b: WeightSimple): number {
|
||||
const a = bnWeight(_a);
|
||||
const b = bnWeight(_b);
|
||||
const r = {
|
||||
proofSize: b.proofSize.isZero()
|
||||
? BN_ZERO
|
||||
: bnMax(BN_ZERO, a.proofSize.mul(BN_HUNDRED).div(b.proofSize)),
|
||||
refTime: b.refTime.isZero()
|
||||
? BN_ZERO
|
||||
: bnMax(BN_ZERO, a.refTime.mul(BN_HUNDRED).div(b.refTime))
|
||||
};
|
||||
|
||||
return (
|
||||
r.proofSize.isZero()
|
||||
? r.refTime.toNumber()
|
||||
: bnMin(r.proofSize, r.refTime).toNumber()
|
||||
) / 100;
|
||||
}
|
||||
|
||||
function getKnown (api: ApiPromise): Known {
|
||||
return {
|
||||
baseExtrinsic: api.consts.system.blockWeights
|
||||
? bnWeight(
|
||||
convertWeight(
|
||||
api.consts.system.blockWeights.perClass.normal.baseExtrinsic
|
||||
).v2Weight
|
||||
)
|
||||
: null,
|
||||
maxBlock: bnWeight(
|
||||
convertWeight(
|
||||
api.consts.system.blockWeights
|
||||
? api.consts.system.blockWeights.maxBlock
|
||||
: api.consts.system.maximumBlockWeight as Weight
|
||||
).v2Weight
|
||||
),
|
||||
maxExtrinsic: api.consts.system.blockWeights && api.consts.system.blockWeights.perClass.normal.maxExtrinsic.isSome
|
||||
? bnWeight(
|
||||
convertWeight(
|
||||
api.consts.system.blockWeights.perClass.normal.maxExtrinsic.unwrap()
|
||||
).v2Weight
|
||||
)
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
function getBatchSize ({ v1Weight, v2Weight }: WeightResult, { baseExtrinsic, maxBlock, maxExtrinsic }: Known): number {
|
||||
let div = 0;
|
||||
|
||||
// for newer chains we will try and calculate the size based on the supplied constants
|
||||
// (At least maxExtrinsic is Option<...>, hence also having the fallback ratio)
|
||||
if (baseExtrinsic && maxExtrinsic) {
|
||||
// 65 div 75 below is around 86% of space, use same safety ratio here
|
||||
// (Since we also have a max total limit for normal, this ensure faster
|
||||
// throughput when the chain is busy at the expense of having less txs
|
||||
// per batch - it does _eventually_ go through without the ratio)
|
||||
div = Math.floor(
|
||||
0.85 * weightDiv(
|
||||
weightSub(maxExtrinsic, baseExtrinsic),
|
||||
weightSub(v2Weight, baseExtrinsic)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// If we don't have a size calculation above, we create the extrinsic with a fallback
|
||||
// of up to 65% of the block weight (applied here as 64 for a safety margin)
|
||||
// (This is based on the Kusama/Polkadot 75% allowance for all extrinsics)
|
||||
return div || Math.floor(
|
||||
maxBlock.refTime
|
||||
.muln(64)
|
||||
.div(v1Weight)
|
||||
.toNumber() / 100
|
||||
);
|
||||
}
|
||||
|
||||
function createBatches (api: ApiPromise, txs: SubmittableExtrinsic<'promise'>[], batchSize: number, type: BatchType = 'default'): SubmittableExtrinsic<'promise'>[] {
|
||||
if (batchSize === 1 || !isFunction(api.tx.utility?.batch)) {
|
||||
return txs;
|
||||
@ -44,7 +162,12 @@ function createBatches (api: ApiPromise, txs: SubmittableExtrinsic<'promise'>[],
|
||||
function useTxBatchImpl (txs?: SubmittableExtrinsic<'promise'>[] | null | false, options?: BatchOptions): SubmittableExtrinsic<'promise'>[] | null {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [batchSize, setBatchSize] = useState(() => Math.floor(options?.max || 64));
|
||||
const [batchSize, setBatchSize] = useState(() => Math.floor(options?.max || 4));
|
||||
|
||||
const known = useMemo(
|
||||
() => getKnown(api),
|
||||
[api]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
txs && txs.length && allAccounts[0] && txs[0].hasPaymentInfo &&
|
||||
@ -52,27 +175,17 @@ function useTxBatchImpl (txs?: SubmittableExtrinsic<'promise'>[] | null | false,
|
||||
try {
|
||||
const paymentInfo = await txs[0].paymentInfo(allAccounts[0]);
|
||||
const weight = convertWeight(paymentInfo.weight);
|
||||
const maxBlock = convertWeight(
|
||||
api.consts.system.blockWeights
|
||||
? api.consts.system.blockWeights.maxBlock
|
||||
: api.consts.system.maximumBlockWeight as Weight
|
||||
);
|
||||
|
||||
setBatchSize((prev) =>
|
||||
weight.v1Weight.isZero()
|
||||
? prev
|
||||
: Math.floor(
|
||||
maxBlock.v1Weight
|
||||
.muln(64) // 65% of the block weight on a single extrinsic (64 for safety)
|
||||
.div(weight.v1Weight)
|
||||
.toNumber() / 100
|
||||
)
|
||||
: getBatchSize(weight, known)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}, [allAccounts, api, options, txs]);
|
||||
}, [allAccounts, api, known, options, txs]);
|
||||
|
||||
return useMemo(
|
||||
() => txs && txs.length
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import type { Call } from '@polkadot/types/interfaces';
|
||||
import type { ICompact, INumber } from '@polkadot/types/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { V2WeightConstruct, WeightResult } from './types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@ -20,15 +21,9 @@ interface V2Weight {
|
||||
proofSize: ICompact<INumber>;
|
||||
}
|
||||
|
||||
interface V2WeightConstruct {
|
||||
refTime: BN | ICompact<INumber>;
|
||||
}
|
||||
|
||||
interface Result {
|
||||
interface Result extends WeightResult {
|
||||
encodedCallLength: number;
|
||||
isWeightV2: boolean;
|
||||
v1Weight: BN;
|
||||
v2Weight: V2WeightConstruct;
|
||||
weight: BN | V2WeightConstruct;
|
||||
}
|
||||
|
||||
@ -43,7 +38,7 @@ const EMPTY_STATE: Partial<Result> = {
|
||||
};
|
||||
|
||||
// return both v1 & v2 weight structures (would depend on actual use)
|
||||
export function convertWeight (weight: V1Weight | V2Weight): { v1Weight: BN, v2Weight: V2WeightConstruct } {
|
||||
export function convertWeight (weight: V1Weight | V2Weight): WeightResult {
|
||||
if ((weight as V2Weight).proofSize) {
|
||||
// V2 weight
|
||||
const refTime = (weight as V2Weight).refTime.toBn();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user