diff --git a/go.mod b/go.mod index 4280285c..2a0d15aa 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.24.2 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.17.1-0.20250603132151-341ea279ce81 + github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1 github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a github.com/ProtonMail/go-proton-api v0.4.1-0.20250417134000-e624a080f7ba github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton diff --git a/go.sum b/go.sum index 882f2da3..3563e3e8 100644 --- a/go.sum +++ b/go.sum @@ -36,10 +36,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I= github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug= github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= -github.com/ProtonMail/gluon v0.17.1-0.20250528125353-9b611f58b753 h1:Zym7WHKLOu1RAUc9b8vkhwEaEU2Gi6MkaurCm7zpK6E= -github.com/ProtonMail/gluon v0.17.1-0.20250528125353-9b611f58b753/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8= -github.com/ProtonMail/gluon v0.17.1-0.20250603132151-341ea279ce81 h1:p3X/izcQ3VtYI7qF0pmejByXPTRisFkkS+xCxXgeCxs= -github.com/ProtonMail/gluon v0.17.1-0.20250603132151-341ea279ce81/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8= +github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1 h1:FvkPBZF/M5GpZTy+hzhaheyi+Z5XWeZOL5GKVKqj85Y= +github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8= github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= github.com/ProtonMail/go-crypto v1.1.4-proton h1:KIo9uNlk3vzlwI7o5VjhiEjI4Ld1TDixOMnoNZyfpFE= github.com/ProtonMail/go-crypto v1.1.4-proton/go.mod h1:zNoyBJW3p/yVWiHNZgfTF9VsjwqYof5YY0M9kt2QaX0= diff --git a/internal/services/syncservice/handler.go b/internal/services/syncservice/handler.go index a4b1a5e9..494ddaea 100644 --- a/internal/services/syncservice/handler.go +++ b/internal/services/syncservice/handler.go @@ -108,8 +108,6 @@ func (t *Handler) Execute( t.log.WithError(err).Error("Sync aborted") break } else if err = t.run(ctx, syncReporter, labels, updateApplier, messageBuilder); err != nil { - t.log.WithError(err).Error("Failed to sync, will retry later") - if sentryErr := t.sentryReporter.ReportMessageWithContext("Failed to sync, will retry later", reporter.Context{ "err": err.Error(), "user_id": t.userID, @@ -117,6 +115,7 @@ func (t *Handler) Execute( t.log.WithError(sentryErr).Error("Failed to report sentry message") } + t.log.WithError(err).Error("Failed to sync, will retry later") sleepCtx(ctx, coolDown) } else { break diff --git a/internal/services/userevents/service.go b/internal/services/userevents/service.go index 6e0f72ea..307cf868 100644 --- a/internal/services/userevents/service.go +++ b/internal/services/userevents/service.go @@ -29,6 +29,8 @@ import ( "time" "github.com/ProtonMail/gluon/async" + "github.com/ProtonMail/gluon/db" + "github.com/ProtonMail/gluon/reporter" "github.com/ProtonMail/gluon/watcher" "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/proton-bridge/v3/internal" @@ -70,6 +72,8 @@ type Service struct { eventPollWaitersLock sync.Mutex eventSubscription events.Subscription eventWatcher *watcher.Watcher[events.Event] + + sentryReporter reporter.Reporter } func NewService( @@ -82,6 +86,7 @@ func NewService( eventTimeout time.Duration, panicHandler async.PanicHandler, eventSubscription events.Subscription, + sentryReporter reporter.Reporter, ) *Service { return &Service{ cpc: cpc.NewCPC(), @@ -99,6 +104,7 @@ func NewService( panicHandler: panicHandler, eventSubscription: eventSubscription, eventWatcher: eventSubscription.Add(events.ConnStatusDown{}, events.ConnStatusUp{}), + sentryReporter: sentryReporter, } } @@ -414,6 +420,14 @@ func (s *Service) handleEventError(ctx context.Context, lastEventID string, even return subscriberName, fmt.Errorf("failed to handle event due to server error: %w", err) } + if db.IsUniqueLabelConstraintError(err) { + if err := s.sentryReporter.ReportMessageWithContext("Unique label constraint error occurred on event", reporter.Context{ + "err": err, + }); err != nil { + s.log.WithError(err).Error("Failed to report label constraint error to sentry") + } + } + // Otherwise, the error is a client-side issue; notify bridge to handle it. s.log.WithField("event", event).Warn("Failed to handle API event") diff --git a/internal/services/userevents/service_handle_event_error_test.go b/internal/services/userevents/service_handle_event_error_test.go index 932b8348..38ba5122 100644 --- a/internal/services/userevents/service_handle_event_error_test.go +++ b/internal/services/userevents/service_handle_event_error_test.go @@ -30,6 +30,7 @@ import ( "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/proton-bridge/v3/internal/events" "github.com/ProtonMail/proton-bridge/v3/internal/events/mocks" + "github.com/ProtonMail/proton-bridge/v3/internal/sentry" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" ) @@ -49,6 +50,7 @@ func TestServiceHandleEventError_SubscriberEventUnwrapping(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) lastEventID := "PrevEvent" @@ -87,6 +89,7 @@ func TestServiceHandleEventError_BadEventPutsServiceOnPause(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) service.Resume() lastEventID := "PrevEvent" @@ -121,6 +124,7 @@ func TestServiceHandleEventError_BadEventFromPublishTimeout(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) lastEventID := "PrevEvent" event := proton.Event{EventID: "MyEvent"} @@ -152,6 +156,7 @@ func TestServiceHandleEventError_NoBadEventCheck(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) lastEventID := "PrevEvent" event := proton.Event{EventID: "MyEvent"} @@ -178,6 +183,7 @@ func TestServiceHandleEventError_JsonUnmarshalEventProducesUncategorizedErrorEve time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) lastEventID := "PrevEvent" event := proton.Event{EventID: "MyEvent"} diff --git a/internal/services/userevents/service_handle_event_test.go b/internal/services/userevents/service_handle_event_test.go index 3f3214b2..c9d587ae 100644 --- a/internal/services/userevents/service_handle_event_test.go +++ b/internal/services/userevents/service_handle_event_test.go @@ -28,6 +28,7 @@ import ( "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/proton-bridge/v3/internal/events" "github.com/ProtonMail/proton-bridge/v3/internal/events/mocks" + "github.com/ProtonMail/proton-bridge/v3/internal/sentry" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" ) @@ -69,6 +70,7 @@ func TestServiceHandleEvent_CheckEventCategoriesHandledInOrder(t *testing.T) { 10*time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscription := NewCallbackSubscriber("test", EventHandler{ @@ -130,6 +132,7 @@ func TestServiceHandleEvent_CheckEventFailureCausesError(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscription := NewCallbackSubscriber("test", EventHandler{ @@ -168,6 +171,7 @@ func TestServiceHandleEvent_CheckEventFailureCausesErrorParallel(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscription := NewCallbackSubscriber("test", EventHandler{ diff --git a/internal/services/userevents/service_test.go b/internal/services/userevents/service_test.go index 336aada7..f501023d 100644 --- a/internal/services/userevents/service_test.go +++ b/internal/services/userevents/service_test.go @@ -27,6 +27,7 @@ import ( "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/proton-bridge/v3/internal/events" mocks2 "github.com/ProtonMail/proton-bridge/v3/internal/events/mocks" + "github.com/ProtonMail/proton-bridge/v3/internal/sentry" "github.com/ProtonMail/proton-bridge/v3/internal/services/orderedtasks" "github.com/ProtonMail/proton-bridge/v3/internal/services/userevents/mocks" "github.com/golang/mock/gomock" @@ -76,6 +77,7 @@ func TestService_EventIDLoadStore(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) _, err := service.Start(context.Background(), group) @@ -132,6 +134,7 @@ func TestService_RetryEventOnNonCatastrophicFailure(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) service.Subscribe(NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber})) @@ -182,6 +185,7 @@ func TestService_OnBadEventServiceIsPaused(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) // Event publisher expectations. @@ -249,6 +253,7 @@ func TestService_UnsubscribeDuringEventHandlingDoesNotCauseDeadlock(t *testing.T time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscription := NewCallbackSubscriber("foo", EventHandler{MessageHandler: subscriber}) @@ -309,6 +314,7 @@ func TestService_UnsubscribeBeforeHandlingEventIsNotConsideredError(t *testing.T time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscription := NewEventSubscriber("Foo") @@ -369,6 +375,7 @@ func TestService_WaitOnEventPublishAfterPause(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) subscriber.EXPECT().HandleMessageEvents(gomock.Any(), gomock.Eq(messageEvents)).Times(1).DoAndReturn(func(_ context.Context, _ []proton.MessageEvent) error { @@ -442,6 +449,7 @@ func TestService_EventRewind(t *testing.T) { time.Second, async.NoopPanicHandler{}, events.NewNullSubscription(), + sentry.NullSentryReporter{}, ) _, err := service.Start(context.Background(), group) diff --git a/internal/user/user.go b/internal/user/user.go index 41019d76..6a05c5af 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -241,6 +241,7 @@ func newImpl( 5*time.Minute, crashHandler, eventSubscription, + reporter, ) addressMode := usertypes.VaultToAddressMode(encVault.AddressMode())