From c39156f165bf06b680ee4858b20b7495b899a444 Mon Sep 17 00:00:00 2001 From: Will Andrews Date: Mon, 23 Feb 2026 19:33:04 +0000 Subject: [PATCH] improve error handling --- pds.go | 23 ++++++++++++++++----- tangled_knot.go | 54 +++++++++++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/pds.go b/pds.go index 625a6e8..fbbc138 100644 --- a/pds.go +++ b/pds.go @@ -27,14 +27,12 @@ func (s *service) backupPDS(ctx context.Context) { if err != nil { slog.Error("backup repo", "error", err) bugsnag.Notify(err) - return } err = s.backupBlobs(ctx) if err != nil { slog.Error("backup blobs", "error", err) bugsnag.Notify(err) - return } slog.Info("finished PDS backup") @@ -71,6 +69,12 @@ func (s *service) backupRepo(ctx context.Context) error { err = os.Remove(filename) if err != nil { slog.Error("failed to delete pds repo file after uploading", "error", err, "filename", f.Name()) + metadata := bugsnag.MetaData{ + "file": { + "filename": f.Name(), + }, + } + bugsnag.Notify(fmt.Errorf("delete pds repo file after uploading: %w", err), metadata) } }() @@ -95,12 +99,12 @@ func (s *service) backupRepo(ctx context.Context) error { fi, err := f.Stat() if err != nil { - return fmt.Errorf("stat file: %w", err) + return fmt.Errorf("stat written file: %w", err) } _, err = s.minioClient.PutObject(ctx, s.bucketName, "pds-repo.zip", f, fi.Size(), minio.PutObjectOptions{}) if err != nil { - return fmt.Errorf("stream repo to bucket: %w", err) + return fmt.Errorf("put repo file to bucket: %w", err) } return nil @@ -123,7 +127,6 @@ func (s *service) backupBlobs(ctx context.Context) error { zipWriter := zip.NewWriter(f) for _, cid := range cids { - slog.Info("processing cid", "cid", cid) blob, err := s.getBlob(ctx, cid) if err != nil { slog.Error("failed to get blob", "cid", cid, "error", err) @@ -222,6 +225,8 @@ func (s *service) getBlob(ctx context.Context, cid string) ([]byte, error) { return existing, nil } + slog.Info("blob not found locally - downloading", "did", s.did, "cid", cid) + // TODO: do proper url encoding of query params url := fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob?did=%s&cid=%s", s.pdsHost, s.did, cid) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) @@ -243,6 +248,14 @@ func (s *service) getBlob(ctx context.Context, cid string) ([]byte, error) { err = os.WriteFile(filename, b, os.ModePerm) if err != nil { slog.Error("writing blob", "error", err, "cid", cid) + metadata := bugsnag.MetaData{ + "blob": { + "filename": filename, + "cid": cid, + "did": s.did, + }, + } + bugsnag.Notify(fmt.Errorf("writing blob to local storage: %w", err), metadata) } return b, nil diff --git a/tangled_knot.go b/tangled_knot.go index a4e0836..9dcb53f 100644 --- a/tangled_knot.go +++ b/tangled_knot.go @@ -17,13 +17,21 @@ import ( ) func (s *service) backupTangledKnot(ctx context.Context) { - s.backupKnotDB(ctx) - s.backupKnotRepos(ctx) + err := s.backupKnotDB(ctx) + if err != nil { + bugsnag.Notify(fmt.Errorf("backup knot db: %w", err)) + slog.Error("failed to backup knot db", "error", err) + } + err = s.backupKnotRepos(ctx) + if err != nil { + bugsnag.Notify(fmt.Errorf("backup knot db: %w", err)) + slog.Error("failed to backup knot db", "error", err) + } slog.Info("finished tangled knot backup") } -func (s *service) backupKnotDB(ctx context.Context) { +func (s *service) backupKnotDB(ctx context.Context) error { dir := os.Getenv("TANGLED_KNOT_DATABASE_DIRECTORY") if dir == "" { slog.Info("TANGLED_KNOT_DATABASE_DIRECTORY env not set - skipping knot DB backup") @@ -32,8 +40,7 @@ func (s *service) backupKnotDB(ctx context.Context) { filename := path.Join(s.blobDir, fmt.Sprintf("%d-knot.zip", time.Now().UnixMilli())) f, err := os.Create(filename) if err != nil { - slog.Error("creating temp file", "error", err) - return + return fmt.Errorf("creating temp file: %w", err) } defer func() { f.Close() @@ -41,6 +48,12 @@ func (s *service) backupKnotDB(ctx context.Context) { err = os.Remove(filename) if err != nil { slog.Error("failed to delete knot db zip file after uploading", "error", err, "filename", f.Name()) + metadata := bugsnag.MetaData{ + "file": { + "filename": f.Name(), + }, + } + bugsnag.Notify(fmt.Errorf("delete knot db zip file after uploading: %w", err), metadata) } }() @@ -49,24 +62,22 @@ func (s *service) backupKnotDB(ctx context.Context) { // reset the reader back to the start so that the minio upload can read the data that's been written. _, err = f.Seek(0, 0) if err != nil { - slog.Error("setting seek on written file", "error", err) - return + return fmt.Errorf("setting seek on written file: %w", err) } fi, err := f.Stat() if err != nil { - slog.Error("failed to stat knot db zip file", "error", err) - return + return fmt.Errorf("stat written file: %w", err) } _, err = s.minioClient.PutObject(ctx, s.bucketName, "knot-db.zip", f, fi.Size(), minio.PutObjectOptions{}) if err != nil { - slog.Error("stream knot DB to bucket", "error", err) - bugsnag.Notify(err) + return fmt.Errorf("put knot db zip file to bucket: %w", err) } + return nil } -func (s *service) backupKnotRepos(ctx context.Context) { +func (s *service) backupKnotRepos(ctx context.Context) error { dir := os.Getenv("TANGLED_KNOT_REPOSITORY_DIRECTORY") if dir == "" { slog.Info("TANGLED_KNOT_REPOSITORY_DIRECTORY env not set - skipping knot repo backup") @@ -75,8 +86,7 @@ func (s *service) backupKnotRepos(ctx context.Context) { filename := path.Join(s.blobDir, fmt.Sprintf("%d-knot-repos.zip", time.Now().UnixMilli())) f, err := os.Create(filename) if err != nil { - slog.Error("creating temp file", "error", err) - return + return fmt.Errorf("creating temp file: %w", err) } defer func() { f.Close() @@ -84,6 +94,12 @@ func (s *service) backupKnotRepos(ctx context.Context) { err = os.Remove(filename) if err != nil { slog.Error("failed to delete knot repos zip file after uploading", "error", err, "filename", f.Name()) + metadata := bugsnag.MetaData{ + "file": { + "filename": f.Name(), + }, + } + bugsnag.Notify(fmt.Errorf("delete knot repos zip file after uploading: %w", err), metadata) } }() @@ -92,21 +108,19 @@ func (s *service) backupKnotRepos(ctx context.Context) { // reset the reader back to the start so that the minio upload can read the data that's been written. _, err = f.Seek(0, 0) if err != nil { - slog.Error("setting seek on written file", "error", err) - return + return fmt.Errorf("setting seek on written file: %w", err) } fi, err := f.Stat() if err != nil { - slog.Error("failed to stat knot db zip file", "error", err) - return + return fmt.Errorf("stat written file: %w", err) } _, err = s.minioClient.PutObject(ctx, s.bucketName, "knot-repos.zip", f, fi.Size(), minio.PutObjectOptions{}) if err != nil { - slog.Error("write knot repos to bucket", "error", err) - bugsnag.Notify(err) + return fmt.Errorf("put knot repo file to bucket: %w", err) } + return nil } func compress(src string, writer io.Writer) error { -- 2.51.2