fix: deep bug hunt II — concurrency, security & optimization (25 files)
Critical: watchdog mutex panic safety, SetGeoAppOverride nil guard, SSD-only app DB restore fallback. High: double deploy race (atomic Deploying flag), delete/remove during deploy guard, ScanStacks overwrite protection, FileBrowser mount mutex, PushEvent history, PushOnce error handling, DB dump sync+close before rename, restic retry fresh context, encrypt failure logging, cross-backup path traversal validation, deepCopyStack completeness. Security: constant-time API key comparison, login rate limiting (5/min), git credential masking in logs, storage path prefix traversal fix. Concurrency: MigrateEncryption lock ordering, SubdomainInUse I/O outside lock, scheduler late-registered jobs, SQLite WAL verification, metrics shutdown context, telemetry scan error logging, asset sync lock scope. Optimization: streaming file copy for DB dumps, restic stats dedup, atomic infra config copy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,25 +54,25 @@ func (c *MetricsCollector) loop(ctx context.Context) {
|
||||
defer ticker.Stop()
|
||||
|
||||
// Sample immediately on start
|
||||
c.sample()
|
||||
c.sampleWith(ctx)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
c.sample()
|
||||
c.sampleWith(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MetricsCollector) sample() {
|
||||
func (c *MetricsCollector) sampleWith(ctx context.Context) {
|
||||
sys := c.sampleSystem()
|
||||
if err := c.store.InsertSystemMetrics(sys); err != nil {
|
||||
c.logger.Printf("[WARN] Failed to store system metrics: %v", err)
|
||||
}
|
||||
|
||||
containers := c.sampleContainers()
|
||||
containers := c.sampleContainers(ctx)
|
||||
if err := c.store.InsertContainerMetrics(containers); err != nil {
|
||||
c.logger.Printf("[WARN] Failed to store container metrics: %v", err)
|
||||
}
|
||||
@@ -96,8 +96,8 @@ func (c *MetricsCollector) sampleSystem() SystemSample {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MetricsCollector) sampleContainers() []ContainerSample {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
func (c *MetricsCollector) sampleContainers(parentCtx context.Context) []ContainerSample {
|
||||
ctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "docker", "stats", "--no-stream",
|
||||
|
||||
@@ -22,9 +22,18 @@ func NewMetricsStore(dbPath string, logger *log.Logger) (*MetricsStore, error) {
|
||||
return nil, fmt.Errorf("open sqlite: %w", err)
|
||||
}
|
||||
|
||||
// Set pragmas for performance and concurrency
|
||||
// Enable WAL mode and verify it took effect
|
||||
var walMode string
|
||||
if err := db.QueryRow("PRAGMA journal_mode=WAL").Scan(&walMode); err != nil {
|
||||
db.Close()
|
||||
return nil, fmt.Errorf("set WAL mode: %w", err)
|
||||
}
|
||||
if walMode != "wal" {
|
||||
db.Close()
|
||||
return nil, fmt.Errorf("WAL mode not supported on this filesystem (got %q)", walMode)
|
||||
}
|
||||
// Set remaining pragmas for performance and concurrency
|
||||
pragmas := []string{
|
||||
"PRAGMA journal_mode=WAL",
|
||||
"PRAGMA synchronous=NORMAL",
|
||||
"PRAGMA busy_timeout=5000",
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ func (s *MetricsStore) GetContainerTelemetry(since time.Time) ([]ContainerTeleme
|
||||
var ct ContainerTelemetry
|
||||
if err := rows.Scan(&ct.ContainerName, &ct.MemoryAvgMB, &ct.MemoryPeakMB,
|
||||
&ct.CPUAvgPercent, &ct.SampleCount); err != nil {
|
||||
log.Printf("[WARN] telemetry row scan failed: %v", err)
|
||||
continue
|
||||
}
|
||||
results = append(results, ct)
|
||||
|
||||
Reference in New Issue
Block a user