any-sync/node/storage/spacestorage.go
2023-01-03 00:02:33 +01:00

233 lines
5.8 KiB
Go

package storage
import (
"github.com/akrylysov/pogreb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/object/acl/liststorage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/object/tree/treestorage"
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacestorage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"go.uber.org/zap"
"os"
"path"
"time"
)
var (
defPogrebOptions = &pogreb.Options{BackgroundCompactionInterval: time.Minute * 5}
log = logger.NewNamed("storage.spacestorage")
spaceValidationFunc = spacestorage.ValidateSpaceStorageCreatePayload
)
type spaceStorage struct {
spaceId string
spaceSettingsId string
objDb *pogreb.DB
keys spaceKeys
aclStorage liststorage.ListStorage
header *spacesyncproto.RawSpaceHeaderWithId
}
func newSpaceStorage(rootPath string, spaceId string) (store spacestorage.SpaceStorage, err error) {
log.With(zap.String("id", spaceId)).Debug("space storage opening with new")
dbPath := path.Join(rootPath, spaceId)
if _, err = os.Stat(dbPath); err != nil {
err = spacestorage.ErrSpaceStorageMissing
return
}
objDb, err := pogreb.Open(dbPath, defPogrebOptions)
if err != nil {
return
}
defer func() {
if err != nil {
log.With(zap.String("id", spaceId), zap.Error(err)).Warn("failed to open storage")
objDb.Close()
}
}()
keys := newSpaceKeys(spaceId)
has, err := objDb.Has(keys.SpaceIdKey())
if err != nil {
return
}
if !has {
err = spacestorage.ErrSpaceStorageMissing
return
}
header, err := objDb.Get(keys.HeaderKey())
if err != nil {
return
}
if header == nil {
err = spacestorage.ErrSpaceStorageMissing
return
}
spaceSettingsId, err := objDb.Get(keys.SpaceSettingsIdKey())
if err != nil {
return
}
if spaceSettingsId == nil {
err = spacestorage.ErrSpaceStorageMissing
return
}
aclStorage, err := newListStorage(objDb)
if err != nil {
return
}
store = &spaceStorage{
spaceId: spaceId,
spaceSettingsId: string(spaceSettingsId),
objDb: objDb,
keys: keys,
header: &spacesyncproto.RawSpaceHeaderWithId{
RawHeader: header,
Id: spaceId,
},
aclStorage: aclStorage,
}
return
}
func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreatePayload) (store spacestorage.SpaceStorage, err error) {
log.With(zap.String("id", payload.SpaceHeaderWithId.Id)).Debug("space storage creating")
dbPath := path.Join(rootPath, payload.SpaceHeaderWithId.Id)
db, err := pogreb.Open(dbPath, defPogrebOptions)
if err != nil {
return
}
defer func() {
if err != nil {
log.With(zap.String("id", payload.SpaceHeaderWithId.Id), zap.Error(err)).Warn("failed to create storage")
db.Close()
}
}()
keys := newSpaceKeys(payload.SpaceHeaderWithId.Id)
has, err := db.Has(keys.SpaceIdKey())
if err != nil {
return
}
if has {
err = spacestorage.ErrSpaceStorageExists
return
}
err = spaceValidationFunc(payload)
if err != nil {
return
}
aclStorage, err := createListStorage(db, payload.AclWithId)
if err != nil {
return
}
store = &spaceStorage{
spaceId: payload.SpaceHeaderWithId.Id,
objDb: db,
keys: keys,
aclStorage: aclStorage,
spaceSettingsId: payload.SpaceSettingsWithId.Id,
header: payload.SpaceHeaderWithId,
}
_, err = store.CreateTreeStorage(treestorage.TreeStorageCreatePayload{
RootRawChange: payload.SpaceSettingsWithId,
Changes: []*treechangeproto.RawTreeChangeWithId{payload.SpaceSettingsWithId},
Heads: []string{payload.SpaceSettingsWithId.Id},
})
if err != nil {
return
}
err = db.Put(keys.SpaceSettingsIdKey(), []byte(payload.SpaceSettingsWithId.Id))
if err != nil {
return
}
err = db.Put(keys.HeaderKey(), payload.SpaceHeaderWithId.RawHeader)
if err != nil {
return
}
err = db.Put(keys.SpaceIdKey(), []byte(payload.SpaceHeaderWithId.Id))
if err != nil {
return
}
return
}
func (s *spaceStorage) Id() string {
return s.spaceId
}
func (s *spaceStorage) SpaceSettingsId() string {
return s.spaceSettingsId
}
func (s *spaceStorage) TreeStorage(id string) (treestorage.TreeStorage, error) {
return newTreeStorage(s.objDb, id)
}
func (s *spaceStorage) TreeRoot(id string) (*treechangeproto.RawTreeChangeWithId, error) {
panic("should not be implemented")
}
func (s *spaceStorage) CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (ts treestorage.TreeStorage, err error) {
return createTreeStorage(s.objDb, payload)
}
func (s *spaceStorage) AclStorage() (liststorage.ListStorage, error) {
return s.aclStorage, nil
}
func (s *spaceStorage) SpaceHeader() (header *spacesyncproto.RawSpaceHeaderWithId, err error) {
return s.header, nil
}
func (s *spaceStorage) SetTreeDeletedStatus(id, state string) (err error) {
return s.objDb.Put(s.keys.TreeDeletedKey(id), []byte(state))
}
func (s *spaceStorage) TreeDeletedStatus(id string) (status string, err error) {
res, err := s.objDb.Get(s.keys.TreeDeletedKey(id))
if err != nil {
return
}
status = string(res)
return
}
func (s *spaceStorage) StoredIds() (ids []string, err error) {
index := s.objDb.Items()
key, _, err := index.Next()
for err == nil {
strKey := string(key)
if isTreeHeadsKey(strKey) {
ids = append(ids, getRootId(strKey))
}
key, _, err = index.Next()
}
if err != pogreb.ErrIterationDone {
return
}
err = nil
return
}
func (s *spaceStorage) Close() (err error) {
log.With(zap.String("id", s.spaceId)).Debug("space storage closed")
return s.objDb.Close()
}