Fix bbolt recover

This commit is contained in:
世界
2026-08-30 17:41:46 +08:00
parent 82098a8760
commit 080f1c56aa
3 changed files with 75 additions and 28 deletions
+72 -25
View File
@@ -53,7 +53,7 @@ type CacheFile struct {
rdrcTimeout time.Duration
optimisticTimeout time.Duration
DB *bbolt.DB
resetAccess sync.Mutex
dbAccess sync.RWMutex
saveMetadataAccess sync.Mutex
saveMetadata *adapter.FakeIPMetadata
saveMetadataTimer *time.Timer
@@ -180,19 +180,29 @@ func (c *CacheFile) start() error {
var db *bbolt.DB
for range 10 {
db, err = bbolt.Open(c.path, fileMode, &options)
if err != nil {
if errors.Is(err, bboltErrors.ErrTimeout) {
continue
}
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
rmErr := filemanager.Remove(c.ctx, c.path)
if rmErr != nil {
return err
}
}
time.Sleep(100 * time.Millisecond)
continue
}
err = checkDatabase(db)
if err == nil {
break
}
if errors.Is(err, bboltErrors.ErrTimeout) {
continue
c.logger.Error("database corrupted: ", err, ": resetting")
db.Close()
rmErr := filemanager.Remove(c.ctx, c.path)
if rmErr != nil {
return err
}
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
rmErr := filemanager.Remove(c.ctx, c.path)
if rmErr != nil {
return err
}
}
time.Sleep(100 * time.Millisecond)
}
if err != nil {
return err
@@ -230,46 +240,83 @@ func (c *CacheFile) start() error {
}
func (c *CacheFile) Close() error {
if c.DB == nil {
c.dbAccess.RLock()
db := c.DB
c.dbAccess.RUnlock()
if db == nil {
return nil
}
return c.DB.Close()
return db.Close()
}
func checkDatabase(db *bbolt.DB) error {
return db.View(func(tx *bbolt.Tx) error {
var checkErr error
for txErr := range tx.Check() {
if checkErr == nil {
checkErr = txErr
}
}
return checkErr
})
}
func (c *CacheFile) database() *bbolt.DB {
c.dbAccess.RLock()
defer c.dbAccess.RUnlock()
return c.DB
}
func (c *CacheFile) view(fn func(tx *bbolt.Tx) error) (err error) {
db := c.database()
defer func() {
if r := recover(); r != nil {
c.resetDB()
r := recover()
if r != nil {
c.resetDB(db, r)
err = E.New("database corrupted: ", r)
}
}()
return c.DB.View(fn)
return db.View(fn)
}
func (c *CacheFile) batch(fn func(tx *bbolt.Tx) error) (err error) {
db := c.database()
defer func() {
if r := recover(); r != nil {
c.resetDB()
r := recover()
if r != nil {
c.resetDB(db, r)
err = E.New("database corrupted: ", r)
}
}()
return c.DB.Batch(fn)
err = db.Batch(fn)
var panicErr bbolt.PanickedError
if errors.As(err, &panicErr) {
c.resetDB(db, panicErr.Reason)
return E.New("database corrupted: ", panicErr.Reason)
}
return err
}
func (c *CacheFile) update(fn func(tx *bbolt.Tx) error) (err error) {
db := c.database()
defer func() {
if r := recover(); r != nil {
c.resetDB()
r := recover()
if r != nil {
c.resetDB(db, r)
err = E.New("database corrupted: ", r)
}
}()
return c.DB.Update(fn)
return db.Update(fn)
}
func (c *CacheFile) resetDB() {
c.resetAccess.Lock()
defer c.resetAccess.Unlock()
c.DB.Close()
func (c *CacheFile) resetDB(failedDB *bbolt.DB, reason any) {
c.dbAccess.Lock()
defer c.dbAccess.Unlock()
if c.DB != failedDB {
return
}
c.logger.Error("database corrupted: ", reason, ": resetting")
failedDB.Close()
filemanager.Remove(c.ctx, c.path)
db, err := bbolt.Open(c.path, 0o666, &bbolt.Options{Timeout: time.Second})
if err == nil {
+1 -1
View File
@@ -34,7 +34,7 @@ require (
github.com/oschwald/maxminddb-golang v1.13.1
github.com/pkg/sftp v1.13.10
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
github.com/sagernet/bbolt v0.0.0-20260821040940-d7518b45c52b
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c
github.com/sagernet/cors v1.2.1
github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b
github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b
+2 -2
View File
@@ -238,8 +238,8 @@ github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
github.com/sagernet/bbolt v0.0.0-20260821040940-d7518b45c52b h1:gjt/KuFojUO9PXzkWCpkIx/3Kd1VyJ/iR2PwS7gx8js=
github.com/sagernet/bbolt v0.0.0-20260821040940-d7518b45c52b/go.mod h1:7/qFPFH5fqK5XQeY8EA1Qpaly08Av77p38Zt50ieQww=
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c h1:JdEDkgiwsDxiZb4+dQYgVOU/lND+jZxFmN5y/q49EXk=
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c/go.mod h1:7/qFPFH5fqK5XQeY8EA1Qpaly08Av77p38Zt50ieQww=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b h1:SfX10lRxbq9ixHm4OZ4/7q+TSpK4Gh0lEVrPaIAB/K0=