all: Send deadlocks as failures, crash only as a last resort (#7785)

This commit is contained in:
Simon Frei
2021-07-27 21:27:52 +02:00
committed by GitHub
parent dc0dd09e93
commit 67b18569cf
6 changed files with 194 additions and 62 deletions
+40 -16
View File
@@ -11,6 +11,8 @@ import (
"context"
"encoding/json"
"net/http"
"runtime/pprof"
"strings"
"time"
"github.com/syncthing/syncthing/lib/build"
@@ -35,9 +37,24 @@ var (
)
type FailureReport struct {
FailureData
Count int
Version string
}
type FailureData struct {
Description string
Count int
Version string
Goroutines string
Extra map[string]string
}
func FailureDataWithGoroutines(description string) FailureData {
var buf *strings.Builder
pprof.NewProfile("goroutine").WriteTo(buf, 1)
return FailureData{
Description: description,
Goroutines: buf.String(),
}
}
type FailureHandler interface {
@@ -64,6 +81,7 @@ type failureHandler struct {
type failureStat struct {
first, last time.Time
count int
data FailureData
}
func (h *failureHandler) Serve(ctx context.Context) error {
@@ -82,23 +100,28 @@ func (h *failureHandler) Serve(ctx context.Context) error {
if !ok {
// Just to be safe - shouldn't ever happen, as
// evChan is set to nil when unsubscribing.
h.addReport(evChanClosed, time.Now())
h.addReport(FailureData{Description: evChanClosed}, time.Now())
evChan = nil
continue
}
descr, ok := e.Data.(string)
if !ok {
var data FailureData
switch d := e.Data.(type) {
case string:
data.Description = d
case FailureData:
data = d
default:
// Same here, shouldn't ever happen.
h.addReport(invalidEventDataType, time.Now())
h.addReport(FailureData{Description: invalidEventDataType}, time.Now())
continue
}
h.addReport(descr, e.Time)
h.addReport(data, e.Time)
case <-timer.C:
reports := make([]FailureReport, 0, len(h.buf))
now := time.Now()
for descr, stat := range h.buf {
if now.Sub(stat.last) > minDelay || now.Sub(stat.first) > maxDelay {
reports = append(reports, newFailureReport(descr, stat.count))
reports = append(reports, newFailureReport(stat))
delete(h.buf, descr)
}
}
@@ -125,8 +148,8 @@ func (h *failureHandler) Serve(ctx context.Context) error {
sub.Unsubscribe()
if len(h.buf) > 0 {
reports := make([]FailureReport, 0, len(h.buf))
for descr, stat := range h.buf {
reports = append(reports, newFailureReport(descr, stat.count))
for _, stat := range h.buf {
reports = append(reports, newFailureReport(stat))
}
timeout, cancel := context.WithTimeout(context.Background(), finalSendTimeout)
defer cancel()
@@ -151,16 +174,17 @@ func (h *failureHandler) applyOpts(opts config.OptionsConfiguration, sub events.
return url, nil, nil
}
func (h *failureHandler) addReport(descr string, evTime time.Time) {
if stat, ok := h.buf[descr]; ok {
func (h *failureHandler) addReport(data FailureData, evTime time.Time) {
if stat, ok := h.buf[data.Description]; ok {
stat.last = evTime
stat.count++
return
}
h.buf[descr] = &failureStat{
h.buf[data.Description] = &failureStat{
first: evTime,
last: evTime,
count: 1,
data: data,
}
}
@@ -210,10 +234,10 @@ func sendFailureReports(ctx context.Context, reports []FailureReport, url string
return
}
func newFailureReport(descr string, count int) FailureReport {
func newFailureReport(stat *failureStat) FailureReport {
return FailureReport{
Description: descr,
Count: count,
FailureData: stat.data,
Count: stat.count,
Version: build.LongVersion,
}
}
+8 -3
View File
@@ -23,8 +23,8 @@ import (
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/model"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/scanner"
"github.com/syncthing/syncthing/lib/upgrade"
@@ -38,15 +38,20 @@ const Version = 3
var StartTime = time.Now().Truncate(time.Second)
type Model interface {
DBSnapshot(folder string) (*db.Snapshot, error)
UsageReportingStats(report *contract.Report, version int, preview bool)
}
type Service struct {
cfg config.Wrapper
model model.Model
model Model
connectionsService connections.Service
noUpgrade bool
forceRun chan struct{}
}
func New(cfg config.Wrapper, m model.Model, connectionsService connections.Service, noUpgrade bool) *Service {
func New(cfg config.Wrapper, m Model, connectionsService connections.Service, noUpgrade bool) *Service {
return &Service{
cfg: cfg,
model: m,