[#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)
|
||||||
fyrchik
commented
Can we do this only if the bucket does not exist? If it exists, we should not bother checking the second. Can we do this only if _the bucket_ does not exist? If it exists, we should not bother checking the second.
Also, we should validate in service that only 1 of the prefixes is used.
|
|||||||
|
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
|
||||||
|
|
|
@ -60,7 +60,10 @@ func (db *DB) iterateExpired(tx *bbolt.Tx, epoch uint64, h ExpiredObjectHandler)
|
||||||
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
fyrchik
commented
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.
fyrchik
commented
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 {
|
if cidBytes == nil {
|
||||||
return nil
|
cidBytes = cidFromAttributeBucket(name, objectV2.SysAttributeExpEpochNeoFS)
|
||||||
|
if cidBytes == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cnrID cid.ID
|
var cnrID cid.ID
|
||||||
|
|
|
@ -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
Not obvious whether
__NEOFS__
is still supported.