feat: chrome multi profiles, sync mutex for sqlite db access
This commit is contained in:
parent
2ca7c6f72b
commit
7532d59016
@ -127,10 +127,38 @@ type Chrome struct {
|
||||
*ChromeConfig
|
||||
}
|
||||
|
||||
func (ch *Chrome) Init(ctx *modules.Context, p *profiles.Profile) error {
|
||||
// if called without profile setup default profile
|
||||
if p == nil {
|
||||
prof, err := ProfileManager.GetProfileByID(BrowserName, ch.Profile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bookmarkDir, err := prof.AbsolutePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch.BkDir = bookmarkDir
|
||||
return ch.init(ctx)
|
||||
}
|
||||
|
||||
ch.ChromeConfig = NewChromeConfig()
|
||||
ch.Profile = p.Id
|
||||
|
||||
if bookmarkDir, err := p.AbsolutePath(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
ch.BkDir = bookmarkDir
|
||||
}
|
||||
|
||||
return ch.init(ctx)
|
||||
}
|
||||
|
||||
// Init() is the first method called after a browser instance is created
|
||||
// and registered.
|
||||
// Return ok, error
|
||||
func (ch *Chrome) Init(_ *modules.Context) error {
|
||||
func (ch *Chrome) init(_ *modules.Context) error {
|
||||
log.Infof("initializing <%s>", ch.Name)
|
||||
return ch.setupWatchers()
|
||||
}
|
||||
@ -433,7 +461,8 @@ func (ch *Chrome) Run() {
|
||||
|
||||
// If the cache is empty just copy buffer to cache
|
||||
// until local db is already populated and preloaded
|
||||
//debugPrint("%d", BufferDB.Count())
|
||||
// debugPrint("%d", BufferDB.Count())
|
||||
log.Debugf("checking if db is empty")
|
||||
if empty, err := database.Cache.DB.IsEmpty(); empty {
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@ -444,12 +473,16 @@ func (ch *Chrome) Run() {
|
||||
|
||||
log.Debugf("syncing <%s> to disk", database.Cache.DB.Name)
|
||||
} else {
|
||||
log.Debug("syncing to db")
|
||||
ch.BufferDB.SyncTo(database.Cache.DB)
|
||||
}
|
||||
|
||||
go database.Cache.DB.SyncToDisk(database.GetDBFullPath())
|
||||
go func(){
|
||||
if err = database.Cache.DB.SyncToDisk(database.GetDBFullPath()); err != nil {
|
||||
log.Critical(err)
|
||||
}
|
||||
}()
|
||||
ch.LastWatchRunTime = time.Since(startRun)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -474,7 +507,7 @@ func init() {
|
||||
// interface guards
|
||||
|
||||
var _ modules.BrowserModule = (*Chrome)(nil)
|
||||
var _ modules.Initializer = (*Chrome)(nil)
|
||||
var _ modules.ProfileInitializer = (*Chrome)(nil)
|
||||
var _ modules.Loader = (*Chrome)(nil)
|
||||
var _ watch.WatchRunner = (*Chrome)(nil)
|
||||
var _ hooks.HookRunner = (*Chrome)(nil)
|
||||
|
@ -110,6 +110,7 @@ func (chrome *Chrome) WatchAllProfiles() bool {
|
||||
}
|
||||
|
||||
|
||||
// chrome uses ID to identify the profile path
|
||||
func (cpm *ChromeProfileManager) GetProfileByID (flavour string, id string) (*profiles.Profile, error) {
|
||||
profiles, err := cpm.GetProfiles(flavour)
|
||||
if err != nil {
|
||||
|
@ -23,8 +23,6 @@
|
||||
package firefox
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"git.blob42.xyz/gosuki/gosuki/internal/config"
|
||||
"git.blob42.xyz/gosuki/gosuki/internal/database"
|
||||
"git.blob42.xyz/gosuki/gosuki/pkg/browsers/mozilla"
|
||||
@ -156,15 +154,15 @@ func init() {
|
||||
// log.Debugf("%p", FFConfig)
|
||||
|
||||
// An example of running custom code when config is ready
|
||||
config.RegisterConfReadyHooks(func(c *cli.Context) error{
|
||||
// log.Debugf("%#v", config.GetAll().Dump())
|
||||
|
||||
|
||||
if userConf := config.GetModule(BrowserName); userConf != nil {
|
||||
watchAll, _ := userConf.Get("WatchAllProfiles")
|
||||
log.Debugf("WATCH_ALL: %v", watchAll)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
// config.RegisterConfReadyHooks(func(c *cli.Context) error{
|
||||
// // log.Debugf("%#v", config.GetAll().Dump())
|
||||
//
|
||||
//
|
||||
// if userConf := config.GetModule(BrowserName); userConf != nil {
|
||||
// watchAll, _ := userConf.Get("WatchAllProfiles")
|
||||
// log.Debugf("WATCH_ALL: %v", watchAll)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// })
|
||||
}
|
||||
|
@ -308,8 +308,9 @@ func (f *Firefox) Init(ctx *modules.Context, p *profiles.Profile) error {
|
||||
return f.init(ctx)
|
||||
}
|
||||
|
||||
//TEST: try multiple profiles at same time
|
||||
// use a new config for this profile
|
||||
// f.FirefoxConfig = NewFirefoxConfig()
|
||||
f.FirefoxConfig = NewFirefoxConfig()
|
||||
f.Profile = p.Name
|
||||
|
||||
|
||||
@ -419,7 +420,8 @@ func (f *Firefox) Load() error {
|
||||
f.BufferDB.SyncTo(database.Cache.DB)
|
||||
}
|
||||
|
||||
database.Cache.DB.SyncToDisk(database.GetDBFullPath())
|
||||
//TODO: send as goroutine ?
|
||||
go database.Cache.DB.SyncToDisk(database.GetDBFullPath())
|
||||
|
||||
//DEBUG:
|
||||
// tree.PrintTree(f.NodeTree)
|
||||
@ -463,7 +465,12 @@ func (ff *Firefox) Run() {
|
||||
|
||||
database.SyncURLIndexToBuffer(ff.URLIndexList, ff.URLIndex, ff.BufferDB)
|
||||
ff.BufferDB.SyncTo(database.Cache.DB)
|
||||
database.Cache.DB.SyncToDisk(database.GetDBFullPath())
|
||||
go func(){
|
||||
err = database.Cache.DB.SyncToDisk(database.GetDBFullPath()); if err != nil {
|
||||
log.Critical(err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
//TODO!: is LastWatchRunTime alone enough ?
|
||||
ff.LastWatchRunTime = time.Since(startRun)
|
||||
|
@ -151,14 +151,10 @@ func startDaemon(c *cli.Context) error {
|
||||
log.Criticalf("TODO: module <%s> is not a BrowserModule", mod.ID)
|
||||
}
|
||||
|
||||
if c.Bool("watch-all") {
|
||||
panic("TODO: watch all profiles")
|
||||
}
|
||||
|
||||
// call runModule for each profile
|
||||
bpm, ok := browser.(profiles.ProfileManager)
|
||||
if ok {
|
||||
if bpm.WatchAllProfiles() {
|
||||
if c.Bool("watch-all") || bpm.WatchAllProfiles() {
|
||||
falvours := bpm.ListFlavours()
|
||||
for _, f := range falvours {
|
||||
profs, err := bpm.GetProfiles(f.Name)
|
||||
|
@ -95,6 +95,7 @@ var detectInstalledCmd = &cli.Command{
|
||||
Usage: "detect installed browsers",
|
||||
Action: func(_ *cli.Context) error {
|
||||
mods := modules.GetModules()
|
||||
fmt.Printf("installed browsers:\n\n")
|
||||
for _, mod := range mods {
|
||||
browser, isBrowser := mod.ModInfo().New().(modules.BrowserModule)
|
||||
if !isBrowser {
|
||||
@ -109,9 +110,6 @@ var detectInstalledCmd = &cli.Command{
|
||||
}
|
||||
|
||||
flavours := pm.ListFlavours()
|
||||
if len(flavours) > 0 {
|
||||
fmt.Printf("Installed browsers:\n\n")
|
||||
}
|
||||
for _, f := range flavours {
|
||||
log.Debugf("found flavour <%s> for <%s>", f.Name, mod.ModInfo().ID)
|
||||
if dir, err := utils.ExpandPath(f.BaseDir); err != nil {
|
||||
|
@ -39,8 +39,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
//TODO!: document this
|
||||
_sql3conns []*sqlite3.SQLiteConn // Only used for backup hook
|
||||
backupHookRegistered bool // set to true once the backup hook is registered
|
||||
|
||||
DefaultDBPath = "./"
|
||||
)
|
||||
@ -236,7 +236,6 @@ func NewDB(name string, dbPath string, dbFormat string, opts ...DsnOptions) *DB
|
||||
|
||||
}
|
||||
|
||||
// TODO: Should check if DB is locked
|
||||
// We should export Open() in its own method and wrap
|
||||
// with interface so we can mock it and test the lock status in Init()
|
||||
// Initialize a sqlite database with Gosuki Schema if not already done
|
||||
@ -252,16 +251,11 @@ func (db *DB) Init() (*DB, error) {
|
||||
// Detect if database file is locked
|
||||
if db.Type == DBTypeRegularFile {
|
||||
|
||||
locked, err := db.Locked()
|
||||
|
||||
if err != nil {
|
||||
if locked, err := db.Locked(); err != nil {
|
||||
return nil, DBError{DBName: db.Name, Err: err}
|
||||
}
|
||||
|
||||
if locked {
|
||||
} else if locked {
|
||||
return nil, ErrVfsLocked
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Open database
|
||||
@ -408,14 +402,16 @@ func flushSqliteCon(con *sqlx.DB) {
|
||||
|
||||
func registerSqliteHooks() {
|
||||
// sqlite backup hook
|
||||
log.Debugf("backup_hook: registering driver %s", DriverBackupMode)
|
||||
// log.Debugf("backup_hook: registering driver %s", DriverBackupMode)
|
||||
// Register the hook
|
||||
sql.Register(DriverBackupMode,
|
||||
&sqlite3.SQLiteDriver{
|
||||
//TODO!: document why ?
|
||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||
//log.Debugf("[ConnectHook] registering new connection")
|
||||
_sql3conns = append(_sql3conns, conn)
|
||||
//log.Debugf("%v", _sql3conns)
|
||||
// log.Debugf("[ConnectHook] registered new connection")
|
||||
log.Debugf("%v", _sql3conns)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
@ -23,13 +23,17 @@ package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
sqlite3 "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// For ever row in `src` try to insert it into `dst`.
|
||||
// If if fails then try to update it. It means `src` is synced to `dst`
|
||||
var mu sync.Mutex
|
||||
|
||||
// Manual UPSERT:
|
||||
// For every row in `src` try to insert it into `dst`. if if fails then try to
|
||||
// update it. It means `src` is synced to `dst`
|
||||
func (src *DB) SyncTo(dst *DB) {
|
||||
var sqlite3Err sqlite3.Error
|
||||
var existingUrls []*SBookmark
|
||||
@ -69,7 +73,7 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
)
|
||||
defer func() {
|
||||
err := tryInsertDstRow.Close()
|
||||
err = tryInsertDstRow.Close()
|
||||
if err != nil {
|
||||
log.Critical(err)
|
||||
}
|
||||
@ -87,7 +91,7 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
)
|
||||
|
||||
defer func(){
|
||||
err := updateDstRow.Close()
|
||||
err = updateDstRow.Close()
|
||||
if err != nil {
|
||||
log.Critical()
|
||||
}
|
||||
@ -102,13 +106,13 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
// Lock destination db
|
||||
log.Debugf("starting transaction")
|
||||
dstTx, err := dst.Handle.Begin()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
|
||||
// Start syncing all entries from source table
|
||||
log.Debugf("scanning entries in source table")
|
||||
for srcTable.Next() {
|
||||
@ -149,7 +153,7 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
}
|
||||
|
||||
// Start a new transaction to update the existing urls
|
||||
dstTx, err = dst.Handle.Begin() // Lock dst db
|
||||
dstTx, err = dst.Handle.Begin()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
@ -196,6 +200,7 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
if err != nil {
|
||||
log.Errorf("%s: %s", err, scan.URL)
|
||||
}
|
||||
log.Debugf("synced %s to %s", scan.URL, dst.Name)
|
||||
|
||||
}
|
||||
|
||||
@ -213,8 +218,13 @@ func (src *DB) SyncTo(dst *DB) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO!: add concurrency
|
||||
// Multiple threads(goroutines) are trying to sync together when running
|
||||
// with watch all. Use sync.Mutex !!
|
||||
func (src *DB) SyncToDisk(dbpath string) error {
|
||||
log.Debugf("Syncing <%s> to <%s>", src.Name, dbpath)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -247,7 +257,12 @@ func (src *DB) SyncToDisk(dbpath string) error {
|
||||
}
|
||||
|
||||
if len(_sql3conns) < 2 {
|
||||
return fmt.Errorf("not enough sql connections for backup")
|
||||
return fmt.Errorf("not enough sql connections for backup call")
|
||||
}
|
||||
|
||||
if _sql3conns[0] == nil {
|
||||
log.Critical("nil sql connection")
|
||||
return fmt.Errorf("nil sql connection")
|
||||
}
|
||||
|
||||
bkp, err := _sql3conns[1].Backup("main", _sql3conns[0], "main")
|
||||
|
Loading…
Reference in New Issue
Block a user