Skip to content

Commit 9994997

Browse files
authored
chore: upgrade synapse-go to v0.3.2
1 parent 14e0c74 commit 9994997

14 files changed

Lines changed: 193 additions & 351 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/knadh/koanf/v2 v2.3.4
1515
github.com/multiformats/go-multihash v0.2.3
1616
github.com/prometheus/client_golang v1.23.2
17-
github.com/strahe/synapse-go v0.3.1
17+
github.com/strahe/synapse-go v0.3.2
1818
github.com/uptrace/bun v1.2.18
1919
github.com/uptrace/bun/dialect/pgdialect v1.2.18
2020
github.com/uptrace/bun/dialect/sqlitedialect v1.2.18

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ github.com/smira/go-statsd v1.3.4 h1:kBYWcLSGT+qC6JVbvfz48kX7mQys32fjDOPrfmsSx2c
390390
github.com/smira/go-statsd v1.3.4/go.mod h1:RjdsESPgDODtg1VpVVf9MJrEW2Hw0wtRNbmB1CAhu6A=
391391
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
392392
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
393-
github.com/strahe/synapse-go v0.3.1 h1:GR+CIRGDxh9LRAXDF35NJTkUUXJap2HdOb5aML56G+U=
394-
github.com/strahe/synapse-go v0.3.1/go.mod h1:8aoCafxC0tCz4Ow7fodZipbsbVojVNiUh6nBxLodxjs=
393+
github.com/strahe/synapse-go v0.3.2 h1:dRnCTPdWoJTGhK0pWQoyDYV56Mvux9vD9bB+iYqYdLg=
394+
github.com/strahe/synapse-go v0.3.2/go.mod h1:8aoCafxC0tCz4Ow7fodZipbsbVojVNiUh6nBxLodxjs=
395395
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
396396
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
397397
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=

internal/admin/api_overview.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,22 +103,16 @@ func (s *Server) handleAPIOverview(w http.ResponseWriter, r *http.Request) {
103103
}
104104

