mirror of https://github.com/portainer/portainer
feat(boltdb): attempt to compact using a read-only database BE-12287 (#1268)
parent
74b1dd04d1
commit
fd6d74602c
|
@ -136,10 +136,8 @@ func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
|
||||||
func (connection *DbConnection) Open() error {
|
func (connection *DbConnection) Open() error {
|
||||||
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
||||||
|
|
||||||
// Now we open the db
|
|
||||||
databasePath := connection.GetDatabaseFilePath()
|
databasePath := connection.GetDatabaseFilePath()
|
||||||
|
db, err := bolt.Open(databasePath, 0600, connection.boltOptions(connection.Compact))
|
||||||
db, err := bolt.Open(databasePath, 0600, connection.boltOptions())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -152,6 +150,15 @@ func (connection *DbConnection) Open() error {
|
||||||
log.Info().Msg("compacting database")
|
log.Info().Msg("compacting database")
|
||||||
if err := connection.compact(); err != nil {
|
if err := connection.compact(); err != nil {
|
||||||
log.Error().Err(err).Msg("failed to compact database")
|
log.Error().Err(err).Msg("failed to compact database")
|
||||||
|
|
||||||
|
// Close the read-only database and re-open in read-write mode
|
||||||
|
if err := connection.Close(); err != nil {
|
||||||
|
log.Warn().Err(err).Msg("failure to close the database after failed compaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.Compact = false
|
||||||
|
|
||||||
|
return connection.Open()
|
||||||
} else {
|
} else {
|
||||||
log.Info().Msg("database compaction completed")
|
log.Info().Msg("database compaction completed")
|
||||||
}
|
}
|
||||||
|
@ -424,9 +431,14 @@ func (connection *DbConnection) RestoreMetadata(s map[string]any) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// compact attempts to compact the database and replace it iff it succeeds
|
// compact attempts to compact the database and replace it iff it succeeds
|
||||||
func (connection *DbConnection) compact() error {
|
func (connection *DbConnection) compact() (err error) {
|
||||||
compactedPath := connection.GetDatabaseFilePath() + compactedSuffix
|
compactedPath := connection.GetDatabaseFilePath() + compactedSuffix
|
||||||
compactedDB, err := bolt.Open(compactedPath, 0o600, connection.boltOptions())
|
|
||||||
|
if err := os.Remove(compactedPath); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return fmt.Errorf("failure to remove an existing compacted database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compactedDB, err := bolt.Open(compactedPath, 0o600, connection.boltOptions(false))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failure to create the compacted database: %w", err)
|
return fmt.Errorf("failure to create the compacted database: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -453,11 +465,12 @@ func (connection *DbConnection) compact() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (connection *DbConnection) boltOptions() *bolt.Options {
|
func (connection *DbConnection) boltOptions(readOnly bool) *bolt.Options {
|
||||||
return &bolt.Options{
|
return &bolt.Options{
|
||||||
Timeout: 1 * time.Second,
|
Timeout: 1 * time.Second,
|
||||||
InitialMmapSize: connection.InitialMmapSize,
|
InitialMmapSize: connection.InitialMmapSize,
|
||||||
FreelistType: bolt.FreelistMapType,
|
FreelistType: bolt.FreelistMapType,
|
||||||
NoFreelistSync: true,
|
NoFreelistSync: true,
|
||||||
|
ReadOnly: readOnly,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/portainer/portainer/api/filesystem"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
|
@ -123,10 +125,7 @@ func Test_NeedsEncryptionMigration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDBCompaction(t *testing.T) {
|
func TestDBCompaction(t *testing.T) {
|
||||||
db := &DbConnection{
|
db := &DbConnection{Path: t.TempDir()}
|
||||||
Path: t.TempDir(),
|
|
||||||
Compact: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := db.Open()
|
err := db.Open()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -147,6 +146,7 @@ func TestDBCompaction(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Reopen the DB to trigger compaction
|
// Reopen the DB to trigger compaction
|
||||||
|
db.Compact = true
|
||||||
err = db.Open()
|
err = db.Open()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -168,10 +168,14 @@ func TestDBCompaction(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Failures
|
// Failures
|
||||||
|
compactedPath := db.GetDatabaseFilePath() + compactedSuffix
|
||||||
err = os.Mkdir(db.GetDatabaseFilePath()+compactedSuffix, 0o755)
|
err = os.Mkdir(compactedPath, 0o755)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f, err := os.Create(filesystem.JoinPaths(compactedPath, "somefile"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, f.Close())
|
||||||
|
|
||||||
err = db.Open()
|
err = db.Open()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue