Compare commits

...

1 Commits

Author SHA1 Message Date
hyung-hwan 487eb57e28 updated sqlite db connecting handling 2026-06-11 19:00:55 +09:00
2 changed files with 82 additions and 16 deletions
+60 -16
View File
@@ -26,6 +26,14 @@ func Open(driver, dsn string) (*Store, error) {
var err error
drv = driverName(driver)
if drv == "sqlite" {
dsn = appendSQLitePragmas(dsn, map[string]string{
"busy_timeout": "10000",
"journal_mode": "WAL",
"foreign_keys": "1",
})
}
fmt.Printf("CONNECTING TO [[[%s]]]\n", dsn)
db, err = sql.Open(drv, dsn)
if err != nil {
goto oops
@@ -41,22 +49,6 @@ func Open(driver, dsn string) (*Store, error) {
goto oops
}
if drv == "sqlite" {
_, err = db.Exec(`PRAGMA busy_timeout = 10000`)
if err != nil {
goto oops
}
_, err = db.Exec(`PRAGMA journal_mode = WAL`)
if err != nil {
goto oops
}
_, err = db.Exec(`PRAGMA foreign_keys = 1`)
if err != nil {
goto oops
}
}
return &Store{DB: db}, nil
oops:
@@ -66,6 +58,58 @@ oops:
return nil, err
}
// appendSQLitePragmas appends _pragma=name(value) URI parameters to the DSN
// for any pragma not already present. A plain file path is promoted to a
// file: URI so it can carry query parameters.
func appendSQLitePragmas(dsn string, pragmas map[string]string) string {
var base string
var existing string
var sep string
var idx int
var name string
var value string
var result string
if strings.HasPrefix(dsn, "file:") {
idx = strings.Index(dsn, "?")
if idx < 0 {
base = dsn
existing = ""
} else {
base = dsn[:idx]
existing = dsn[idx+1:]
}
} else if strings.Contains(dsn, "?") {
idx = strings.Index(dsn, "?")
base = dsn[:idx]
existing = dsn[idx+1:]
} else if dsn != ":memory:" {
base = "file:" + dsn
existing = ""
} else {
base = dsn
existing = ""
}
result = base
if existing != "" {
result = base + "?" + existing
sep = "&"
} else {
sep = "?"
}
for name, value = range pragmas {
// [NOTE] this existence check isn't accurate but is practically good
if strings.Contains(existing, "_pragma="+name+"(") {
continue
}
result = result + sep + "_pragma=" + name + "(" + value + ")"
sep = "&"
}
return result
}
func driverName(driver string) string {
switch strings.ToLower(strings.TrimSpace(driver)) {
case "sqlite", "sqlite3":
+22
View File
@@ -277,6 +277,15 @@ function SessionTerminalPanel(props: SessionPanelProps) {
syncedSessionIDsRef.current = syncedSessionIDs
}, [syncedSessionIDs])
useEffect(() => {
if (status !== 'pending' && status !== 'connecting') { return }
const timer = window.setTimeout(() => {
setError('Connection timed out. Click Connect to try again.')
setStatus('error')
}, 30000)
return () => { window.clearTimeout(timer) }
}, [status, sessionID])
useEffect(() => {
let animationFrameID: number
let settleTimerOne: number
@@ -853,6 +862,8 @@ export default function SSHSessionPage() {
const [streamWSCheck, setStreamWSCheck] = useState<number>(0)
const streamHandlersRef = useRef<Record<string, SessionStreamHandlers>>({})
const attachedSessionIDsRef = useRef<Record<string, boolean>>({})
const wsReconnectAttemptRef = useRef<number>(0)
const wsReconnectTimerRef = useRef<number>(0)
const stackedLayout = useMediaQuery(theme.breakpoints.down('lg'))
const historyDialogMode: boolean = useMediaQuery(theme.breakpoints.down('md'))
const rowCount = useMemo(() => {
@@ -1334,6 +1345,7 @@ export default function SSHSessionPage() {
streamWSRef.current = ws
ws.onopen = () => {
let i: number
wsReconnectAttemptRef.current = 0
attachedSessionIDsRef.current = {}
for (i = 0; i < openSessionIDs.length; i += 1) {
attachStreamSession(openSessionIDs[i])
@@ -1374,6 +1386,12 @@ export default function SSHSessionPage() {
handlers = streamHandlersRef.current[ids[i]]
if (handlers) handlers.onTransportClosed('SSH workspace stream closed')
}
// schedule reconnect with exponential backoff; immediate on first attempt
const delay: number = Math.min(Math.pow(2, wsReconnectAttemptRef.current + 1) * 1000, 30000)
wsReconnectAttemptRef.current += 1
wsReconnectTimerRef.current = window.setTimeout(() => {
setStreamWSCheck((prev) => prev + 1)
}, delay)
}
ws.onerror = () => {
setPageError((prev) => prev || 'SSH workspace stream error')
@@ -1383,6 +1401,10 @@ export default function SSHSessionPage() {
useEffect(() => {
// close the workspace websocket upon unmount
return () => {
if (wsReconnectTimerRef.current) {
window.clearTimeout(wsReconnectTimerRef.current)
wsReconnectTimerRef.current = 0
}
if (streamWSRef.current) {
streamWSRef.current.close()
streamWSRef.current = null