diff --git a/pkg/morph/client/container/containers_of.go b/pkg/morph/client/container/containers_of.go index 5fe15be0d..260f52943 100644 --- a/pkg/morph/client/container/containers_of.go +++ b/pkg/morph/client/container/containers_of.go @@ -11,18 +11,23 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) +// 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 + // ContainersOf returns a list of container identifiers belonging // to the specified user of FrostFS system. If idUser is nil, returns the list of all containers. // // If remote RPC does not support neo-go session API, fallback to List() method. func (c *Client) ContainersOf(idUser *user.ID) ([]cid.ID, error) { - var rawID []byte - - if idUser != nil { - rawID = idUser.WalletBytes() - } - var cidList []cid.ID + var err error + cb := func(item stackitem.Item) error { rawID, err := client.BytesFromStackItem(item) if err != nil { @@ -40,23 +45,39 @@ 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, batchSize, cnrHash, containersOfMethod, rawID) - if err != nil { - if errors.Is(err, unwrap.ErrNoSessionID) { - return c.list(idUser) + handleSessionErr := func() error { + cidList, err = c.list(idUser) + if err != nil { + return err } - return nil, err + return nil } + if err = c.IterateContainersOf(idUser, cb, handleSessionErr); err != nil { + return nil, err + } return cidList, nil } + +// IterateContainersOf iterates over a list of container identifiers +// belonging to the specified user of FrostFS system and executes +// `cb` on each element. +// +// This method uses `(*Client).TestInvokeIterator()` which can return +// `unwrap.ErrNoSessionID` if the remote neo-go node does not support sessions. +// So providing handler for this type of errors is required. +func (c *Client) IterateContainersOf(idUser *user.ID, cb func(item stackitem.Item) error, handleSessionError func() error) error { + var rawID []byte + if idUser != nil { + rawID = idUser.WalletBytes() + } + + cnrHash := c.client.ContractAddress() + + err := c.client.Morph().TestInvokeIterator(cb, batchSize, cnrHash, containersOfMethod, rawID) + if err != nil && errors.Is(err, unwrap.ErrNoSessionID) { + return handleSessionError() + } + + return nil +}