Skip to content

Commit 0203941

Browse files
author
JkLondon
committed
commit
1 parent cbd605c commit 0203941

File tree

4 files changed

+65
-20
lines changed

4 files changed

+65
-20
lines changed

cmd/etui/app/app.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package app
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"path/filepath"
8+
"runtime/debug"
69
"time"
710

811
"github.com/gdamore/tcell/v2"
@@ -54,10 +57,10 @@ func (a *App) Run(parent context.Context, infoCh <-chan *commands.StagesInfo, er
5457
AddItem(footer, 2, 1, false)
5558

5659
// Start background goroutines
57-
go a.fillStagesInfo(ctx, nodeView, infoCh)
58-
go a.runClock(ctx, nodeView.Clock)
59-
go a.handleErrors(ctx, errCh, footer)
60-
go a.pollDownloader(ctx, nodeView.Downloader, errCh)
60+
go a.safeGo("fillStagesInfo", errCh, func() { a.fillStagesInfo(ctx, nodeView, infoCh) })
61+
go a.safeGo("runClock", errCh, func() { a.runClock(ctx, nodeView.Clock) })
62+
go a.handleErrors(ctx, errCh, footer) // not wrapped — it drains errCh itself
63+
go a.safeGo("pollDownloader", errCh, func() { a.pollDownloader(ctx, nodeView.Downloader, errCh) })
6164

6265
// Page navigation
6366
currentPage, pagesCount := 0, 2
@@ -85,6 +88,33 @@ func (a *App) Run(parent context.Context, infoCh <-chan *commands.StagesInfo, er
8588
return nil
8689
}
8790

91+
// safeGo wraps a function with panic recovery, logging the stack to etui-crash.log
92+
// and sending the error to errCh for display in the TUI footer.
93+
func (a *App) safeGo(name string, errCh chan error, fn func()) {
94+
defer func() {
95+
if r := recover(); r != nil {
96+
msg := fmt.Sprintf("panic in %s: %v\n%s", name, r, debug.Stack())
97+
a.writeCrashLog(msg)
98+
select {
99+
case errCh <- fmt.Errorf("panic in %s: %v (see etui-crash.log)", name, r):
100+
default:
101+
}
102+
}
103+
}()
104+
fn()
105+
}
106+
107+
func (a *App) writeCrashLog(msg string) {
108+
logPath := filepath.Join(a.datadir, "etui-crash.log")
109+
entry := fmt.Sprintf("[%s] %s\n\n", time.Now().Format(time.RFC3339), msg)
110+
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
111+
if err != nil {
112+
return
113+
}
114+
defer f.Close()
115+
f.WriteString(entry)
116+
}
117+
88118
// fillStagesInfo reads StagesInfo from the channel and updates the node-info view.
89119
func (a *App) fillStagesInfo(ctx context.Context, view *widgets.NodeInfoView, infoCh <-chan *commands.StagesInfo) {
90120
for {

cmd/etui/cmd/stages.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package cmd
22

33
import (
4-
"errors"
54
"fmt"
5+
"os"
6+
"path/filepath"
7+
"runtime/debug"
68
"time"
79

810
"github.com/spf13/cobra"
911

1012
"github.com/erigontech/erigon/cmd/etui/app"
1113
"github.com/erigontech/erigon/cmd/integration/commands"
1214
log "github.com/erigontech/erigon/common/log/v3"
13-
"github.com/erigontech/erigon/db/state"
1415
)
1516

1617
var (
@@ -32,28 +33,26 @@ var infoCmd = &cobra.Command{
3233
defer close(infoCh)
3334
defer func() {
3435
if r := recover(); r != nil {
35-
println("recovered from panic: ", r)
36+
msg := fmt.Sprintf("panic in InfoAllStages: %v\n%s", r, debug.Stack())
37+
writeCrashLog(datadirCli, msg)
38+
select {
39+
case errCh <- fmt.Errorf("panic: %v (see etui-crash.log)", r):
40+
default:
41+
}
3642
}
3743
}()
38-
backoff := 5 * time.Second
44+
const retryInterval = 5 * time.Second
3945
for {
4046
err := commands.InfoAllStages(cmd.Context(), logger, datadirCli, infoCh)
4147
if err == nil {
4248
return
4349
}
44-
// Salt files missing means OtterSync is still downloading — retry
45-
if !errors.Is(err, state.ErrCannotStartWithoutSaltFiles) {
46-
select {
47-
case errCh <- err:
48-
default:
49-
}
50-
return
51-
}
52-
// Don't report to errCh — the downloader widget already shows progress.
50+
// All errors are transient for etui: DB locked, salt files
51+
// missing, chaindata not yet created, etc. Always retry.
5352
select {
5453
case <-cmd.Context().Done():
5554
return
56-
case <-time.After(backoff):
55+
case <-time.After(retryInterval):
5756
}
5857
}
5958
}()
@@ -65,3 +64,15 @@ var infoCmd = &cobra.Command{
6564
func init() {
6665
infoCmd.Flags().StringVar(&datadirCli, "datadir", "", "Directory containing versioned files")
6766
}
67+
68+
// writeCrashLog appends a panic message with stack trace to etui-crash.log in the datadir.
69+
func writeCrashLog(datadir, msg string) {
70+
logPath := filepath.Join(datadir, "etui-crash.log")
71+
entry := fmt.Sprintf("[%s] %s\n\n", time.Now().Format(time.RFC3339), msg)
72+
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
73+
if err != nil {
74+
return
75+
}
76+
defer f.Close()
77+
f.WriteString(entry)
78+
}

cmd/integration/commands/root.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ func openDB(opts kv2.MdbxOpts, applyMigrations bool, chain string, logger log.Lo
9999
panic(opts.GetLabel())
100100
}
101101

102-
rawDB := opts.MustOpen()
102+
rawDB, err := opts.Open(context.Background())
103+
if err != nil {
104+
return nil, fmt.Errorf("open chaindata: %w", err)
105+
}
103106
if applyMigrations {
104107
dirs := datadir.New(datadirCli)
105108
migrationsDB, err := migrations.OpenMigrationsDB(dirs.Migrations, logger)

cmd/integration/commands/stages_info.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ func InfoAllStages(ctx context.Context, logger log.Logger, dataDirPath string, i
278278

279279
opts := kv2.New(dbcfg.ChainDB, logger).
280280
Path(chainDataPath).
281-
Accede(true) // open read-only, don't create
281+
Accede(true).
282+
Readonly(true) // open read-only so etui can coexist with running erigon
282283

283284
db, err := openDB(opts, false, "", logger)
284285
if err != nil {

0 commit comments

Comments
 (0)