105105
// Objects
106-
objCounts, err := s.repos.Objects.CountByState(ctx)
106+
objCounts, err := s.repos.Objects.AggregateByState(ctx)
107107
if err != nil {
108-
s.logger.Warn("overview: failed to count objects", "error", err)
108+
s.logger.Warn("overview: failed to aggregate objects", "error", err)
109109
} else {
110110
for _, oc := range objCounts {
111111
resp.Objects.ByState[oc.State] = oc.Count
112112
resp.Objects.Total += oc.Count
113+
resp.Objects.TotalSizeBytes += oc.TotalSize
113114
}
114115
}
115-
116-
totalSize, err := s.repos.Objects.TotalSize(ctx)
117-
if err != nil {
118-
s.logger.Warn("overview: failed to get total size", "error", err)
119-
} else {
120-
resp.Objects.TotalSizeBytes = totalSize
121-
}
122116
objectAttention, err := s.repos.Objects.CountOverviewAttention(ctx)
123117
if err != nil {
124118
s.logger.Warn("overview: failed to count object attention", "error", err)

internal/admin/api_overview_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ func TestAPIOverviewIncludesAttentionAndActivePipeline(t *testing.T) {
6262

6363
var body struct {
6464
Objects struct {
65-
ByState map[string]int64 `json:"by_state"`
66-
Attention struct {
65+
ByState map[string]int64 `json:"by_state"`
66+
TotalSizeBytes int64 `json:"total_size_bytes"`
67+
Attention struct {
6768
NeedsAttention int64 `json:"needs_attention"`
6869
Unavailable int64 `json:"unavailable"`
6970
} `json:"attention"`
@@ -87,6 +88,9 @@ func TestAPIOverviewIncludesAttentionAndActivePipeline(t *testing.T) {
8788
if body.Objects.ByState[string(model.ObjectStateCached)] == 0 {
8889
t.Fatal("overview should keep legacy object state counts")
8990
}
91+
if body.Objects.TotalSizeBytes != 30 {
92+
t.Fatalf("total_size_bytes = %d, want 30", body.Objects.TotalSizeBytes)
93+
}
9094
if body.Tasks.ByStatus[string(model.TaskStatusCompleted)] == 0 {
9195
t.Fatal("overview should keep legacy task status counts")
9296
}

internal/db/repository/interfaces.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ type ObjectRepository interface {
157157
ResetStaleVersionStates(ctx context.Context, fromState, toState model.ObjectState, staleBefore time.Time) (int, error)
158158
// CountByState returns object counts grouped by state.
159159
CountByState(ctx context.Context) ([]ObjectStateCount, error)
160+
// AggregateByState returns object counts and sizes grouped by state.
161+
AggregateByState(ctx context.Context) ([]ObjectStateAggregate, error)
160162
CountOverviewAttention(ctx context.Context) (ObjectAttentionCount, error)
161-
// TotalSize returns the sum of current object sizes in bytes.
162-
TotalSize(ctx context.Context) (int64, error)
163163
// CountByBucket returns the number of current objects in a bucket.
164164
CountByBucket(ctx context.Context, bucketID int64) (int64, error)
165165
// TotalSizeByBucket returns the sum of current object sizes in a bucket.
@@ -516,6 +516,13 @@ type ObjectStateCount struct {
516516
Count int64 `bun:"count"`
517517
}
518518

519+
// ObjectStateAggregate holds object count and total size grouped by state.
520+
type ObjectStateAggregate struct {
521+
State string `bun:"state"`
522+
Count int64 `bun:"count"`
523+
TotalSize int64 `bun:"total_size"`
524+
}
525+
519526
type ObjectAttentionCount struct {
520527
NeedsAttention int64 `bun:"needs_attention"`
521528
Unavailable int64 `bun:"unavailable"`

internal/db/repository/object_repo.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,22 @@ func (r *BunObjectRepo) CountByState(ctx context.Context) ([]ObjectStateCount, e
880880
return counts, nil
881881
}
882882

883+
// AggregateByState returns current object counts and sizes grouped by state.
884+
func (r *BunObjectRepo) AggregateByState(ctx context.Context) ([]ObjectStateAggregate, error) {
885+
var rows []ObjectStateAggregate
886+
err := r.db.NewSelect().
887+
Model((*model.ObjectVersion)(nil)).
888+
ColumnExpr("state, COUNT(*) AS count, COALESCE(SUM(size), 0) AS total_size").
889+
Where("is_current = ?", true).
890+
Where("is_delete_marker = ?", false).
891+
GroupExpr("state").
892+
Scan(ctx, &rows)
893+
if err != nil {
894+
return nil, fmt.Errorf("aggregating current object versions by state: %w", err)
895+
}
896+
return rows, nil
897+
}
898+
883899
func (r *BunObjectRepo) CountOverviewAttention(ctx context.Context) (ObjectAttentionCount, error) {
884900
var count ObjectAttentionCount
885901
query := `WITH current_versions AS (
@@ -922,21 +938,6 @@ func (r *BunObjectRepo) CountOverviewAttention(ctx context.Context) (ObjectAtten
922938
return count, nil
923939
}
924940

925-
// TotalSize returns the sum of all current object sizes in bytes.
926-
func (r *BunObjectRepo) TotalSize(ctx context.Context) (int64, error) {
927-
var total int64
928-
err := r.db.NewSelect().
929-
Model((*model.ObjectVersion)(nil)).
930-
ColumnExpr("COALESCE(SUM(size), 0)").
931-
Where("is_current = ?", true).
932-
Where("is_delete_marker = ?", false).
933-
Scan(ctx, &total)
934-
if err != nil {
935-
return 0, fmt.Errorf("computing total current object size: %w", err)
936-
}
937-
return total, nil
938-
}
939-
940941
// CountByBucket returns the number of current objects in a bucket.
941942
func (r *BunObjectRepo) CountByBucket(ctx context.Context, bucketID int64) (int64, error) {
942943
count, err := r.db.NewSelect().

internal/db/repository/object_repo_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,43 @@ func TestObjectRepo_CountOverviewAttention(t *testing.T) {
160160
}
161161
}
162162

163+
func TestObjectRepo_AggregateByStateIncludesTotalSize(t *testing.T) {
164+
db := testDB(t)
165+
repos := repository.NewRepositories(db)
166+
ctx := context.Background()
167+
bucket := seedBucket(t, db, "state-aggregate-bucket")
168+
169+
cached := newObjectVersion(bucket.ID, "cached.txt", "01J00000000000000000000B01", 10)
170+
if _, err := repos.Objects.CreateVersionAndSetCurrent(ctx, cached); err != nil {
171+
t.Fatalf("seed cached version: %v", err)
172+
}
173+
failed := newObjectVersion(bucket.ID, "failed.txt", "01J00000000000000000000B02", 20)
174+
if _, err := repos.Objects.CreateVersionAndSetCurrent(ctx, failed); err != nil {
175+
t.Fatalf("seed failed version: %v", err)
176+
}
177+
if err := repos.Objects.UpdateVersionStateToFailed(ctx, failed.VersionID, model.ObjectStateCached, "upload failed"); err != nil {
178+
t.Fatalf("mark failed version: %v", err)
179+
}
180+
if _, err := repos.Objects.CreateDeleteMarkerAndSetCurrent(ctx, bucket.ID, "deleted.txt", "01J00000000000000000000B03"); err != nil {
181+
t.Fatalf("seed delete marker: %v", err)
182+
}
183+
184+
rows, err := repos.Objects.AggregateByState(ctx)
185+
if err != nil {
186+
t.Fatalf("AggregateByState: %v", err)
187+
}
188+
byState := make(map[string]repository.ObjectStateAggregate, len(rows))
189+
for _, row := range rows {
190+
byState[row.State] = row
191+
}
192+
if got := byState[string(model.ObjectStateCached)]; got.Count != 1 || got.TotalSize != 10 {
193+
t.Fatalf("cached aggregate = count:%d size:%d, want 1/10", got.Count, got.TotalSize)
194+
}
195+
if got := byState[string(model.ObjectStateFailed)]; got.Count != 1 || got.TotalSize != 20 {
196+
t.Fatalf("failed aggregate = count:%d size:%d, want 1/20", got.Count, got.TotalSize)
197+
}
198+
}
199+
163200
func TestObjectRepo_CreateVersionAndSetCurrent_ConcurrentFirstUpload(t *testing.T) {
164201
sqldb, err := sql.Open("sqlite", "file:"+filepath.Join(t.TempDir(), "objects.db")+"?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)")
165202
if err != nil {

internal/securetoken/token.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ package securetoken
33
import (
44
"crypto/rand"
55
"encoding/base64"
6+
"fmt"
67
)
78

89
// URL generates a URL-safe random token from n bytes of entropy.
910
func URL(n int) (string, error) {
11+
if n <= 0 {
12+
return "", fmt.Errorf("securetoken: entropy bytes must be positive")
13+
}
1014
buf := make([]byte, n)
1115
if _, err := rand.Read(buf); err != nil {
1216
return "", err

internal/securetoken/token_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package securetoken
2+
3+
import (
4+
"encoding/base64"
5+
"strconv"
6+
"testing"
7+
)
8+
9+
func TestURLReturnsRawURLTokenWithRequestedEntropy(t *testing.T) {
10+
for _, n := range []int{16, 20, 32} {
11+
t.Run(strconv.Itoa(n), func(t *testing.T) {
12+
token, err := URL(n)
13+
if err != nil {
14+
t.Fatalf("URL(%d) error = %v, want nil", n, err)
15+
}
16+
if token == "" {
17+
t.Fatalf("URL(%d) token is empty", n)
18+
}
19+
decoded, err := base64.RawURLEncoding.DecodeString(token)
20+
if err != nil {
21+
t.Fatalf("URL(%d) token is not raw URL base64: %v", n, err)
22+
}
23+
if len(decoded) != n {
24+
t.Fatalf("URL(%d) decoded bytes = %d, want %d", n, len(decoded), n)
25+
}
26+
})
27+
}
28+
}
29+
30+
func TestURLRejectsInvalidEntropy(t *testing.T) {
31+
for _, n := range []int{0, -1} {
32+
t.Run(strconv.Itoa(n), func(t *testing.T) {
33+
defer func() {
34+
if r := recover(); r != nil {
35+
t.Fatalf("URL(%d) panicked, want error: %v", n, r)
36+
}
37+
}()
38+
token, err := URL(n)
39+
if err == nil {
40+
t.Fatalf("URL(%d) error = nil, want error", n)
41+
}
42+
if token != "" {
43+
t.Fatalf("URL(%d) token = %q, want empty", n, token)
44+
}
45+
})
46+
}
47+
}

internal/worker/wallet_operation_runner_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ type fakeWalletOperator struct {
429429
withdrawAmount *big.Int
430430
onFund func(context.Context)
431431
blockFund bool
432+
fundErr error
432433
}
433434

434435
func (f *fakeWalletOperator) FundUSDFC(ctx context.Context, amount *big.Int) (string, error) {
@@ -440,6 +441,9 @@ func (f *fakeWalletOperator) FundUSDFC(ctx context.Context, amount *big.Int) (st
440441
<-ctx.Done()
441442
return "", ctx.Err()
442443
}
444+
if f.fundErr != nil {
445+
return "", f.fundErr
446+
}
443447
return f.fundHash, nil
444448
}
445449

@@ -515,3 +519,33 @@ func (f *fakeWalletEventPublisher) statuses() []model.WalletOperationStatus {
515519
}
516520
return statuses
517521
}
522+
523+
func TestWalletOperationRunner_BroadcastErrorFailsOperation(t *testing.T) {
524+
db := testutil.NewTestDB(t)
525+
repos := repository.NewRepositories(db)
526+
ctx := context.Background()
527+
op, _, err := repos.WalletOperations.CreateOrGet(ctx, repository.CreateWalletOperationInput{
528+
Type: model.WalletOperationTypeFund,
529+
ClientRequestID: "fund-broadcast-error",
530+
Amount: "100",
531+
})
532+
if err != nil {
533+
t.Fatalf("CreateOrGet: %v", err)
534+
}
535+
536+
operator := &fakeWalletOperator{fundErr: errors.New("rpc node offline")}
537+
runner := NewWalletOperationRunner(repos, operator, nil, time.Millisecond, nil)
538+
539+
runner.runOnce(ctx)
540+
541+
got, err := repos.WalletOperations.GetByID(ctx, op.ID)
542+
if err != nil {
543+
t.Fatalf("GetByID: %v", err)
544+
}
545+
if got.Status != model.WalletOperationStatusFailed {
546+
t.Fatalf("status = %q, want failed", got.Status)
547+
}
548+
if got.LastError == nil || !strings.Contains(*got.LastError, "rpc node offline") {
549+
t.Fatalf("last_error = %v, want rpc node offline", got.LastError)
550+
}
551+
}

0 commit comments

Comments
 (0)