mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: store vacuum and clean (#2293)
* Move all vacuum code into driver * Remove db from Store
This commit is contained in:
@ -56,8 +56,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver := sqlite.NewDriver(db.DBInstance)
|
driver := sqlite.NewDriver(db.DBInstance)
|
||||||
|
store := store.New(driver, profile)
|
||||||
store := store.New(db.DBInstance, driver, profile)
|
|
||||||
s, err := server.NewServer(ctx, profile, store)
|
s, err := server.NewServer(ctx, profile, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -51,8 +51,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver := sqlite.NewDriver(db.DBInstance)
|
driver := sqlite.NewDriver(db.DBInstance)
|
||||||
|
s := store.New(driver, profile)
|
||||||
s := store.New(db.DBInstance, driver, profile)
|
|
||||||
resources, err := s.ListResources(ctx, &store.FindResource{})
|
resources, err := s.ListResources(ctx, &store.FindResource{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to list resources, error: %+v\n", err)
|
fmt.Printf("failed to list resources, error: %+v\n", err)
|
||||||
|
@ -48,8 +48,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver := sqlite.NewDriver(db.DBInstance)
|
driver := sqlite.NewDriver(db.DBInstance)
|
||||||
|
store := store.New(driver, profile)
|
||||||
store := store.New(db.DBInstance, driver, profile)
|
|
||||||
if err := ExecuteSetup(ctx, store, hostUsername, hostPassword); err != nil {
|
if err := ExecuteSetup(ctx, store, hostUsername, hostPassword); err != nil {
|
||||||
fmt.Printf("failed to setup, error: %+v\n", err)
|
fmt.Printf("failed to setup, error: %+v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -162,7 +162,7 @@ func (s *Server) Shutdown(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close database connection
|
// Close database connection
|
||||||
if err := s.Store.GetDB().Close(); err != nil {
|
if err := s.Store.Close(); err != nil {
|
||||||
fmt.Printf("failed to close database, error: %v\n", err)
|
fmt.Printf("failed to close database, error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
|
Vacuum(ctx context.Context) error
|
||||||
|
BackupTo(ctx context.Context, filename string) error
|
||||||
|
Close() error
|
||||||
|
|
||||||
CreateActivity(ctx context.Context, create *Activity) (*Activity, error)
|
CreateActivity(ctx context.Context, create *Activity) (*Activity, error)
|
||||||
|
|
||||||
CreateResource(ctx context.Context, create *Resource) (*Resource, error)
|
CreateResource(ctx context.Context, create *Resource) (*Resource, error)
|
||||||
|
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Visibility is the type of a visibility.
|
// Visibility is the type of a visibility.
|
||||||
@ -107,35 +106,9 @@ func (s *Store) UpdateMemo(ctx context.Context, update *UpdateMemo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) DeleteMemo(ctx context.Context, delete *DeleteMemo) error {
|
func (s *Store) DeleteMemo(ctx context.Context, delete *DeleteMemo) error {
|
||||||
if err := s.driver.DeleteMemo(ctx, delete); err != nil {
|
return s.driver.DeleteMemo(ctx, delete)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.Vacuum(ctx); err != nil {
|
|
||||||
// Prevent linter warning.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FindMemosVisibilityList(ctx context.Context, memoIDs []int32) ([]Visibility, error) {
|
func (s *Store) FindMemosVisibilityList(ctx context.Context, memoIDs []int32) ([]Visibility, error) {
|
||||||
return s.driver.FindMemosVisibilityList(ctx, memoIDs)
|
return s.driver.FindMemosVisibilityList(ctx, memoIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumMemo(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
stmt := `
|
|
||||||
DELETE FROM
|
|
||||||
memo
|
|
||||||
WHERE
|
|
||||||
creator_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
user
|
|
||||||
)`
|
|
||||||
_, err := tx.ExecContext(ctx, stmt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MemoOrganizer struct {
|
type MemoOrganizer struct {
|
||||||
@ -32,28 +31,3 @@ func (s *Store) GetMemoOrganizer(ctx context.Context, find *FindMemoOrganizer) (
|
|||||||
func (s *Store) DeleteMemoOrganizer(ctx context.Context, delete *DeleteMemoOrganizer) error {
|
func (s *Store) DeleteMemoOrganizer(ctx context.Context, delete *DeleteMemoOrganizer) error {
|
||||||
return s.driver.DeleteMemoOrganizer(ctx, delete)
|
return s.driver.DeleteMemoOrganizer(ctx, delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumMemoOrganizer(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
stmt := `
|
|
||||||
DELETE FROM
|
|
||||||
memo_organizer
|
|
||||||
WHERE
|
|
||||||
memo_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
memo
|
|
||||||
)
|
|
||||||
OR user_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
user
|
|
||||||
)`
|
|
||||||
_, err := tx.ExecContext(ctx, stmt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MemoRelationType string
|
type MemoRelationType string
|
||||||
@ -54,13 +53,3 @@ func (s *Store) GetMemoRelation(ctx context.Context, find *FindMemoRelation) (*M
|
|||||||
func (s *Store) DeleteMemoRelation(ctx context.Context, delete *DeleteMemoRelation) error {
|
func (s *Store) DeleteMemoRelation(ctx context.Context, delete *DeleteMemoRelation) error {
|
||||||
return s.driver.DeleteMemoRelation(ctx, delete)
|
return s.driver.DeleteMemoRelation(ctx, delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumMemoRelations(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
if _, err := tx.ExecContext(ctx, `
|
|
||||||
DELETE FROM memo_relation
|
|
||||||
WHERE memo_id NOT IN (SELECT id FROM memo) OR related_memo_id NOT IN (SELECT id FROM memo)
|
|
||||||
`); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
@ -75,33 +74,5 @@ func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) DeleteResource(ctx context.Context, delete *DeleteResource) error {
|
func (s *Store) DeleteResource(ctx context.Context, delete *DeleteResource) error {
|
||||||
err := s.driver.DeleteResource(ctx, delete)
|
return s.driver.DeleteResource(ctx, delete)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.Vacuum(ctx); err != nil {
|
|
||||||
// Prevent linter warning.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func vacuumResource(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
stmt := `
|
|
||||||
DELETE FROM
|
|
||||||
resource
|
|
||||||
WHERE
|
|
||||||
creator_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
user
|
|
||||||
)`
|
|
||||||
_, err := tx.ExecContext(ctx, stmt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -234,6 +234,10 @@ func (d *Driver) DeleteMemo(ctx context.Context, delete *store.DeleteMemo) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := d.Vacuum(ctx); err != nil {
|
||||||
|
// Prevent linter warning.
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,3 +272,22 @@ func (d *Driver) FindMemosVisibilityList(ctx context.Context, memoIDs []int32) (
|
|||||||
|
|
||||||
return visibilityList, nil
|
return visibilityList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vacuumMemo(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
stmt := `
|
||||||
|
DELETE FROM
|
||||||
|
memo
|
||||||
|
WHERE
|
||||||
|
creator_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
user
|
||||||
|
)`
|
||||||
|
_, err := tx.ExecContext(ctx, stmt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -80,3 +81,28 @@ func (d *Driver) DeleteMemoOrganizer(ctx context.Context, delete *store.DeleteMe
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vacuumMemoOrganizer(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
stmt := `
|
||||||
|
DELETE FROM
|
||||||
|
memo_organizer
|
||||||
|
WHERE
|
||||||
|
memo_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
memo
|
||||||
|
)
|
||||||
|
OR user_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
user
|
||||||
|
)`
|
||||||
|
_, err := tx.ExecContext(ctx, stmt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
"github.com/usememos/memos/store"
|
||||||
@ -104,3 +105,13 @@ func (d *Driver) DeleteMemoRelation(ctx context.Context, delete *store.DeleteMem
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vacuumMemoRelations(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
if _, err := tx.ExecContext(ctx, `
|
||||||
|
DELETE FROM memo_relation
|
||||||
|
WHERE memo_id NOT IN (SELECT id FROM memo) OR related_memo_id NOT IN (SELECT id FROM memo)
|
||||||
|
`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -181,5 +181,29 @@ func (d *Driver) DeleteResource(ctx context.Context, delete *store.DeleteResourc
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := d.Vacuum(ctx); err != nil {
|
||||||
|
// Prevent linter warning.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func vacuumResource(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
stmt := `
|
||||||
|
DELETE FROM
|
||||||
|
resource
|
||||||
|
WHERE
|
||||||
|
creator_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
user
|
||||||
|
)`
|
||||||
|
_, err := tx.ExecContext(ctx, stmt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"modernc.org/sqlite"
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
"github.com/usememos/memos/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,3 +19,91 @@ func NewDriver(db *sql.DB) store.Driver {
|
|||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Driver) Vacuum(ctx context.Context) error {
|
||||||
|
tx, err := d.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
if err := vacuumImpl(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vacuum sqlite database file size after deleting resource.
|
||||||
|
if _, err := d.db.Exec("VACUUM"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func vacuumImpl(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
if err := vacuumMemo(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := vacuumResource(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := vacuumUserSetting(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := vacuumMemoOrganizer(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := vacuumMemoRelations(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := vacuumTag(ctx, tx); err != nil {
|
||||||
|
// Prevent revive warning.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) BackupTo(ctx context.Context, filename string) error {
|
||||||
|
conn, err := d.db.Conn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("fail to get conn %s", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
err = conn.Raw(func(driverConn any) error {
|
||||||
|
type backuper interface {
|
||||||
|
NewBackup(string) (*sqlite.Backup, error)
|
||||||
|
}
|
||||||
|
backupConn, ok := driverConn.(backuper)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("db connection is not a sqlite backuper")
|
||||||
|
}
|
||||||
|
|
||||||
|
bck, err := backupConn.NewBackup(filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("fail to create sqlite backup %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for more := true; more; {
|
||||||
|
more, err = bck.Step(-1)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("fail to execute sqlite backup %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bck.Finish()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("fail to backup %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) Close() error {
|
||||||
|
return d.db.Close()
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
"github.com/usememos/memos/store"
|
||||||
@ -73,3 +74,22 @@ func (d *Driver) DeleteTag(ctx context.Context, delete *store.DeleteTag) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vacuumTag(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
stmt := `
|
||||||
|
DELETE FROM
|
||||||
|
tag
|
||||||
|
WHERE
|
||||||
|
creator_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
user
|
||||||
|
)`
|
||||||
|
_, err := tx.ExecContext(ctx, stmt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -168,5 +168,11 @@ func (d *Driver) DeleteUser(ctx context.Context, delete *store.DeleteUser) error
|
|||||||
if _, err := result.RowsAffected(); err != nil {
|
if _, err := result.RowsAffected(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := d.Vacuum(ctx); err != nil {
|
||||||
|
// Prevent linter warning.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -153,3 +154,22 @@ func (d *Driver) ListUserSettingsV1(ctx context.Context, find *store.FindUserSet
|
|||||||
|
|
||||||
return userSettingList, nil
|
return userSettingList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
stmt := `
|
||||||
|
DELETE FROM
|
||||||
|
user_setting
|
||||||
|
WHERE
|
||||||
|
user_id NOT IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
user
|
||||||
|
)`
|
||||||
|
_, err := tx.ExecContext(ctx, stmt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,20 +2,14 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"modernc.org/sqlite"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/server/profile"
|
"github.com/usememos/memos/server/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store provides database access to all raw objects.
|
// Store provides database access to all raw objects.
|
||||||
type Store struct {
|
type Store struct {
|
||||||
Profile *profile.Profile
|
Profile *profile.Profile
|
||||||
db *sql.DB
|
|
||||||
driver Driver
|
driver Driver
|
||||||
systemSettingCache sync.Map // map[string]*SystemSetting
|
systemSettingCache sync.Map // map[string]*SystemSetting
|
||||||
userCache sync.Map // map[int]*User
|
userCache sync.Map // map[int]*User
|
||||||
@ -24,98 +18,21 @@ type Store struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new instance of Store.
|
// New creates a new instance of Store.
|
||||||
func New(db *sql.DB, driver Driver, profile *profile.Profile) *Store {
|
func New(driver Driver, profile *profile.Profile) *Store {
|
||||||
return &Store{
|
return &Store{
|
||||||
Profile: profile,
|
Profile: profile,
|
||||||
db: db,
|
|
||||||
driver: driver,
|
driver: driver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetDB() *sql.DB {
|
|
||||||
return s.db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) BackupTo(ctx context.Context, filename string) error {
|
func (s *Store) BackupTo(ctx context.Context, filename string) error {
|
||||||
conn, err := s.db.Conn(ctx)
|
return s.driver.BackupTo(ctx, filename)
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("fail to get conn %s", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
err = conn.Raw(func(driverConn any) error {
|
|
||||||
type backuper interface {
|
|
||||||
NewBackup(string) (*sqlite.Backup, error)
|
|
||||||
}
|
|
||||||
backupConn, ok := driverConn.(backuper)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("db connection is not a sqlite backuper")
|
|
||||||
}
|
|
||||||
|
|
||||||
bck, err := backupConn.NewBackup(filename)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("fail to create sqlite backup %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for more := true; more; {
|
|
||||||
more, err = bck.Step(-1)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("fail to execute sqlite backup %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bck.Finish()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("fail to backup %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Vacuum(ctx context.Context) error {
|
func (s *Store) Vacuum(ctx context.Context) error {
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
return s.driver.Vacuum(ctx)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tx.Rollback()
|
|
||||||
|
|
||||||
if err := s.vacuumImpl(ctx, tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vacuum sqlite database file size after deleting resource.
|
|
||||||
if _, err := s.db.Exec("VACUUM"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Store) vacuumImpl(ctx context.Context, tx *sql.Tx) error {
|
func (s *Store) Close() error {
|
||||||
if err := vacuumMemo(ctx, tx); err != nil {
|
return s.driver.Close()
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := vacuumResource(ctx, tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := vacuumUserSetting(ctx, tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := vacuumMemoOrganizer(ctx, tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := vacuumMemoRelations(ctx, tx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := vacuumTag(ctx, tx); err != nil {
|
|
||||||
// Prevent revive warning.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
20
store/tag.go
20
store/tag.go
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
@ -30,22 +29,3 @@ func (s *Store) ListTags(ctx context.Context, find *FindTag) ([]*Tag, error) {
|
|||||||
func (s *Store) DeleteTag(ctx context.Context, delete *DeleteTag) error {
|
func (s *Store) DeleteTag(ctx context.Context, delete *DeleteTag) error {
|
||||||
return s.driver.DeleteTag(ctx, delete)
|
return s.driver.DeleteTag(ctx, delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumTag(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
stmt := `
|
|
||||||
DELETE FROM
|
|
||||||
tag
|
|
||||||
WHERE
|
|
||||||
creator_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
user
|
|
||||||
)`
|
|
||||||
_, err := tx.ExecContext(ctx, stmt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -130,10 +130,6 @@ func (s *Store) DeleteUser(ctx context.Context, delete *DeleteUser) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Vacuum(ctx); err != nil {
|
|
||||||
// Prevent linter warning.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.userCache.Delete(delete.ID)
|
s.userCache.Delete(delete.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
storepb "github.com/usememos/memos/proto/gen/store"
|
storepb "github.com/usememos/memos/proto/gen/store"
|
||||||
)
|
)
|
||||||
@ -125,22 +124,3 @@ func (s *Store) GetUserAccessTokens(ctx context.Context, userID int32) ([]*store
|
|||||||
accessTokensUserSetting := userSetting.GetAccessTokens()
|
accessTokensUserSetting := userSetting.GetAccessTokens()
|
||||||
return accessTokensUserSetting.AccessTokens, nil
|
return accessTokensUserSetting.AccessTokens, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
|
|
||||||
stmt := `
|
|
||||||
DELETE FROM
|
|
||||||
user_setting
|
|
||||||
WHERE
|
|
||||||
user_id NOT IN (
|
|
||||||
SELECT
|
|
||||||
id
|
|
||||||
FROM
|
|
||||||
user
|
|
||||||
)`
|
|
||||||
_, err := tx.ExecContext(ctx, stmt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -41,8 +41,7 @@ func NewTestingServer(ctx context.Context, t *testing.T) (*TestingServer, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver := sqlite.NewDriver(db.DBInstance)
|
driver := sqlite.NewDriver(db.DBInstance)
|
||||||
|
store := store.New(driver, profile)
|
||||||
store := store.New(db.DBInstance, driver, profile)
|
|
||||||
server, err := server.NewServer(ctx, profile, store)
|
server, err := server.NewServer(ctx, profile, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create server")
|
return nil, errors.Wrap(err, "failed to create server")
|
||||||
|
@ -25,7 +25,6 @@ func NewTestingStore(ctx context.Context, t *testing.T) *store.Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver := sqlite.NewDriver(db.DBInstance)
|
driver := sqlite.NewDriver(db.DBInstance)
|
||||||
|
store := store.New(driver, profile)
|
||||||
store := store.New(db.DBInstance, driver, profile)
|
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user