forked from TrueCloudLab/frostfs-s3-gw
[#135] crdt: Add GSet
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
751d66bde0
commit
0590f84d68
2 changed files with 162 additions and 0 deletions
124
internal/frostfs/crdt/gset.go
Normal file
124
internal/frostfs/crdt/gset.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package crdt
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
const (
|
||||
versionsAddAttr = "S3-CRDT-Versions-Add"
|
||||
)
|
||||
|
||||
type ObjectVersions struct {
|
||||
name string
|
||||
objects []*ObjectVersion
|
||||
addList []string
|
||||
isSorted bool
|
||||
}
|
||||
|
||||
type ObjectVersion struct {
|
||||
OjbID oid.ID
|
||||
Headers map[string]string
|
||||
CreationEpoch uint64
|
||||
}
|
||||
|
||||
func (o *ObjectVersion) VersionID() string {
|
||||
return o.OjbID.EncodeToString()
|
||||
}
|
||||
|
||||
func NewObjectVersions(name string) *ObjectVersions {
|
||||
return &ObjectVersions{name: name}
|
||||
}
|
||||
|
||||
func NewObjectVersion(obj *object.Object) *ObjectVersion {
|
||||
objID, _ := obj.ID()
|
||||
headers := make(map[string]string)
|
||||
|
||||
for _, attr := range obj.Attributes() {
|
||||
headers[attr.Key()] = attr.Value()
|
||||
}
|
||||
|
||||
return &ObjectVersion{
|
||||
OjbID: objID,
|
||||
Headers: headers,
|
||||
CreationEpoch: obj.CreationEpoch(),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) AppendVersion(ov *ObjectVersion) {
|
||||
addVers := append(splitVersions(ov.Headers[versionsAddAttr]), ov.VersionID())
|
||||
v.objects = append(v.objects, ov)
|
||||
for _, add := range addVers {
|
||||
if !contains(v.addList, add) {
|
||||
v.addList = append(v.addList, add)
|
||||
}
|
||||
}
|
||||
v.isSorted = false
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) GetCRDTHeaders() map[string]string {
|
||||
if len(v.objects) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
headers := make(map[string]string, 2)
|
||||
|
||||
if len(v.addList) != 0 {
|
||||
headers[versionsAddAttr] = v.getAddHeader()
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) GetLast() *ObjectVersion {
|
||||
if len(v.objects) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
v.sort()
|
||||
return v.objects[len(v.objects)-1]
|
||||
}
|
||||
|
||||
func splitVersions(header string) []string {
|
||||
if len(header) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.Split(header, ",")
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) sort() {
|
||||
if !v.isSorted {
|
||||
sort.Slice(v.objects, func(i, j int) bool {
|
||||
return less(v.objects[i], v.objects[j])
|
||||
})
|
||||
v.isSorted = true
|
||||
}
|
||||
}
|
||||
|
||||
func (v *ObjectVersions) getAddHeader() string {
|
||||
return strings.Join(v.addList, ",")
|
||||
}
|
||||
|
||||
func less(ov1, ov2 *ObjectVersion) bool {
|
||||
if ov1.CreationEpoch == ov2.CreationEpoch {
|
||||
return ov1.VersionID() < ov2.VersionID()
|
||||
}
|
||||
return ov1.CreationEpoch < ov2.CreationEpoch
|
||||
}
|
||||
|
||||
func contains(list []string, elem string) bool {
|
||||
for _, item := range list {
|
||||
if elem == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
38
internal/frostfs/crdt/gset_test.go
Normal file
38
internal/frostfs/crdt/gset_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package crdt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestObjectVersionsSort(t *testing.T) {
|
||||
t.Run("sort by epoch", func(t *testing.T) {
|
||||
versions := NewObjectVersions("test")
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 3})
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 1})
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 2})
|
||||
|
||||
last := versions.GetLast()
|
||||
require.Equal(t, uint64(3), last.CreationEpoch)
|
||||
})
|
||||
|
||||
t.Run("sort by oids", func(t *testing.T) {
|
||||
versions := NewObjectVersions("test")
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 3, OjbID: getTestOID(2)})
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 3, OjbID: getTestOID(1)})
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 1, OjbID: getTestOID(3)})
|
||||
versions.AppendVersion(&ObjectVersion{CreationEpoch: 2, OjbID: getTestOID(4)})
|
||||
|
||||
last := versions.GetLast()
|
||||
require.Equal(t, uint64(3), last.CreationEpoch)
|
||||
require.Equal(t, getTestOID(2).String(), last.VersionID())
|
||||
})
|
||||
}
|
||||
|
||||
func getTestOID(val byte) oid.ID {
|
||||
var res oid.ID
|
||||
res.SetSHA256([32]byte{val})
|
||||
return res
|
||||
}
|
Loading…
Reference in a new issue