Locate object-storing shards #1635
118
cmd/frostfs-cli/modules/control/locate.go
Normal file
|
@ -0,0 +1,118 @@
|
|||||
package control
|
||||||
|
||||||
import (
|
||||||
"bytes"
|
||||||
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
|
||||||
object "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/object"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||||
rawclient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
|
||||||
"github.com/mr-tron/base58"
|
||||||
"github.com/spf13/cobra"
|
||||||
)
|
||||||
|
||||||
const (
|
||||||
FullInfoFlag = "full"
|
||||||
FullInfoFlagUsage = "Print full ShardInfo."
|
||||||
)
|
||||||
|
||||||
var locateObjectCmd = &cobra.Command{
|
||||||
a-savchuk marked this conversation as resolved
Outdated
|
||||||
Use: "locate-object",
|
||||||
Short: "List shards storing the object",
|
||||||
Long: "List shards storing the object",
|
||||||
Run: locateObject,
|
||||||
}
|
||||||
|
||||||
func initControlLocateObjectCmd() {
|
||||||
initControlFlags(locateObjectCmd)
|
||||||
|
||||||
flags := locateObjectCmd.Flags()
|
||||||
|
||||||
flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
|
||||||
_ = locateObjectCmd.MarkFlagRequired(commonflags.CIDFlag)
|
||||||
|
||||||
flags.String(commonflags.OIDFlag, "", commonflags.OIDFlagUsage)
|
||||||
_ = locateObjectCmd.MarkFlagRequired(commonflags.OIDFlag)
|
||||||
|
||||||
flags.Bool(commonflags.JSON, false, "Print shard info as a JSON array. Requires --full flag.")
|
||||||
flags.Bool(FullInfoFlag, false, FullInfoFlagUsage)
|
||||||
}
|
||||||
|
||||||
func locateObject(cmd *cobra.Command, _ []string) {
|
||||||
var cnr cid.ID
|
||||||
var obj oid.ID
|
||||||
|
||||||
_ = object.ReadObjectAddress(cmd, &cnr, &obj)
|
||||||
|
||||||
pk := key.Get(cmd)
|
||||||
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
You should use control endpoint, please see the command for shard listing
You should use control endpoint, please see the command for shard listing
https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/5d79abe523b0254fe0484220d6d2c57a172090e1/cmd/frostfs-cli/modules/control/shards_list.go#L41
elebedeva
commented
Fixed Fixed
|
||||||
|
||||||
body := new(control.ListShardsForObjectRequest_Body)
|
||||||
body.SetContainerId(cnr.EncodeToString())
|
||||||
body.SetObjectId(obj.EncodeToString())
|
||||||
req := new(control.ListShardsForObjectRequest)
|
||||||
req.SetBody(body)
|
||||||
signRequest(cmd, pk, req)
|
||||||
|
||||||
cli := getClient(cmd, pk)
|
||||||
|
||||||
var err error
|
||||||
var resp *control.ListShardsForObjectResponse
|
||||||
err = cli.ExecRaw(func(client *rawclient.Client) error {
|
||||||
resp, err = control.ListShardsForObject(client, req)
|
||||||
return err
|
||||||
})
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
||||||
verifyResponse(cmd, resp.GetSignature(), resp.GetBody())
|
||||||
|
||||||
shardIDs := resp.GetBody().GetShard_ID()
|
||||||
dstepanov-yadro marked this conversation as resolved
Outdated
dstepanov-yadro
commented
duplicate
duplicate https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/5d79abe523b0254fe0484220d6d2c57a172090e1/cmd/frostfs-cli/modules/object/util.go#L77
elebedeva
commented
Made Made `object.readObjectAddress()` public and reused it in `locateObject()`.
|
||||||
|
||||||
isFull, _ := cmd.Flags().GetBool(FullInfoFlag)
|
||||||
if !isFull {
|
||||||
for _, id := range shardIDs {
|
||||||
cmd.Println(base58.Encode(id))
|
||||||
}
|
||||||
return
|
||||||
}
|
||||||
|
||||||
// get full shard info
|
||||||
listShardsReq := new(control.ListShardsRequest)
|
||||||
listShardsReq.SetBody(new(control.ListShardsRequest_Body))
|
||||||
signRequest(cmd, pk, listShardsReq)
|
||||||
var listShardsResp *control.ListShardsResponse
|
||||||
err = cli.ExecRaw(func(client *rawclient.Client) error {
|
||||||
listShardsResp, err = control.ListShards(client, listShardsReq)
|
||||||
return err
|
||||||
})
|
||||||
commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
|
||||||
|
||||||
verifyResponse(cmd, listShardsResp.GetSignature(), listShardsResp.GetBody())
|
||||||
|
||||||
shards := listShardsResp.GetBody().GetShards()
|
||||||
sortShardsByID(shards)
|
||||||
shards = filterShards(shards, shardIDs)
|
||||||
|
||||||
isJSON, _ := cmd.Flags().GetBool(commonflags.JSON)
|
||||||
if isJSON {
|
||||||
prettyPrintShardsJSON(cmd, shards)
|
||||||
} else {
|
||||||
prettyPrintShards(cmd, shards)
|
||||||
}
|
||||||
}
|
||||||
|
||||||
func filterShards(info []control.ShardInfo, ids [][]byte) []control.ShardInfo {
|
||||||
var res []control.ShardInfo
|
||||||
for _, id := range ids {
|
||||||
for _, inf := range info {
|
||||||
if bytes.Equal(inf.Shard_ID, id) {
|
||||||
res = append(res, inf)
|
||||||
}
|
||||||
}
|
||||||
}
|
||||||
return res
|
||||||
}
|
|
@ -39,6 +39,7 @@ func init() {
|
|||
listRulesCmd,
|
||||
getRuleCmd,
|
||||
listTargetsCmd,
|
||||
locateObjectCmd,
|
||||
)
|
||||
|
||||
initControlHealthCheckCmd()
|
||||
|
@ -52,4 +53,5 @@ func init() {
|
|||
initControlListRulesCmd()
|
||||
initControGetRuleCmd()
|
||||
initControlListTargetsCmd()
|
||||
initControlLocateObjectCmd()
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func deleteObject(cmd *cobra.Command, _ []string) {
|
|||
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("required flag \"%s\" not set", commonflags.OIDFlag))
|
||||
}
|
||||
|
||||
objAddr = readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr = ReadObjectAddress(cmd, &cnr, &obj)
|
||||
}
|
||||
|
||||
pk := key.GetOrGenerate(cmd)
|
||||
|
|
|
@ -46,7 +46,7 @@ func getObject(cmd *cobra.Command, _ []string) {
|
|||
var cnr cid.ID
|
||||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr := ReadObjectAddress(cmd, &cnr, &obj)
|
||||
|
||||
filename := cmd.Flag(fileFlag).Value.String()
|
||||
out, closer := createOutWriter(cmd, filename)
|
||||
|
|
|
@ -52,7 +52,7 @@ func getObjectHash(cmd *cobra.Command, _ []string) {
|
|||
var cnr cid.ID
|
||||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr := ReadObjectAddress(cmd, &cnr, &obj)
|
||||
|
||||
ranges, err := getRangeList(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "", err)
|
||||
|
|
|
@ -47,7 +47,7 @@ func getObjectHeader(cmd *cobra.Command, _ []string) {
|
|||
var cnr cid.ID
|
||||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr := ReadObjectAddress(cmd, &cnr, &obj)
|
||||
pk := key.GetOrGenerate(cmd)
|
||||
|
||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||
|
|
|
@ -101,7 +101,7 @@ func initObjectNodesCmd() {
|
|||
func objectNodes(cmd *cobra.Command, _ []string) {
|
||||
var cnrID cid.ID
|
||||
var objID oid.ID
|
||||
readObjectAddress(cmd, &cnrID, &objID)
|
||||
ReadObjectAddress(cmd, &cnrID, &objID)
|
||||
|
||||
pk := key.GetOrGenerate(cmd)
|
||||
cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
|
||||
|
|
|
@ -56,7 +56,7 @@ func patch(cmd *cobra.Command, _ []string) {
|
|||
var cnr cid.ID
|
||||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr := ReadObjectAddress(cmd, &cnr, &obj)
|
||||
|
||||
ranges, err := getRangeSlice(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "", err)
|
||||
|
|
|
@ -47,7 +47,7 @@ func getObjectRange(cmd *cobra.Command, _ []string) {
|
|||
var cnr cid.ID
|
||||
var obj oid.ID
|
||||
|
||||
objAddr := readObjectAddress(cmd, &cnr, &obj)
|
||||
objAddr := ReadObjectAddress(cmd, &cnr, &obj)
|
||||
|
||||
ranges, err := getRangeList(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "", err)
|
||||
|
|
|
@ -74,7 +74,7 @@ func parseXHeaders(cmd *cobra.Command) []string {
|
|||
return xs
|
||||
}
|
||||
|
||||
func readObjectAddress(cmd *cobra.Command, cnr *cid.ID, obj *oid.ID) oid.Address {
|
||||
func ReadObjectAddress(cmd *cobra.Command, cnr *cid.ID, obj *oid.ID) oid.Address {
|
||||
readCID(cmd, cnr)
|
||||
readOID(cmd, obj)
|
||||
|
||||
|
|
4
go.mod
|
@ -4,7 +4,7 @@ go 1.22
|
|||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.17.1
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
|
||||
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d
|
||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20250212111929-d34e1329c824
|
||||
|
@ -12,7 +12,7 @@ require (
|
|||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20250306092416-69b0711d12d9
|
||||
git.frostfs.info/TrueCloudLab/hrw v1.2.1
|
||||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0
|
||||
git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4
|
||||
|
|
8
go.sum
|
@ -1,7 +1,7 @@
|
|||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08 h1:tl1TT+zNk1lF/J5EaD3syDrTaYbQwvJKVOVENM4oQ+k=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1-0.20241205083807-762d7f9f9f08/go.mod h1:5fSm/l5xSjGWqsPUffSdboiGFUHa7y/1S0fvxzQowN8=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1 h1:k1Qw8dWUQczfo0eVXlhrq9eXEbUMyDLW8jEMzY+gxMc=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.21.1/go.mod h1:5fSm/l5xSjGWqsPUffSdboiGFUHa7y/1S0fvxzQowN8=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
||||
git.frostfs.info/TrueCloudLab/frostfs-locode-db v0.4.1-0.20240710074952-65761deb5c0d h1:uJ/wvuMdepbkaV8XMS5uN9B0FQWMep0CttSuDZiDhq0=
|
||||
|
@ -18,8 +18,8 @@ git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972 h1:/96
|
|||
git.frostfs.info/TrueCloudLab/multinet v0.0.0-20241015075604-6cb0d80e0972/go.mod h1:2hM42MBrlhvN6XToaW6OWNk5ZLcu1FhaukGgxtfpDDI=
|
||||
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07 h1:gPaqGsk6gSWQyNVjaStydfUz6Z/loHc9XyvGrJ5qSPY=
|
||||
git.frostfs.info/TrueCloudLab/neoneo-go v0.106.1-0.20241015133823-8aee80dbdc07/go.mod h1:bZyJexBlrja4ngxiBgo8by5pVHuAbhg9l09/8yVGDyg=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88 h1:vgbfkcnIexZUm3vREBBSa/Gv1Whjd1SFCUd0A+IaGPQ=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240814080254-96225afacb88/go.mod h1:SgioiGhQNWqiV5qpFAXRDJF81SEFRBhtwGEiU0FViyA=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b h1:M50kdfrf/h8c3cz0bJ2AEUcbXvAlPFVC1Wp1WkfZ/8E=
|
||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240822104152-a3bc3099bd5b/go.mod h1:GZTk55RI4dKzsK6BCn5h2xxE28UHNfgoq/NJxW/LQ6A=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA=
|
||||
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc=
|
||||
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA=
|
||||
|
|
|
@ -252,6 +252,7 @@ const (
|
|||
ShardFailureToMarkLockersAsGarbage = "failure to mark lockers as garbage"
|
||||
ShardFailureToGetExpiredUnlockedObjects = "failure to get expired unlocked objects"
|
||||
ShardCouldNotMarkObjectToDeleteInMetabase = "could not mark object to delete in metabase"
|
||||
ShardCouldNotFindObject = "could not find object"
|
||||
WritecacheWaitingForChannelsToFlush = "waiting for channels to flush"
|
||||
WritecacheCantRemoveObjectFromWritecache = "can't remove object from write-cache"
|
||||
BlobovniczatreeCouldNotGetObjectFromLevel = "could not get object from level"
|
||||
|
|
|
@ -11,6 +11,9 @@ import (
|
|||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/hrw"
|
||||||
"github.com/google/uuid"
|
||||||
|
@ -442,3 +445,46 @@ func (e *StorageEngine) deleteShards(ctx context.Context, ids []*shard.ID) ([]ha
|
|||||
func (s hashedShard) Hash() uint64 {
|
||||||
return s.hash
|
||||||
acid-ant
commented
Need to take mtx here to iterate over shards. Need to take mtx here to iterate over shards.
elebedeva
commented
Fixed. Fixed.
|
||||||
}
|
||||||
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
We do not actually know slice capacity, and most likely there is 0 or 1 shard. We do not actually know slice capacity, and _most likely_ there is 0 or 1 shard.
Preallocation is not needed here, just `var info []shard.Info`.
elebedeva
commented
Fixed Fixed
|
||||||
func (e *StorageEngine) ListShardsForObject(ctx context.Context, obj oid.Address) ([]shard.Info, error) {
|
||||||
var err error
|
||||||
var info []shard.Info
|
||||||
prm := shard.ExistsPrm{
|
||||||
Address: obj,
|
||||||
dstepanov-yadro marked this conversation as resolved
Outdated
dstepanov-yadro
commented
`Engine.Exists` has some error handling: https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/5d79abe523b0254fe0484220d6d2c57a172090e1/pkg/local_object_storage/engine/exists.go#L16
Is this error checking not performed here intentionally?
elebedeva
commented
It was intentional as I assumed we should stop on every error. I didn't consider that It was intentional as I assumed we should stop on every error. I didn't consider that `ObjectNotFound` could be an error.
Added error checks.
|
||||||
}
|
||||||
var siErr *objectSDK.SplitInfoError
|
||||||
var ecErr *objectSDK.ECInfoError
|
||||||
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
Let's iterate over sorted shards Let's iterate over _sorted_ shards
dstepanov-yadro
commented
Why? It is required to find all shards store object, but not the first shard stores object. Why? It is required to find all shards store object, but not the first shard stores object.
a-savchuk
commented
Ok, nevermind Ok, nevermind
|
||||||
|
||||||
e.iterateOverUnsortedShards(func(hs hashedShard) (stop bool) {
|
||||||
res, exErr := hs.Exists(ctx, prm)
|
||||||
if exErr != nil {
|
||||||
if client.IsErrObjectAlreadyRemoved(exErr) {
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
Imagine an object is removed by the user, but it was a mistake, so we want quickly locate this object, which is still physically stored. Not saying it is will be used, but let's discuss. Imagine an object is removed by the user, but it was a mistake, so we want quickly locate this object, which is still physically stored. Not saying it is will be used, but let's discuss.
elebedeva
commented
Why would a user need to quickly locate this object? Why would a user need to quickly locate this object?
fyrchik
commented
`control` service is not for a user, it is for administrator and service engineers.
I may want to save and quarantine these objects.
But, anyway, fetching part is currently missing, so we may discuss this separately.
|
||||||
err = new(apistatus.ObjectAlreadyRemoved)
|
||||||
return true
|
||||||
}
|
||||||
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
`ECInfoError` too
elebedeva
commented
Added check for Added check for `ECInfoError`
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
Move these variables definitions outside shard iterating Move these variables definitions outside shard iterating
elebedeva
commented
Fixed Fixed
|
||||||
|
||||||
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
Let's merge similar check together and make less Let's merge similar check together and make less `if`s
elebedeva
commented
Ok, fixed Ok, fixed
|
||||||
// Check if error is either SplitInfoError or ECInfoError.
|
||||||
// True means the object is virtual.
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
I would argue that these errors are, actually, useful: this means that queried object is virtual and this shard stores a part of it (a chunk of it). IMO this command should somewhat resemble I would argue that these errors are, actually, useful: this means that queried object is virtual and this shard stores a part of it (a chunk of it). IMO this command should somewhat resemble `object nodes`, but instead of object we find shards on a single node.
WDYT? @dstepanov-yadro
elebedeva
commented
Good point, didn't consider it. Good point, didn't consider it.
|
||||||
if errors.As(exErr, &siErr) || errors.As(exErr, &ecErr) {
|
||||||
info = append(info, hs.DumpInfo())
|
||||||
return false
|
||||||
}
|
||||||
|
||||||
if shard.IsErrObjectExpired(exErr) {
|
||||||
err = exErr
|
||||||
return true
|
||||||
}
|
||||||
|
||||||
if !client.IsErrObjectNotFound(exErr) {
|
||||||
e.reportShardError(ctx, hs, "could not check existence of object in shard", exErr, zap.Stringer("address", prm.Address))
|
||||||
}
|
||||||
|
||||||
return false
|
||||||
}
|
||||||
if res.Exists() {
|
||||||
info = append(info, hs.DumpInfo())
|
||||||
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
I think I think `Exists` should not be called if an error was returned. Now this happens if you get an unexpected error you then report. You can either add return `false` after error reporting or use `else` or do something else
elebedeva
commented
Fair point, added Fair point, added `return false` at the end of error check.
|
||||||
}
|
||||||
return false
|
||||||
})
|
||||||
return info, err
|
||||||
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ const (
|
|||
rpcListTargetsLocalOverrides = "ListTargetsLocalOverrides"
|
||||
rpcDetachShards = "DetachShards"
|
||||
rpcStartShardRebuild = "StartShardRebuild"
|
||||
rpcListShardsForObject = "ListShardsForObject"
|
||||
)
|
||||
|
||||
// HealthCheck executes ControlService.HealthCheck RPC.
|
||||
|
@ -364,3 +365,22 @@ func StartShardRebuild(cli *client.Client, req *StartShardRebuildRequest, opts .
|
|||
|
||||
return wResp.message, nil
|
||||
}
|
||||
|
||||
// ListShardsForObject executes ControlService.ListShardsForObject RPC.
|
||||
func ListShardsForObject(
|
||||
cli *client.Client,
|
||||
req *ListShardsForObjectRequest,
|
||||
opts ...client.CallOption,
|
||||
) (*ListShardsForObjectResponse, error) {
|
||||
wResp := newResponseWrapper[ListShardsForObjectResponse]()
|
||||
|
||||
wReq := &requestWrapper{
|
||||
m: req,
|
||||
}
|
||||
err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceName, rpcListShardsForObject), wReq, wResp, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return wResp.message, nil
|
||||
}
|
||||
|
|
66
pkg/services/control/server/list_shards_for_object.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/server/ctrlmessage"
|
||||
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (s *Server) ListShardsForObject(ctx context.Context, req *control.ListShardsForObjectRequest) (*control.ListShardsForObjectResponse, error) {
|
||||
err := s.isValidRequest(req)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.PermissionDenied, err.Error())
|
||||
}
|
||||
|
||||
var obj oid.ID
|
||||
err = obj.DecodeString(req.GetBody().GetObjectId())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
var cnr cid.ID
|
||||
err = cnr.DecodeString(req.GetBody().GetContainerId())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
resp := new(control.ListShardsForObjectResponse)
|
||||
body := new(control.ListShardsForObjectResponse_Body)
|
||||
resp.SetBody(body)
|
||||
|
||||
var objAddr oid.Address
|
||||
objAddr.SetContainer(cnr)
|
||||
objAddr.SetObject(obj)
|
||||
info, err := s.s.ListShardsForObject(ctx, objAddr)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if len(info) == 0 {
|
||||
return nil, status.Error(codes.NotFound, logs.ShardCouldNotFindObject)
|
||||
elebedeva
commented
Fixed Fixed
|
||||
}
|
||||
|
||||
body.SetShard_ID(shardInfoToProto(info))
|
||||
|
||||
// Sign the response
|
||||
if err := ctrlmessage.Sign(s.key, resp); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func shardInfoToProto(infos []shard.Info) [][]byte {
|
||||
shardInfos := make([][]byte, 0, len(infos))
|
||||
for _, info := range infos {
|
||||
shardInfos = append(shardInfos, *info.ID)
|
||||
}
|
||||
|
||||
return shardInfos
|
||||
}
|
|
@ -89,6 +89,9 @@ service ControlService {
|
|||
|
||||
// StartShardRebuild starts shard rebuild process.
|
||||
rpc StartShardRebuild(StartShardRebuildRequest) returns (StartShardRebuildResponse);
|
||||
|
||||
// ListShardsForObject returns shard info where object is stored.
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
I think I think `GetShardByObjectID` is a bad naming -- `ObjectID` is not some persistent identifier of a shard, it is something that a shard can store.
May be sth like `ListShardsForObject`?
elebedeva
commented
Thanks, renamed Thanks, renamed `GetShardByObjectID` to `ListShardsForObject`.
|
||||
rpc ListShardsForObject(ListShardsForObjectRequest) returns (ListShardsForObjectResponse);
|
||||
}
|
||||
|
||||
// Health check request.
|
||||
|
@ -729,3 +732,23 @@ message StartShardRebuildResponse {
|
|||
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message ListShardsForObjectRequest {
|
||||
message Body {
|
||||
string object_id = 1;
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
`object_id` and `container_id` would be better (in accordance with other fields like `chain_id` or `shard_id`)
elebedeva
commented
Fixed Fixed
a-savchuk marked this conversation as resolved
Outdated
a-savchuk
commented
How about using bytes instead of string? An ID as bytes takes 32 bytes, the same ID as a string takes about 44 bytes. Also I see we already use bytes in control IR service. What do you think? How about using bytes instead of string? An ID as bytes takes 32 bytes, the same ID as a string takes about 44 bytes. Also I see we already use bytes in control IR service.
What do you think?
dstepanov-yadro
commented
I think it is an an error that we use I think it is an an error that we use `bytes` for shardID. String is more human readable and control API is not so performance critical to save 12 bytes:)
|
||||
string container_id = 2;
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
||||
message ListShardsForObjectResponse {
|
||||
message Body {
|
||||
// List of the node's shards storing object.
|
||||
repeated bytes shard_ID = 1;
|
||||
fyrchik
commented
I don't really mind having I don't _really_ mind having `ShardInfo` here, but still think that shard ID is the only thing we need (may be another struct with a single field in it, to allow extension).
`ShardInfo` is primarily to be fetched with `shards list` command.
These will be changed independently.
fyrchik
commented
If you find at least one approval from other team member, fine by me. If you find at least one approval from other team member, fine by me.
elebedeva
commented
Made Made `ListShardsForObject` return only `shard ids`.
Full shard info can be listed with `--full` flag.
|
||||
}
|
||||
|
||||
Body body = 1;
|
||||
Signature signature = 2;
|
||||
}
|
||||
|
|
724
pkg/services/control/service_frostfs.pb.go
generated
|
@ -17303,3 +17303,727 @@ func (x *StartShardRebuildResponse) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
||||
type ListShardsForObjectRequest_Body struct {
|
||||
ObjectId string `json:"objectId"`
|
||||
ContainerId string `json:"containerId"`
|
||||
}
|
||||
|
||||
var (
|
||||
_ encoding.ProtoMarshaler = (*ListShardsForObjectRequest_Body)(nil)
|
||||
_ encoding.ProtoUnmarshaler = (*ListShardsForObjectRequest_Body)(nil)
|
||||
_ json.Marshaler = (*ListShardsForObjectRequest_Body)(nil)
|
||||
_ json.Unmarshaler = (*ListShardsForObjectRequest_Body)(nil)
|
||||
)
|
||||
|
||||
// StableSize returns the size of x in protobuf format.
|
||||
//
|
||||
// Structures with the same field values have the same binary size.
|
||||
func (x *ListShardsForObjectRequest_Body) StableSize() (size int) {
|
||||
if x == nil {
|
||||
return 0
|
||||
}
|
||||
size += proto.StringSize(1, x.ObjectId)
|
||||
size += proto.StringSize(2, x.ContainerId)
|
||||
return size
|
||||
}
|
||||
|
||||
// MarshalProtobuf implements the encoding.ProtoMarshaler interface.
|
||||
func (x *ListShardsForObjectRequest_Body) MarshalProtobuf(dst []byte) []byte {
|
||||
m := pool.MarshalerPool.Get()
|
||||
defer pool.MarshalerPool.Put(m)
|
||||
x.EmitProtobuf(m.MessageMarshaler())
|
||||
dst = m.Marshal(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (x *ListShardsForObjectRequest_Body) EmitProtobuf(mm *easyproto.MessageMarshaler) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
if len(x.ObjectId) != 0 {
|
||||
mm.AppendString(1, x.ObjectId)
|
||||
}
|
||||
if len(x.ContainerId) != 0 {
|
||||
mm.AppendString(2, x.ContainerId)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalProtobuf implements the encoding.ProtoUnmarshaler interface.
|
||||
func (x *ListShardsForObjectRequest_Body) UnmarshalProtobuf(src []byte) (err error) {
|
||||
var fc easyproto.FieldContext
|
||||
for len(src) > 0 {
|
||||
src, err = fc.NextField(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read next field in %s", "ListShardsForObjectRequest_Body")
|
||||
}
|
||||
switch fc.FieldNum {
|
||||
case 1: // ObjectId
|
||||
data, ok := fc.String()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "ObjectId")
|
||||
}
|
||||
x.ObjectId = data
|
||||
case 2: // ContainerId
|
||||
data, ok := fc.String()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "ContainerId")
|
||||
}
|
||||
x.ContainerId = data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) GetObjectId() string {
|
||||
if x != nil {
|
||||
return x.ObjectId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) SetObjectId(v string) {
|
||||
x.ObjectId = v
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) GetContainerId() string {
|
||||
if x != nil {
|
||||
return x.ContainerId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) SetContainerId(v string) {
|
||||
x.ContainerId = v
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (x *ListShardsForObjectRequest_Body) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
x.MarshalEasyJSON(&w)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) MarshalEasyJSON(out *jwriter.Writer) {
|
||||
if x == nil {
|
||||
out.RawString("null")
|
||||
return
|
||||
}
|
||||
first := true
|
||||
out.RawByte('{')
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"objectId\":"
|
||||
out.RawString(prefix)
|
||||
out.String(x.ObjectId)
|
||||
}
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"containerId\":"
|
||||
out.RawString(prefix)
|
||||
out.String(x.ContainerId)
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (x *ListShardsForObjectRequest_Body) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
x.UnmarshalEasyJSON(&r)
|
||||
return r.Error()
|
||||
}
|
||||
func (x *ListShardsForObjectRequest_Body) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "objectId":
|
||||
{
|
||||
var f string
|
||||
f = in.String()
|
||||
x.ObjectId = f
|
||||
}
|
||||
case "containerId":
|
||||
{
|
||||
var f string
|
||||
f = in.String()
|
||||
x.ContainerId = f
|
||||
}
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
||||
type ListShardsForObjectRequest struct {
|
||||
Body *ListShardsForObjectRequest_Body `json:"body"`
|
||||
Signature *Signature `json:"signature"`
|
||||
}
|
||||
|
||||
var (
|
||||
_ encoding.ProtoMarshaler = (*ListShardsForObjectRequest)(nil)
|
||||
_ encoding.ProtoUnmarshaler = (*ListShardsForObjectRequest)(nil)
|
||||
_ json.Marshaler = (*ListShardsForObjectRequest)(nil)
|
||||
_ json.Unmarshaler = (*ListShardsForObjectRequest)(nil)
|
||||
)
|
||||
|
||||
// StableSize returns the size of x in protobuf format.
|
||||
//
|
||||
// Structures with the same field values have the same binary size.
|
||||
func (x *ListShardsForObjectRequest) StableSize() (size int) {
|
||||
if x == nil {
|
||||
return 0
|
||||
}
|
||||
size += proto.NestedStructureSize(1, x.Body)
|
||||
size += proto.NestedStructureSize(2, x.Signature)
|
||||
return size
|
||||
}
|
||||
|
||||
// ReadSignedData fills buf with signed data of x.
|
||||
// If buffer length is less than x.SignedDataSize(), new buffer is allocated.
|
||||
//
|
||||
// Returns any error encountered which did not allow writing the data completely.
|
||||
// Otherwise, returns the buffer in which the data is written.
|
||||
//
|
||||
// Structures with the same field values have the same signed data.
|
||||
func (x *ListShardsForObjectRequest) SignedDataSize() int {
|
||||
return x.GetBody().StableSize()
|
||||
}
|
||||
|
||||
// SignedDataSize returns size of the request signed data in bytes.
|
||||
//
|
||||
// Structures with the same field values have the same signed data size.
|
||||
func (x *ListShardsForObjectRequest) ReadSignedData(buf []byte) ([]byte, error) {
|
||||
return x.GetBody().MarshalProtobuf(buf), nil
|
||||
}
|
||||
|
||||
// MarshalProtobuf implements the encoding.ProtoMarshaler interface.
|
||||
func (x *ListShardsForObjectRequest) MarshalProtobuf(dst []byte) []byte {
|
||||
m := pool.MarshalerPool.Get()
|
||||
defer pool.MarshalerPool.Put(m)
|
||||
x.EmitProtobuf(m.MessageMarshaler())
|
||||
dst = m.Marshal(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (x *ListShardsForObjectRequest) EmitProtobuf(mm *easyproto.MessageMarshaler) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
if x.Body != nil {
|
||||
x.Body.EmitProtobuf(mm.AppendMessage(1))
|
||||
}
|
||||
if x.Signature != nil {
|
||||
x.Signature.EmitProtobuf(mm.AppendMessage(2))
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalProtobuf implements the encoding.ProtoUnmarshaler interface.
|
||||
func (x *ListShardsForObjectRequest) UnmarshalProtobuf(src []byte) (err error) {
|
||||
var fc easyproto.FieldContext
|
||||
for len(src) > 0 {
|
||||
src, err = fc.NextField(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read next field in %s", "ListShardsForObjectRequest")
|
||||
}
|
||||
switch fc.FieldNum {
|
||||
case 1: // Body
|
||||
data, ok := fc.MessageData()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "Body")
|
||||
}
|
||||
x.Body = new(ListShardsForObjectRequest_Body)
|
||||
if err := x.Body.UnmarshalProtobuf(data); err != nil {
|
||||
return fmt.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
case 2: // Signature
|
||||
data, ok := fc.MessageData()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "Signature")
|
||||
}
|
||||
x.Signature = new(Signature)
|
||||
if err := x.Signature.UnmarshalProtobuf(data); err != nil {
|
||||
return fmt.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) GetBody() *ListShardsForObjectRequest_Body {
|
||||
if x != nil {
|
||||
return x.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) SetBody(v *ListShardsForObjectRequest_Body) {
|
||||
x.Body = v
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) GetSignature() *Signature {
|
||||
if x != nil {
|
||||
return x.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) SetSignature(v *Signature) {
|
||||
x.Signature = v
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (x *ListShardsForObjectRequest) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
x.MarshalEasyJSON(&w)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) MarshalEasyJSON(out *jwriter.Writer) {
|
||||
if x == nil {
|
||||
out.RawString("null")
|
||||
return
|
||||
}
|
||||
first := true
|
||||
out.RawByte('{')
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"body\":"
|
||||
out.RawString(prefix)
|
||||
x.Body.MarshalEasyJSON(out)
|
||||
}
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"signature\":"
|
||||
out.RawString(prefix)
|
||||
x.Signature.MarshalEasyJSON(out)
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (x *ListShardsForObjectRequest) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
x.UnmarshalEasyJSON(&r)
|
||||
return r.Error()
|
||||
}
|
||||
func (x *ListShardsForObjectRequest) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "body":
|
||||
{
|
||||
var f *ListShardsForObjectRequest_Body
|
||||
f = new(ListShardsForObjectRequest_Body)
|
||||
f.UnmarshalEasyJSON(in)
|
||||
x.Body = f
|
||||
}
|
||||
case "signature":
|
||||
{
|
||||
var f *Signature
|
||||
f = new(Signature)
|
||||
f.UnmarshalEasyJSON(in)
|
||||
x.Signature = f
|
||||
}
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
||||
type ListShardsForObjectResponse_Body struct {
|
||||
Shard_ID [][]byte `json:"shardID"`
|
||||
}
|
||||
|
||||
var (
|
||||
_ encoding.ProtoMarshaler = (*ListShardsForObjectResponse_Body)(nil)
|
||||
_ encoding.ProtoUnmarshaler = (*ListShardsForObjectResponse_Body)(nil)
|
||||
_ json.Marshaler = (*ListShardsForObjectResponse_Body)(nil)
|
||||
_ json.Unmarshaler = (*ListShardsForObjectResponse_Body)(nil)
|
||||
)
|
||||
|
||||
// StableSize returns the size of x in protobuf format.
|
||||
//
|
||||
// Structures with the same field values have the same binary size.
|
||||
func (x *ListShardsForObjectResponse_Body) StableSize() (size int) {
|
||||
if x == nil {
|
||||
return 0
|
||||
}
|
||||
size += proto.RepeatedBytesSize(1, x.Shard_ID)
|
||||
return size
|
||||
}
|
||||
|
||||
// MarshalProtobuf implements the encoding.ProtoMarshaler interface.
|
||||
func (x *ListShardsForObjectResponse_Body) MarshalProtobuf(dst []byte) []byte {
|
||||
m := pool.MarshalerPool.Get()
|
||||
defer pool.MarshalerPool.Put(m)
|
||||
x.EmitProtobuf(m.MessageMarshaler())
|
||||
dst = m.Marshal(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (x *ListShardsForObjectResponse_Body) EmitProtobuf(mm *easyproto.MessageMarshaler) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
for j := range x.Shard_ID {
|
||||
mm.AppendBytes(1, x.Shard_ID[j])
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalProtobuf implements the encoding.ProtoUnmarshaler interface.
|
||||
func (x *ListShardsForObjectResponse_Body) UnmarshalProtobuf(src []byte) (err error) {
|
||||
var fc easyproto.FieldContext
|
||||
for len(src) > 0 {
|
||||
src, err = fc.NextField(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read next field in %s", "ListShardsForObjectResponse_Body")
|
||||
}
|
||||
switch fc.FieldNum {
|
||||
case 1: // Shard_ID
|
||||
data, ok := fc.Bytes()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "Shard_ID")
|
||||
}
|
||||
x.Shard_ID = append(x.Shard_ID, data)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectResponse_Body) GetShard_ID() [][]byte {
|
||||
if x != nil {
|
||||
return x.Shard_ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectResponse_Body) SetShard_ID(v [][]byte) {
|
||||
x.Shard_ID = v
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (x *ListShardsForObjectResponse_Body) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
x.MarshalEasyJSON(&w)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
func (x *ListShardsForObjectResponse_Body) MarshalEasyJSON(out *jwriter.Writer) {
|
||||
if x == nil {
|
||||
out.RawString("null")
|
||||
return
|
||||
}
|
||||
first := true
|
||||
out.RawByte('{')
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"shardID\":"
|
||||
out.RawString(prefix)
|
||||
out.RawByte('[')
|
||||
for i := range x.Shard_ID {
|
||||
if i != 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
if x.Shard_ID[i] != nil {
|
||||
out.Base64Bytes(x.Shard_ID[i])
|
||||
} else {
|
||||
out.String("")
|
||||
}
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (x *ListShardsForObjectResponse_Body) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
x.UnmarshalEasyJSON(&r)
|
||||
return r.Error()
|
||||
}
|
||||
func (x *ListShardsForObjectResponse_Body) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "shardID":
|
||||
{
|
||||
var f []byte
|
||||
var list [][]byte
|
||||
in.Delim('[')
|
||||
for !in.IsDelim(']') {
|
||||
{
|
||||
tmp := in.Bytes()
|
||||
if len(tmp) == 0 {
|
||||
tmp = nil
|
||||
}
|
||||
f = tmp
|
||||
}
|
||||
list = append(list, f)
|
||||
in.WantComma()
|
||||
}
|
||||
x.Shard_ID = list
|
||||
in.Delim(']')
|
||||
}
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
||||
type ListShardsForObjectResponse struct {
|
||||
Body *ListShardsForObjectResponse_Body `json:"body"`
|
||||
Signature *Signature `json:"signature"`
|
||||
}
|
||||
|
||||
var (
|
||||
_ encoding.ProtoMarshaler = (*ListShardsForObjectResponse)(nil)
|
||||
_ encoding.ProtoUnmarshaler = (*ListShardsForObjectResponse)(nil)
|
||||
_ json.Marshaler = (*ListShardsForObjectResponse)(nil)
|
||||
_ json.Unmarshaler = (*ListShardsForObjectResponse)(nil)
|
||||
)
|
||||
|
||||
// StableSize returns the size of x in protobuf format.
|
||||
//
|
||||
// Structures with the same field values have the same binary size.
|
||||
func (x *ListShardsForObjectResponse) StableSize() (size int) {
|
||||
if x == nil {
|
||||
return 0
|
||||
}
|
||||
size += proto.NestedStructureSize(1, x.Body)
|
||||
size += proto.NestedStructureSize(2, x.Signature)
|
||||
return size
|
||||
}
|
||||
|
||||
// ReadSignedData fills buf with signed data of x.
|
||||
// If buffer length is less than x.SignedDataSize(), new buffer is allocated.
|
||||
//
|
||||
// Returns any error encountered which did not allow writing the data completely.
|
||||
// Otherwise, returns the buffer in which the data is written.
|
||||
//
|
||||
// Structures with the same field values have the same signed data.
|
||||
func (x *ListShardsForObjectResponse) SignedDataSize() int {
|
||||
return x.GetBody().StableSize()
|
||||
}
|
||||
|
||||
// SignedDataSize returns size of the request signed data in bytes.
|
||||
//
|
||||
// Structures with the same field values have the same signed data size.
|
||||
func (x *ListShardsForObjectResponse) ReadSignedData(buf []byte) ([]byte, error) {
|
||||
return x.GetBody().MarshalProtobuf(buf), nil
|
||||
}
|
||||
|
||||
// MarshalProtobuf implements the encoding.ProtoMarshaler interface.
|
||||
func (x *ListShardsForObjectResponse) MarshalProtobuf(dst []byte) []byte {
|
||||
m := pool.MarshalerPool.Get()
|
||||
defer pool.MarshalerPool.Put(m)
|
||||
x.EmitProtobuf(m.MessageMarshaler())
|
||||
dst = m.Marshal(dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (x *ListShardsForObjectResponse) EmitProtobuf(mm *easyproto.MessageMarshaler) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
if x.Body != nil {
|
||||
x.Body.EmitProtobuf(mm.AppendMessage(1))
|
||||
}
|
||||
if x.Signature != nil {
|
||||
x.Signature.EmitProtobuf(mm.AppendMessage(2))
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalProtobuf implements the encoding.ProtoUnmarshaler interface.
|
||||
func (x *ListShardsForObjectResponse) UnmarshalProtobuf(src []byte) (err error) {
|
||||
var fc easyproto.FieldContext
|
||||
for len(src) > 0 {
|
||||
src, err = fc.NextField(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read next field in %s", "ListShardsForObjectResponse")
|
||||
}
|
||||
switch fc.FieldNum {
|
||||
case 1: // Body
|
||||
data, ok := fc.MessageData()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "Body")
|
||||
}
|
||||
x.Body = new(ListShardsForObjectResponse_Body)
|
||||
if err := x.Body.UnmarshalProtobuf(data); err != nil {
|
||||
return fmt.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
case 2: // Signature
|
||||
data, ok := fc.MessageData()
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal field %s", "Signature")
|
||||
}
|
||||
x.Signature = new(Signature)
|
||||
if err := x.Signature.UnmarshalProtobuf(data); err != nil {
|
||||
return fmt.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) GetBody() *ListShardsForObjectResponse_Body {
|
||||
if x != nil {
|
||||
return x.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) SetBody(v *ListShardsForObjectResponse_Body) {
|
||||
x.Body = v
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) GetSignature() *Signature {
|
||||
if x != nil {
|
||||
return x.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) SetSignature(v *Signature) {
|
||||
x.Signature = v
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (x *ListShardsForObjectResponse) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
x.MarshalEasyJSON(&w)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) MarshalEasyJSON(out *jwriter.Writer) {
|
||||
if x == nil {
|
||||
out.RawString("null")
|
||||
return
|
||||
}
|
||||
first := true
|
||||
out.RawByte('{')
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"body\":"
|
||||
out.RawString(prefix)
|
||||
x.Body.MarshalEasyJSON(out)
|
||||
}
|
||||
{
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
const prefix string = "\"signature\":"
|
||||
out.RawString(prefix)
|
||||
x.Signature.MarshalEasyJSON(out)
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (x *ListShardsForObjectResponse) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
x.UnmarshalEasyJSON(&r)
|
||||
return r.Error()
|
||||
}
|
||||
func (x *ListShardsForObjectResponse) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "body":
|
||||
{
|
||||
var f *ListShardsForObjectResponse_Body
|
||||
f = new(ListShardsForObjectResponse_Body)
|
||||
f.UnmarshalEasyJSON(in)
|
||||
x.Body = f
|
||||
}
|
||||
case "signature":
|
||||
{
|
||||
var f *Signature
|
||||
f = new(Signature)
|
||||
f.UnmarshalEasyJSON(in)
|
||||
x.Signature = f
|
||||
}
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
|
|
39
pkg/services/control/service_grpc.pb.go
generated
|
@ -41,6 +41,7 @@ const (
|
|||
ControlService_SealWriteCache_FullMethodName = "/control.ControlService/SealWriteCache"
|
||||
ControlService_DetachShards_FullMethodName = "/control.ControlService/DetachShards"
|
||||
ControlService_StartShardRebuild_FullMethodName = "/control.ControlService/StartShardRebuild"
|
||||
ControlService_ListShardsForObject_FullMethodName = "/control.ControlService/ListShardsForObject"
|
||||
)
|
||||
|
||||
// ControlServiceClient is the client API for ControlService service.
|
||||
|
@ -95,6 +96,8 @@ type ControlServiceClient interface {
|
|||
DetachShards(ctx context.Context, in *DetachShardsRequest, opts ...grpc.CallOption) (*DetachShardsResponse, error)
|
||||
// StartShardRebuild starts shard rebuild process.
|
||||
StartShardRebuild(ctx context.Context, in *StartShardRebuildRequest, opts ...grpc.CallOption) (*StartShardRebuildResponse, error)
|
||||
// ListShardsForObject returns shard info where object is stored.
|
||||
ListShardsForObject(ctx context.Context, in *ListShardsForObjectRequest, opts ...grpc.CallOption) (*ListShardsForObjectResponse, error)
|
||||
}
|
||||
|
||||
type controlServiceClient struct {
|
||||
|
@ -303,6 +306,15 @@ func (c *controlServiceClient) StartShardRebuild(ctx context.Context, in *StartS
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlServiceClient) ListShardsForObject(ctx context.Context, in *ListShardsForObjectRequest, opts ...grpc.CallOption) (*ListShardsForObjectResponse, error) {
|
||||
out := new(ListShardsForObjectResponse)
|
||||
err := c.cc.Invoke(ctx, ControlService_ListShardsForObject_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ControlServiceServer is the server API for ControlService service.
|
||||
// All implementations should embed UnimplementedControlServiceServer
|
||||
// for forward compatibility
|
||||
|
@ -355,6 +367,8 @@ type ControlServiceServer interface {
|
|||
DetachShards(context.Context, *DetachShardsRequest) (*DetachShardsResponse, error)
|
||||
// StartShardRebuild starts shard rebuild process.
|
||||
StartShardRebuild(context.Context, *StartShardRebuildRequest) (*StartShardRebuildResponse, error)
|
||||
// ListShardsForObject returns shard info where object is stored.
|
||||
ListShardsForObject(context.Context, *ListShardsForObjectRequest) (*ListShardsForObjectResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedControlServiceServer should be embedded to have forward compatible implementations.
|
||||
|
@ -427,6 +441,9 @@ func (UnimplementedControlServiceServer) DetachShards(context.Context, *DetachSh
|
|||
func (UnimplementedControlServiceServer) StartShardRebuild(context.Context, *StartShardRebuildRequest) (*StartShardRebuildResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method StartShardRebuild not implemented")
|
||||
}
|
||||
func (UnimplementedControlServiceServer) ListShardsForObject(context.Context, *ListShardsForObjectRequest) (*ListShardsForObjectResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListShardsForObject not implemented")
|
||||
}
|
||||
|
||||
// UnsafeControlServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ControlServiceServer will
|
||||
|
@ -835,6 +852,24 @@ func _ControlService_StartShardRebuild_Handler(srv interface{}, ctx context.Cont
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlService_ListShardsForObject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListShardsForObjectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServiceServer).ListShardsForObject(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlService_ListShardsForObject_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServiceServer).ListShardsForObject(ctx, req.(*ListShardsForObjectRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ControlService_ServiceDesc is the grpc.ServiceDesc for ControlService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
|
@ -930,6 +965,10 @@ var ControlService_ServiceDesc = grpc.ServiceDesc{
|
|||
MethodName: "StartShardRebuild",
|
||||
Handler: _ControlService_StartShardRebuild_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListShardsForObject",
|
||||
Handler: _ControlService_ListShardsForObject_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "pkg/services/control/service.proto",
|
||||
|
|
Control commands have different common flags, see the command for shard listing
initControlFlags(listShardsCmd)
Please double-check and fix if needed
Fixed