From d19f29c46753da7fdeae26076fd25c19afc9cfb1 Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 10 Oct 2022 17:30:48 -0700 Subject: [PATCH] single-thread DB access. send selectedline updates for run/comment commands --- pkg/cmdrunner/cmdrunner.go | 33 +++++++++++++++++-- pkg/sstore/dbops.go | 66 ++++++++++++++++++-------------------- pkg/sstore/txwrap.go | 7 ++++ 3 files changed, 68 insertions(+), 38 deletions(-) diff --git a/pkg/cmdrunner/cmdrunner.go b/pkg/cmdrunner/cmdrunner.go index 2aaf5184..3aefe3c0 100644 --- a/pkg/cmdrunner/cmdrunner.go +++ b/pkg/cmdrunner/cmdrunner.go @@ -217,7 +217,7 @@ func resolveNonNegInt(arg string, def int) (int, error) { } func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { - ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected) + ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected) if err != nil { return nil, fmt.Errorf("/run error: %w", err) } @@ -252,7 +252,26 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U if err != nil { return nil, err } - update := sstore.ModelUpdate{Line: rtnLine, Cmd: cmd, Interactive: pk.Interactive} + sw, err := sstore.GetScreenWindowByIds(ctx, ids.SessionId, ids.ScreenId, ids.WindowId) + if err != nil { + // ignore error here, because the command has already run (nothing to do) + fmt.Printf("/run error getting screen-window: %v\n", err) + } + if sw != nil { + updateMap := make(map[string]interface{}) + updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum + sw, err = sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) + if err != nil { + // ignore error again (nothing to do) + fmt.Printf("/run error updating screen-window selected line: %v\n", err) + } + } + update := sstore.ModelUpdate{ + Line: rtnLine, + Cmd: cmd, + ScreenWindow: sw, + Interactive: pk.Interactive, + } sstore.MainBus.SendUpdate(ids.SessionId, update) ctxVal := ctx.Value(historyContextKey) if ctxVal != nil { @@ -1237,7 +1256,15 @@ func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto if err != nil { return nil, err } - return sstore.ModelUpdate{Line: rtnLine}, nil + updateMap := make(map[string]interface{}) + updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum + sw, err := sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) + if err != nil { + // ignore error again (nothing to do) + fmt.Printf("/comment error updating screen-window selected line: %v\n", err) + } + update := sstore.ModelUpdate{Line: rtnLine, ScreenWindow: sw} + return update, nil } func maybeQuote(s string, quote bool) string { diff --git a/pkg/sstore/dbops.go b/pkg/sstore/dbops.go index feb5ced6..d34f0172 100644 --- a/pkg/sstore/dbops.go +++ b/pkg/sstore/dbops.go @@ -2,7 +2,6 @@ package sstore import ( "context" - "database/sql" "fmt" "strconv" "strings" @@ -16,17 +15,13 @@ const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, linei const DefaultMaxHistoryItems = 1000 func NumSessions(ctx context.Context) (int, error) { - db, err := GetDB(ctx) - if err != nil { - return 0, err - } - query := "SELECT count(*) FROM session" - var count int - err = db.GetContext(ctx, &count, query) - if err != nil { - return 0, err - } - return count, nil + var numSessions int + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := "SELECT count(*) FROM session" + numSessions = tx.GetInt(query) + return nil + }) + return numSessions, txErr } func GetAllRemotes(ctx context.Context) ([]*RemoteType, error) { @@ -157,17 +152,14 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error { if hitem == nil { return fmt.Errorf("cannot insert nil history item") } - db, err := GetDB(ctx) - if err != nil { - return err - } - query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd) VALUES - (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd)` - _, err = db.NamedExec(query, hitem.ToMap()) - if err != nil { - return err - } - return nil + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `INSERT INTO history + ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd) VALUES + (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd)` + tx.NamedExecWrap(query, hitem.ToMap()) + return nil + }) + return txErr } func runHistoryQuery(tx *TxWrap, sessionId string, windowId string, opts HistoryQueryOpts) ([]*HistoryItemType, error) { @@ -372,20 +364,24 @@ func GetSessionById(ctx context.Context, id string) (*SessionType, error) { } func GetSessionByName(ctx context.Context, name string) (*SessionType, error) { - db, err := GetDB(ctx) - if err != nil { - return nil, err - } - var sessionId string - query := `SELECT sessionid FROM session WHERE name = ?` - err = db.GetContext(ctx, &sessionId, query, name) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil + var session *SessionType + txErr := WithTx(ctx, func(tx *TxWrap) error { + query := `SELECT sessionid FROM session WHERE name = ?` + sessionId := tx.GetString(query, name) + if sessionId == "" { + return nil } - return nil, err + var err error + session, err = GetSessionById(tx.Context(), sessionId) + if err != nil { + return err + } + return nil + }) + if txErr != nil { + return nil, txErr } - return GetSessionById(ctx, sessionId) + return session, nil } // also creates default window, returns sessionId diff --git a/pkg/sstore/txwrap.go b/pkg/sstore/txwrap.go index 8033c5f9..02e5698a 100644 --- a/pkg/sstore/txwrap.go +++ b/pkg/sstore/txwrap.go @@ -3,6 +3,7 @@ package sstore import ( "context" "database/sql" + "sync" "github.com/jmoiron/sqlx" ) @@ -15,6 +16,9 @@ type TxWrap struct { type txWrapKey struct{} +// single-threaded access to DB +var globalNestingLock = &sync.Mutex{} + func IsTxWrapContext(ctx context.Context) bool { ctxVal := ctx.Value(txWrapKey{}) return ctxVal != nil @@ -30,6 +34,9 @@ func WithTx(ctx context.Context, fn func(tx *TxWrap) error) (rtnErr error) { } } if txWrap == nil { + globalNestingLock.Lock() + defer globalNestingLock.Unlock() + db, err := GetDB(ctx) if err != nil { return err