Optimize fullfsync.

This commit is contained in:
Nuno Cruces
2025-09-30 12:54:18 +01:00
parent 23aad5f62f
commit 14c1e490b4
6 changed files with 62 additions and 38 deletions

View File

@@ -94,6 +94,10 @@ const (
OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
OPEN_WAL OpenFlag = 0x00080000 /* VFS only */
OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
_FLAG_ATOMIC OpenFlag = 0x10000000
_FLAG_KEEP_WAL OpenFlag = 0x20000000
_FLAG_PSOW OpenFlag = 0x40000000
_FLAG_SYNC_DIR OpenFlag = 0x80000000
)
// AccessFlag is a flag for the [VFS] Access method.

View File

@@ -51,7 +51,7 @@ func (vfsOS) Delete(path string, syncDir bool) error {
return _OK
}
defer f.Close()
err = osSync(f, false, false)
err = osSync(f, 0, SYNC_FULL)
if err != nil {
return _IOERR_DIR_FSYNC
}
@@ -132,13 +132,15 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
file := vfsFile{
File: f,
psow: true,
atomic: osBatchAtomic(f),
readOnly: flags&OPEN_READONLY != 0,
syncDir: isUnix && isCreate && isJournl,
delete: !isUnix && flags&OPEN_DELETEONCLOSE != 0,
flags: flags | _FLAG_PSOW,
shm: NewSharedMemory(name.String()+"-shm", flags),
}
if osBatchAtomic(f) {
file.flags |= _FLAG_ATOMIC
}
if isUnix && isCreate && isJournl {
file.flags |= _FLAG_SYNC_DIR
}
return &file, flags, nil
}
@@ -146,12 +148,7 @@ type vfsFile struct {
*os.File
shm SharedMemory
lock LockLevel
readOnly bool
keepWAL bool
syncDir bool
atomic bool
delete bool
psow bool
flags OpenFlag
}
var (
@@ -164,7 +161,7 @@ var (
)
func (f *vfsFile) Close() error {
if f.delete {
if !isUnix && f.flags&OPEN_DELETEONCLOSE != 0 {
defer os.Remove(f.Name())
}
if f.shm != nil {
@@ -183,21 +180,18 @@ func (f *vfsFile) WriteAt(p []byte, off int64) (n int, err error) {
}
func (f *vfsFile) Sync(flags SyncFlag) error {
dataonly := (flags & SYNC_DATAONLY) != 0
fullsync := (flags & 0x0f) == SYNC_FULL
err := osSync(f.File, fullsync, dataonly)
err := osSync(f.File, f.flags, flags)
if err != nil {
return err
}
if isUnix && f.syncDir {
f.syncDir = false
if isUnix && f.flags&_FLAG_SYNC_DIR != 0 {
f.flags ^= _FLAG_SYNC_DIR
d, err := os.Open(filepath.Dir(f.File.Name()))
if err != nil {
return nil
}
defer d.Close()
err = osSync(d, false, false)
err = osSync(f.File, f.flags, flags)
if err != nil {
return _IOERR_DIR_FSYNC
}
@@ -215,10 +209,10 @@ func (f *vfsFile) SectorSize() int {
func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
ret := IOCAP_SUBPAGE_READ
if f.atomic {
if f.flags&_FLAG_ATOMIC != 0 {
ret |= IOCAP_BATCH_ATOMIC
}
if f.psow {
if f.flags&_FLAG_PSOW != 0 {
ret |= IOCAP_POWERSAFE_OVERWRITE
}
if runtime.GOOS == "windows" {
@@ -250,7 +244,19 @@ func (f *vfsFile) HasMoved() (bool, error) {
}
func (f *vfsFile) LockState() LockLevel { return f.lock }
func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
func (f *vfsFile) PersistWAL() bool { return f.keepWAL }
func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
func (f *vfsFile) SetPersistWAL(keepWAL bool) { f.keepWAL = keepWAL }
func (f *vfsFile) PowersafeOverwrite() bool { return f.flags&_FLAG_PSOW != 0 }
func (f *vfsFile) PersistWAL() bool { return f.flags&_FLAG_KEEP_WAL != 0 }
func (f *vfsFile) SetPowersafeOverwrite(psow bool) {
f.flags &^= _FLAG_PSOW
if psow {
f.flags |= _FLAG_PSOW
}
}
func (f *vfsFile) SetPersistWAL(keepWAL bool) {
f.flags &^= _FLAG_KEEP_WAL
if keepWAL {
f.flags |= _FLAG_KEEP_WAL
}
}

View File

@@ -41,7 +41,7 @@ func (f *vfsFile) Lock(lock LockLevel) error {
}
// Do not allow any kind of write-lock on a read-only database.
if f.readOnly && lock >= LOCK_RESERVED {
if lock >= LOCK_RESERVED && f.flags&OPEN_READONLY != 0 {
return _IOERR_LOCK
}

View File

@@ -23,12 +23,26 @@ type flocktimeout_t struct {
timeout unix.Timespec
}
func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
if fullsync {
return file.Sync()
func osSync(file *os.File, open OpenFlag, sync SyncFlag) error {
var cmd int
if sync&SYNC_FULL == SYNC_FULL {
// For rollback journals all we really need is a barrier.
if open&OPEN_MAIN_JOURNAL != 0 {
cmd = unix.F_BARRIERFSYNC
} else {
cmd = unix.F_FULLFSYNC
}
}
fd := file.Fd()
for {
err := unix.Fsync(int(file.Fd()))
err := error(unix.ENOTSUP)
if cmd != 0 {
_, err = unix.FcntlInt(fd, cmd, 0)
}
if err == unix.ENOTSUP {
err = unix.Fsync(int(fd))
}
if err != unix.EINTR {
return err
}

View File

@@ -10,7 +10,7 @@ import (
"golang.org/x/sys/unix"
)
func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
func osSync(file *os.File, _ OpenFlag, _ SyncFlag) error {
// SQLite trusts Linux's fdatasync for all fsync's.
for {
err := unix.Fdatasync(int(file.Fd()))

View File

@@ -4,6 +4,6 @@ package vfs
import "os"
func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
func osSync(file *os.File, _ OpenFlag, _ SyncFlag) error {
return file.Sync()
}