mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
Fix bbolt recover
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
Reference in New Issue
Block a user