[#159] Add handle __SYSTEM__ sys attributes #159

Merged
fyrchik merged 1 commit from dkirillov/frostfs-node:feature/add_frostfs_system_attribute into master 2023-03-23 08:10:22 +00:00
10 changed files with 40 additions and 26 deletions

View file

@ -26,6 +26,7 @@ Changelog for FrostFS Node
- `neofs-cli` buffer for object put increased from 4 KiB to 3 MiB (#2243) - `neofs-cli` buffer for object put increased from 4 KiB to 3 MiB (#2243)
- Expired locked object is available for reading (#56) - Expired locked object is available for reading (#56)
- Initialize write-cache asynchronously (#32) - Initialize write-cache asynchronously (#32)
- Update system attribute names (#159)
### Fixed ### Fixed
- Increase payload size metric on shards' `put` operation (#1794) - Increase payload size metric on shards' `put` operation (#1794)
@ -70,6 +71,9 @@ You need to change configuration environment variables to `FROSTFS_*` if you use
New config field `object.delete.tombstone_lifetime` allows to set tombstone lifetime New config field `object.delete.tombstone_lifetime` allows to set tombstone lifetime
more appropriate for a specific deployment. more appropriate for a specific deployment.
Use `__SYSTEM__` prefix for system attributes instead of `__NEOFS__`
(existed objects with old attributes will be treated as before, but for new objects new attributes will be used).
## [0.35.0] - 2022-12-28 - Sindo (신도, 信島) ## [0.35.0] - 2022-12-28 - Sindo (신도, 信島)
### Added ### Added

View file

@ -9,16 +9,16 @@ duplicated header names or headers with empty values are considered invalid.
## Existing headers ## Existing headers
There are some "well-known" headers starting with `__FROSTFS__` prefix that There are some "well-known" headers starting with `__SYSTEM__` prefix that
affect system behaviour. For backward compatibility, the same set of affect system behaviour. For backward compatibility, the same set of
"well-known" headers may also use `__NEOFS__` prefix: "well-known" headers may also use `__NEOFS__` prefix:
* `__FROSTFS__NETMAP_EPOCH` - netmap epoch to use for object placement calculation. The `value` is string * `__SYSTEM__NETMAP_EPOCH` - netmap epoch to use for object placement calculation. The `value` is string
encoded `uint64` in decimal presentation. If set to '0' or omitted, the encoded `uint64` in decimal presentation. If set to '0' or omitted, the
current epoch only will be used. current epoch only will be used.
* `__FROSTFS__NETMAP_LOOKUP_DEPTH` - if object can't be found using current epoch's netmap, this header limits * `__SYSTEM__NETMAP_LOOKUP_DEPTH` - if object can't be found using current epoch's netmap, this header limits
how many past epochs the node can look up through. Depth is applied to a current epoch or the value how many past epochs the node can look up through. Depth is applied to a current epoch or the value
of `__FROSTFS__NETMAP_EPOCH` attribute. The `value` is string encoded `uint64` in decimal presentation. of `__SYSTEM__NETMAP_EPOCH` attribute. The `value` is string encoded `uint64` in decimal presentation.
If set to '0' or not set, only the current epoch is used. If set to '0' or not set, only the current epoch is used.
## `frostfs-cli` commands with `--xhdr` ## `frostfs-cli` commands with `--xhdr`
@ -30,5 +30,5 @@ List of commands with support of extended headers:
Example: Example:
```shell ```shell
$ frostfs-cli object put -r s01.frostfs.devenv:8080 -w wallet.json --cid CID --file FILE --xhdr "__FROSTFS__NETMAP_EPOCH=777" $ frostfs-cli object put -r s01.frostfs.devenv:8080 -w wallet.json --cid CID --file FILE --xhdr "__SYSTEM__NETMAP_EPOCH=777"
``` ```

View file

@ -62,7 +62,7 @@ var listContainersCmd = &cobra.Command{
res, err := internalclient.GetContainer(prmGet) res, err := internalclient.GetContainer(prmGet)
if err == nil { if err == nil {
res.Container().IterateAttributes(func(key, val string) { res.Container().IterateAttributes(func(key, val string) {
if !strings.HasPrefix(key, container.SysAttributePrefix) { if !strings.HasPrefix(key, container.SysAttributePrefix) && !strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
// FIXME(@cthulhu-rider): neofs-sdk-go#314 use dedicated method to skip system attributes // FIXME(@cthulhu-rider): neofs-sdk-go#314 use dedicated method to skip system attributes
cmd.Printf(" %s: %s\n", key, val) cmd.Printf(" %s: %s\n", key, val)
} }

View file

@ -70,7 +70,7 @@ var listContainerObjectsCmd = &cobra.Command{
attrs := resHead.Header().Attributes() attrs := resHead.Header().Attributes()
for i := range attrs { for i := range attrs {
attrKey := attrs[i].Key() attrKey := attrs[i].Key()
if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) { if !strings.HasPrefix(attrKey, v2object.SysAttributePrefix) && !strings.HasPrefix(attrKey, v2object.SysAttributePrefixNeoFS) {
// FIXME(@cthulhu-rider): neofs-sdk-go#226 use dedicated method to skip system attributes // FIXME(@cthulhu-rider): neofs-sdk-go#226 use dedicated method to skip system attributes
cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value()) cmd.Printf(" %s: %s\n", attrKey, attrs[i].Value())
} }

4
go.mod
View file

@ -3,9 +3,9 @@ module git.frostfs.info/TrueCloudLab/frostfs-node
go 1.18 go 1.18
require ( require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.11.2-0.20230307104236-f69d2ad83c51 git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.11.2-0.20230315095236-9dc375346703
git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb git.frostfs.info/TrueCloudLab/frostfs-contract v0.0.0-20230307110621-19a8ef2d02fb
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230307124721-94476f905599 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230316081442-bec77f280a85
git.frostfs.info/TrueCloudLab/hrw v1.2.0 git.frostfs.info/TrueCloudLab/hrw v1.2.0
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 git.frostfs.info/TrueCloudLab/tzhash v1.8.0
github.com/cheggaaa/pb v1.0.29 github.com/cheggaaa/pb v1.0.29

BIN
go.sum

Binary file not shown.

View file

@ -353,7 +353,7 @@ func (v *FormatValidator) checkExpiration(obj *object.Object) error {
func expirationEpochAttribute(obj *object.Object) (uint64, error) { func expirationEpochAttribute(obj *object.Object) (uint64, error) {
for _, a := range obj.Attributes() { for _, a := range obj.Attributes() {
if a.Key() != objectV2.SysAttributeExpEpoch { if a.Key() != objectV2.SysAttributeExpEpoch && a.Key() != objectV2.SysAttributeExpEpochNeoFS {
continue continue
} }

View file

@ -110,20 +110,9 @@ func objectStatus(tx *bbolt.Tx, addr oid.Address, currEpoch uint64) uint8 {
// GC is expected to collect all the objects that have // GC is expected to collect all the objects that have
// expired previously for less than the one epoch duration // expired previously for less than the one epoch duration
var expired bool expired := isExpiredWithAttribute(tx, objectV2.SysAttributeExpEpoch, addr, currEpoch)
if !expired {
// bucket with objects that have expiration attr expired = isExpiredWithAttribute(tx, objectV2.SysAttributeExpEpochNeoFS, addr, currEpoch)
attrKey := make([]byte, bucketKeySize+len(objectV2.SysAttributeExpEpoch))
expirationBucket := tx.Bucket(attributeBucketName(addr.Container(), objectV2.SysAttributeExpEpoch, attrKey))
if expirationBucket != nil {
// bucket that contains objects that expire in the current epoch
prevEpochBkt := expirationBucket.Bucket([]byte(strconv.FormatUint(currEpoch-1, 10)))
if prevEpochBkt != nil {
rawOID := objectKey(addr.Object(), make([]byte, objectKeySize))
if prevEpochBkt.Get(rawOID) != nil {
expired = true
}
}
} }
if expired { if expired {
@ -136,6 +125,24 @@ func objectStatus(tx *bbolt.Tx, addr oid.Address, currEpoch uint64) uint8 {
return inGraveyardWithKey(addrKey, graveyardBkt, garbageBkt) return inGraveyardWithKey(addrKey, graveyardBkt, garbageBkt)
} }
func isExpiredWithAttribute(tx *bbolt.Tx, attr string, addr oid.Address, currEpoch uint64) bool {
// bucket with objects that have expiration attr
attrKey := make([]byte, bucketKeySize+len(attr))
expirationBucket := tx.Bucket(attributeBucketName(addr.Container(), attr, attrKey))
if expirationBucket != nil {
// bucket that contains objects that expire in the current epoch
prevEpochBkt := expirationBucket.Bucket([]byte(strconv.FormatUint(currEpoch-1, 10)))
if prevEpochBkt != nil {
rawOID := objectKey(addr.Object(), make([]byte, objectKeySize))
if prevEpochBkt.Get(rawOID) != nil {
return true
}
}
}
return false
}
func inGraveyardWithKey(addrKey []byte, graveyard, garbageBCK *bbolt.Bucket) uint8 { func inGraveyardWithKey(addrKey []byte, graveyard, garbageBCK *bbolt.Bucket) uint8 {
if graveyard == nil { if graveyard == nil {
// incorrect metabase state, does not make // incorrect metabase state, does not make

View file

@ -59,9 +59,12 @@ func (db *DB) IterateExpired(epoch uint64, h ExpiredObjectHandler) error {
func (db *DB) iterateExpired(tx *bbolt.Tx, epoch uint64, h ExpiredObjectHandler) error { func (db *DB) iterateExpired(tx *bbolt.Tx, epoch uint64, h ExpiredObjectHandler) error {
err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error { err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
cidBytes := cidFromAttributeBucket(name, objectV2.SysAttributeExpEpoch) cidBytes := cidFromAttributeBucket(name, objectV2.SysAttributeExpEpoch)
fyrchik marked this conversation as resolved
Review

Can we try having 1 bucket on the metabase level? I think translation could be done on an upper level.

Can we try having 1 bucket on the metabase level? I think translation could be done on an upper level.
Review

Okay, it can make our code more complex without many benefits. Let's postpone this discussion.

Okay, it can make our code more complex without many benefits. Let's postpone this discussion.
if cidBytes == nil {
cidBytes = cidFromAttributeBucket(name, objectV2.SysAttributeExpEpochNeoFS)
if cidBytes == nil { if cidBytes == nil {
return nil return nil
} }
}
var cnrID cid.ID var cnrID cid.ID
err := cnrID.Decode(cidBytes) err := cnrID.Decode(cidBytes)

View file

@ -73,7 +73,7 @@ func (g *ExpirationChecker) IsTombstoneAvailable(ctx context.Context, a oid.Addr
func (g *ExpirationChecker) handleTS(addr string, ts *object.Object, reqEpoch uint64) bool { func (g *ExpirationChecker) handleTS(addr string, ts *object.Object, reqEpoch uint64) bool {
for _, atr := range ts.Attributes() { for _, atr := range ts.Attributes() {
if atr.Key() == objectV2.SysAttributeExpEpoch { if atr.Key() == objectV2.SysAttributeExpEpoch || atr.Key() == objectV2.SysAttributeExpEpochNeoFS {
epoch, err := strconv.ParseUint(atr.Value(), 10, 64) epoch, err := strconv.ParseUint(atr.Value(), 10, 64)
if err != nil { if err != nil {
g.log.Warn( g.log.Warn(