all: Reorder sequences for better rename detection (#6574)
This commit is contained in:
@@ -62,6 +62,9 @@ const (
|
||||
|
||||
// KeyTypeBlockList <block list hash> = BlockList
|
||||
KeyTypeBlockList = 13
|
||||
|
||||
// KeyTypeBlockListMap <int32 folder ID> <block list hash> <file name> = <nothing>
|
||||
KeyTypeBlockListMap = 14
|
||||
)
|
||||
|
||||
type keyer interface {
|
||||
@@ -79,6 +82,8 @@ type keyer interface {
|
||||
// block map key stuff (former BlockMap)
|
||||
GenerateBlockMapKey(key, folder, hash, name []byte) (blockMapKey, error)
|
||||
NameFromBlockMapKey(key []byte) []byte
|
||||
GenerateBlockListMapKey(key, folder, hash, name []byte) (blockListMapKey, error)
|
||||
NameFromBlockListMapKey(key []byte) []byte
|
||||
|
||||
// file need index
|
||||
GenerateNeedFileKey(key, folder, name []byte) (needFileKey, error)
|
||||
@@ -203,6 +208,29 @@ func (k blockMapKey) WithoutHashAndName() []byte {
|
||||
return k[:keyPrefixLen+keyFolderLen]
|
||||
}
|
||||
|
||||
type blockListMapKey []byte
|
||||
|
||||
func (k defaultKeyer) GenerateBlockListMapKey(key, folder, hash, name []byte) (blockListMapKey, error) {
|
||||
folderID, err := k.folderIdx.ID(folder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = resize(key, keyPrefixLen+keyFolderLen+keyHashLen+len(name))
|
||||
key[0] = KeyTypeBlockListMap
|
||||
binary.BigEndian.PutUint32(key[keyPrefixLen:], folderID)
|
||||
copy(key[keyPrefixLen+keyFolderLen:], hash)
|
||||
copy(key[keyPrefixLen+keyFolderLen+keyHashLen:], name)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (k defaultKeyer) NameFromBlockListMapKey(key []byte) []byte {
|
||||
return key[keyPrefixLen+keyFolderLen+keyHashLen:]
|
||||
}
|
||||
|
||||
func (k blockListMapKey) WithoutHashAndName() []byte {
|
||||
return k[:keyPrefixLen+keyFolderLen]
|
||||
}
|
||||
|
||||
type needFileKey []byte
|
||||
|
||||
func (k needFileKey) WithoutName() []byte {
|
||||
|
||||
+38
-4
@@ -195,6 +195,7 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
|
||||
if ok && unchanged(f, ef) {
|
||||
continue
|
||||
}
|
||||
blocksHashSame := ok && bytes.Equal(ef.BlocksHash, f.BlocksHash)
|
||||
|
||||
if ok {
|
||||
if !ef.IsDirectory() && !ef.IsDeleted() && !ef.IsInvalid() {
|
||||
@@ -207,6 +208,15 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !blocksHashSame {
|
||||
keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, ef.BlocksHash, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = t.Delete(keyBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyBuf, err = db.keyer.GenerateSequenceKey(keyBuf, folder, ef.SequenceNo())
|
||||
@@ -260,6 +270,15 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !blocksHashSame {
|
||||
keyBuf, err := db.keyer.GenerateBlockListMapKey(keyBuf, folder, f.BlocksHash, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = t.Put(keyBuf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.Checkpoint(func() error {
|
||||
@@ -296,7 +315,7 @@ func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
}
|
||||
|
||||
// Remove all sequences related to the folder
|
||||
k1, err := db.keyer.GenerateSequenceKey(nil, folder, 0)
|
||||
k1, err := db.keyer.GenerateSequenceKey(k0, folder, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -305,7 +324,7 @@ func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
}
|
||||
|
||||
// Remove all items related to the given folder from the global bucket
|
||||
k2, err := db.keyer.GenerateGlobalVersionKey(nil, folder, nil)
|
||||
k2, err := db.keyer.GenerateGlobalVersionKey(k1, folder, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -314,7 +333,7 @@ func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
}
|
||||
|
||||
// Remove all needs related to the folder
|
||||
k3, err := db.keyer.GenerateNeedFileKey(nil, folder, nil)
|
||||
k3, err := db.keyer.GenerateNeedFileKey(k2, folder, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -323,7 +342,7 @@ func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
}
|
||||
|
||||
// Remove the blockmap of the folder
|
||||
k4, err := db.keyer.GenerateBlockMapKey(nil, folder, nil, nil)
|
||||
k4, err := db.keyer.GenerateBlockMapKey(k3, folder, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -331,6 +350,14 @@ func (db *Lowlevel) dropFolder(folder []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
k5, err := db.keyer.GenerateBlockListMapKey(k4, folder, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.deleteKeyPrefix(k5.WithoutHashAndName()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
@@ -383,6 +410,13 @@ func (db *Lowlevel) dropDeviceFolder(device, folder []byte, meta *metadataTracke
|
||||
if err := t.deleteKeyPrefix(key.WithoutHashAndName()); err != nil {
|
||||
return err
|
||||
}
|
||||
key2, err := db.keyer.GenerateBlockListMapKey(key, folder, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.deleteKeyPrefix(key2.WithoutHashAndName()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
+48
-6
@@ -22,9 +22,9 @@ import (
|
||||
// 6: v0.14.50
|
||||
// 7: v0.14.53
|
||||
// 8-9: v1.4.0
|
||||
// 10: v1.6.0
|
||||
// 10-11: v1.6.0
|
||||
const (
|
||||
dbVersion = 10
|
||||
dbVersion = 11
|
||||
dbMinSyncthingVersion = "v1.6.0"
|
||||
)
|
||||
|
||||
@@ -85,8 +85,9 @@ func (db *schemaUpdater) updateSchema() error {
|
||||
{5, db.updateSchemaTo5},
|
||||
{6, db.updateSchema5to6},
|
||||
{7, db.updateSchema6to7},
|
||||
{9, db.updateSchemato9},
|
||||
{10, db.updateSchemato10},
|
||||
{9, db.updateSchemaTo9},
|
||||
{10, db.updateSchemaTo10},
|
||||
{11, db.updateSchemaTo11},
|
||||
}
|
||||
|
||||
for _, m := range migrations {
|
||||
@@ -450,7 +451,7 @@ func (db *schemaUpdater) updateSchema6to7(_ int) error {
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
func (db *schemaUpdater) updateSchemato9(prev int) error {
|
||||
func (db *schemaUpdater) updateSchemaTo9(prev int) error {
|
||||
// Loads and rewrites all files with blocks, to deduplicate block lists.
|
||||
// Checks for missing or incorrect sequence entries and rewrites those.
|
||||
|
||||
@@ -499,7 +500,7 @@ func (db *schemaUpdater) updateSchemato9(prev int) error {
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
func (db *schemaUpdater) updateSchemato10(_ int) error {
|
||||
func (db *schemaUpdater) updateSchemaTo10(_ int) error {
|
||||
t, err := db.newReadWriteTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -568,3 +569,44 @@ func (db *schemaUpdater) updateSchemato10(_ int) error {
|
||||
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
func (db *schemaUpdater) updateSchemaTo11(_ int) error {
|
||||
// Populates block list map for every folder.
|
||||
|
||||
t, err := db.newReadWriteTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.close()
|
||||
|
||||
var dk []byte
|
||||
for _, folderStr := range db.ListFolders() {
|
||||
folder := []byte(folderStr)
|
||||
var putErr error
|
||||
err := t.withHave(folder, protocol.LocalDeviceID[:], nil, true, func(fi FileIntf) bool {
|
||||
f := fi.(FileInfoTruncated)
|
||||
if f.IsDirectory() || f.IsDeleted() || f.IsInvalid() || f.BlocksHash == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
name := []byte(f.FileName())
|
||||
dk, putErr = db.keyer.GenerateBlockListMapKey(dk, folder, f.BlocksHash, name)
|
||||
if putErr != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if putErr = t.Put(dk, nil); putErr != nil {
|
||||
return false
|
||||
}
|
||||
putErr = t.Checkpoint()
|
||||
return putErr == nil
|
||||
})
|
||||
if putErr != nil {
|
||||
return putErr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return t.Commit()
|
||||
}
|
||||
|
||||
@@ -365,6 +365,13 @@ func (s *Snapshot) RemoteNeedFolderFiles(device protocol.DeviceID, page, perpage
|
||||
return files
|
||||
}
|
||||
|
||||
func (s *Snapshot) WithBlocksHash(hash []byte, fn Iterator) {
|
||||
l.Debugf(`%s WithBlocksHash("%x")`, s.folder, hash)
|
||||
if err := s.t.withBlocksHash([]byte(s.folder), hash, nativeFileIterator(fn)); err != nil && !backend.IsClosed(err) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileSet) Sequence(device protocol.DeviceID) int64 {
|
||||
return s.meta.Sequence(device)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package db
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
@@ -304,6 +305,47 @@ func (t *readOnlyTransaction) withGlobal(folder, prefix []byte, truncate bool, f
|
||||
return dbi.Error()
|
||||
}
|
||||
|
||||
func (t *readOnlyTransaction) withBlocksHash(folder, hash []byte, iterator Iterator) error {
|
||||
key, err := t.keyer.GenerateBlockListMapKey(nil, folder, hash, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter, err := t.NewPrefixIterator(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
file := string(t.keyer.NameFromBlockListMapKey(iter.Key()))
|
||||
f, ok, err := t.getFile(folder, protocol.LocalDeviceID[:], []byte(osutil.NormalizedFilename(file)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
f.Name = osutil.NativeFilename(f.Name)
|
||||
|
||||
if !bytes.Equal(f.BlocksHash, hash) {
|
||||
l.Warnf("Mismatching block map list hashes: got %x expected %x", f.BlocksHash, hash)
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsDeleted() || f.IsInvalid() || f.IsDirectory() || f.IsSymlink() {
|
||||
l.Warnf("Found something of unexpected type in block list map: %s", f)
|
||||
continue
|
||||
}
|
||||
|
||||
if !iterator(f) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return iter.Error()
|
||||
}
|
||||
|
||||
func (t *readOnlyTransaction) availability(folder, file []byte) ([]protocol.DeviceID, error) {
|
||||
vl, err := t.getGlobalVersions(nil, folder, file)
|
||||
if backend.IsNotFound(err) {
|
||||
|
||||
Reference in New Issue
Block a user