[#159] Add handle __SYSTEM__ sys attributes #159
10 changed files with 40 additions and 26 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
```
|
```
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
4
go.mod
|
@ -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
BIN
go.sum
Binary file not shown.
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|||||||
|
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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue
Can we try having 1 bucket on the metabase level? I think translation could be done on an upper level.
Okay, it can make our code more complex without many benefits. Let's postpone this discussion.