From df055fead56e6acef84f34da61bdca0c5b321f28 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 25 Jan 2024 11:21:00 +0300 Subject: [PATCH] [#931] morph: Provide batch size for container listing explicitly Besides VM stack item limit we also have restrictions on the size of JSON for a stackitem, which is 128k. This limit is much harder to calculate, because JSON representation includes type and the encoding is different for different items. Thus is makes no sense to invent our own default, so use the one provided by neo-go. But for container listing we know exactly what we process, so use big enough value, which is tested. Introduced in be8607a1f6. Refs #902 Refs https://github.com/nspcc-dev/neo-go/blob/v0.105.0/pkg/vm/stackitem/json.go#L353 Signed-off-by: Evgenii Stratonikov --- pkg/morph/client/client.go | 11 +++-------- pkg/morph/client/container/containers_of.go | 11 ++++++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index 0c8544bd0..88f9703e7 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -22,13 +22,13 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/wallet" @@ -207,16 +207,11 @@ func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, return vub, nil } -// defaultPrefetchBatchSize is the default number of items to prefetch. -// It is dependent on VM limits (2048 items on stack), the default works for simple items. -// For example, to iterate over 2 field structs, the limit should be divided by 3 = 1 (struct itself) + 2 (fields). -const defaultPrefetchBatchSize = vm.MaxStackSize - 16 - // TestInvokeIterator invokes contract method returning an iterator and executes cb on each element. // If cb returns an error, the session is closed and this error is returned as-is. // If the remove neo-go node does not support sessions, `unwrap.ErrNoSessionID` is returned. // batchSize is the number of items to prefetch: if the number of items in the iterator is less than batchSize, no session will be created. -// The default batchSize is 2000 (VM is limited by having 2048 items on stack, so if each iterator item is simple, 2000 items won't hit the limit). +// The default batchSize is 100, the default limit from neo-go. func (c *Client) TestInvokeIterator(cb func(stackitem.Item) error, batchSize int, contract util.Uint160, method string, args ...interface{}) error { start := time.Now() success := false @@ -225,7 +220,7 @@ func (c *Client) TestInvokeIterator(cb func(stackitem.Item) error, batchSize int }() if batchSize <= 0 { - batchSize = defaultPrefetchBatchSize + batchSize = invoker.DefaultIteratorResultItems } c.switchLock.RLock() diff --git a/pkg/morph/client/container/containers_of.go b/pkg/morph/client/container/containers_of.go index 140047eb2..ce127335d 100644 --- a/pkg/morph/client/container/containers_of.go +++ b/pkg/morph/client/container/containers_of.go @@ -40,8 +40,17 @@ func (c *Client) ContainersOf(idUser *user.ID) ([]cid.ID, error) { return nil } + // We would like to have batch size as big as possible, + // to reduce the number of round-trips and avoid creating sessions. + // The limit depends on 2 things: + // 1. VM limits: max 2048 items on stack. + // 2. JSON encoded size for the item with type = 128k. + // It turns out, that for container ID the second limit is hit first, + // 512 is big enough value and it is beautiful. + const batchSize = 512 + cnrHash := c.client.ContractAddress() - err := c.client.Morph().TestInvokeIterator(cb, 0, cnrHash, containersOfMethod, rawID) + err := c.client.Morph().TestInvokeIterator(cb, batchSize, cnrHash, containersOfMethod, rawID) if err != nil { if errors.Is(err, unwrap.ErrNoSessionID) { return c.List(idUser)