Skip to content

refactor: make subscription sync a service#3700

Merged
turip merged 1 commit into
mainfrom
refactor/make-subscription-sync-a-service
Dec 15, 2025
Merged

refactor: make subscription sync a service#3700
turip merged 1 commit into
mainfrom
refactor/make-subscription-sync-a-service

Conversation

@turip
Copy link
Copy Markdown
Member

@turip turip commented Dec 15, 2025

Overview

This refactor just moves subscriptionsync into a service, as we will have to have DB layer for sync avoidance.

It also corrects the typo of missing h from Synchornize.

Notes for reviewer

Summary by CodeRabbit

Release Notes

  • Refactor
    • Reorganized billing subscription synchronization module with updated service architecture and improved internal component organization.

✏️ Tip: You can customize this high-level summary in your review settings.

@turip turip requested a review from a team as a code owner December 15, 2025 15:07
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 15, 2025

📝 Walkthrough

Walkthrough

Large-scale refactor replacing a handler-based architecture with a service-based abstraction for billing subscription synchronization. Involves package restructuring from billing/worker/subscription to billing/worker/subscriptionsync/service, correcting method names (e.g., SyncronizeSubscriptionSynchronizeSubscription), and comprehensively updating all dependent wiring, constructors, and call sites across the codebase.

Changes

Cohort / File(s) Change Summary
Core Service/Interface Definition
openmeter/billing/worker/subscriptionsync/service.go
New interfaces Service, SyncService, and EventHandler defined to abstract subscription synchronization and event handling contracts
Service Implementation
openmeter/billing/worker/subscriptionsync/service/service.go
New Service struct with constructor New(config Config) introduced; includes Config and FeatureFlags types for dependency injection and compile-time interface assertion
Sync Logic Refactor
openmeter/billing/worker/subscriptionsync/service/sync.go
Refactored from handler-based to service-based receiver; removed old Handler and Config types; renamed methods (SyncronizeSubscriptionSynchronizeSubscription, etc.); updated all field references and internal call sites
Event Handlers
openmeter/billing/worker/subscriptionsync/service/handlers.go
Receiver changed from *Handler to *Service; updated HandleInvoiceCreation to accept *billing.InvoiceCreatedEvent instead of billing.EventInvoice; corrected method names and references
Reconciler Update
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go
Updated to use subscriptionsync.Service instead of *Handler; corrected method name in reconciliation logic (SyncronizeSubscriptionSynchronizeSubscription)
Package Reorganization
openmeter/billing/worker/subscriptionsync/service/* (feehelper.go, invoiceupdate.go, patch.go, phaseiterator.go, phaseiterator_test.go, suitebase_test.go, sync_test.go, syncbillinganchor_test.go)
Package namespace changed from billingworkersubscription to service; all receivers and references updated consistently; test names corrected
Validator Update
openmeter/billing/validators/customer/customer.go
Constructor signature updated to accept subscriptionsync.Service instead of *billingworkersubscription.Handler; nil-check added; method call corrected to SynchronizeSubscription
Core Wiring (App Layer)
app/common/billing.go
New constructor NewBillingSubscriptionSyncService() introduced; NewBillingSubscriptionReconciler() updated to accept subscriptionsync.Service and return *reconciler.Reconciler; old NewBillingSubscriptionHandler() removed
Billing Worker Wiring
app/common/openmeter_billingworker.go
NewBillingWorkerOptions() parameter updated from subsSyncHandler *Handler to subscriptionSyncService subscriptionsync.Service; field renamed and type updated accordingly; wiring set updated to use new service constructor
Worker Implementation
openmeter/billing/worker/worker.go
WorkerOptions field renamed from BillingSubscriptionSyncHandler to BillingSubscriptionSync and type changed to subscriptionsync.Service; validation and initialization updated; event handler calls use corrected method names
Generated Wiring (CLI)
cmd/billing-worker/wire_gen.go, cmd/jobs/internal/wire_gen.go
Service instantiation and wiring updated to use NewBillingSubscriptionSyncService instead of NewBillingSubscriptionHandler; parameter passing reflects new service type
Wire Setup Files
cmd/jobs/internal/wire.go, cmd/jobs/billing/subscriptionsync/sync.go, cmd/jobs/quickstart/cronjobs.go
Import aliases updated to reference subscriptionsync/reconciler instead of billing/worker/subscription; constructor calls and type references updated in wiring and call sites
Test Integration
test/billing/subscription_test.go, test/subscription/framework_test.go, test/subscription/scenario_firstofmonth_test.go
Field renamed from SubscriptionSyncHandler to SubscriptionSyncService; constructor usage switched from billingworkersubscription.New() to subscriptionsyncservice.New(); method calls corrected (SyncronizeSubscriptionSynchronizeSubscription)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review focus areas:
    • Verify that all receiver type changes from *Handler to *Service are applied consistently across sync.go and related files
    • Confirm method name corrections (SyncronizeSubscriptionSynchronizeSubscription) are complete and spelled correctly everywhere
    • Validate wiring changes in DI setup across app/common/, cmd/billing-worker/, and cmd/jobs/internal/ reflect the new constructor and parameter types
    • Check that test fixtures and integration tests correctly initialize the new Service with proper config validation
    • Ensure event handler signature updates (especially HandleInvoiceCreation parameter type change) are consistently applied

Possibly related PRs

  • feat: subscription sync job #2492 — Original PR introducing NewBillingSubscriptionHandler and related wiring; this PR refactors and replaces that handler-based approach with the new service-based architecture.
  • fix: decrease subs sync noise #3142 — Modifies log level inside subscription synchronization logic; directly related to the methods being renamed/reorganized in this refactor.
  • feat: collect usage on draft invoices #2834 — Adds SyncronizeSubscriptionAndInvoiceCustomer method that this PR renames to SynchronizeSubscriptionAndInvoiceCustomer and reorganizes into the new service structure.

Suggested labels

kind/refactor, area/billing

Suggested reviewers

  • chrisgacsal
  • tothandras

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: refactoring subscription sync functionality into a service-based architecture, which is the primary intent of this PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/make-subscription-sync-a-service

Comment @coderabbitai help to get the list of available commands and usage tips.

@turip turip added the release-note/ignore Ignore this change when generating release notes label Dec 15, 2025
@turip turip force-pushed the refactor/make-subscription-sync-a-service branch from 3b2d044 to 8eb6a42 Compare December 15, 2025 15:08
@turip turip enabled auto-merge (squash) December 15, 2025 15:09
@turip turip merged commit d2406bd into main Dec 15, 2025
25 of 26 checks passed
@turip turip deleted the refactor/make-subscription-sync-a-service branch December 15, 2025 15:12
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (9)
openmeter/billing/worker/subscriptionsync/service/feehelper.go (1)

1-71: Helpers look good after the move; just note setIfDoesNotEqual’s pointer expectations

The move into the service package keeps the flat-fee helpers and the small typeWithEqual/setIfDoesNotEqual utility intact, so behaviour should be unchanged.

One small thing to keep in mind: setIfDoesNotEqual assumes both existing and wasChange are non‑nil, and will panic if either is nil. That’s totally fine if callers always pass real pointers, but it might be worth either:

  • adding a short comment about that precondition, or
  • adding a defensive if existing == nil || wasChange == nil { return } if you expect more generic reuse later.
openmeter/billing/worker/subscriptionsync/service/suitebase_test.go (1)

35-59: Test suite migration to Service looks good; be mindful of shared feature flags

The suite’s move from holding a *Handler to a *Service built via New(Config{...}) is consistent with the new implementation, and the rest of the helpers still compose fine around it.

One minor thing to keep in mind: AfterTest and enableProrating mutate s.Service.featureFlags on a shared Service instance. That’s totally reasonable for this style of integration suite, but if you ever start running these tests (or subtests) in parallel, you’ll want either a per‑test Service instance or some light synchronization around toggling those flags to avoid data races.

Also applies to: 118-118, 161-164

openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (1)

104-127: Consider using clock.Now() instead of time.Now() for reconciliation

Here you pass time.Now() into SynchronizeSubscription, while ListSubscriptions above already relies on clock.Now() for its time window. For consistency and better testability (respecting frozen/adjusted time), it’d be cleaner to use clock.Now() here too:

- return r.subscriptionSync.SynchronizeSubscription(ctx, subsView, time.Now())
+ return r.subscriptionSync.SynchronizeSubscription(ctx, subsView, clock.Now())

Totally fine to keep as-is if there’s a specific reason to use wall-clock time, but aligning with clock would match the rest of this component.

openmeter/billing/validators/customer/customer.go (1)

17-30: Validator wiring + sync usage looks good (tiny optional tweak)

The move to a subscriptionsync.Service dependency and the extra nil-check in NewValidator are solid and keep the dependency graph explicit. The sync call in ValidateDeleteCustomer is also wired correctly to SynchronizeSubscription.

If you ever want to make time-dependent tests a bit more deterministic, you could capture asOf := time.Now() once before the for loop and reuse it for each SynchronizeSubscription call, instead of calling time.Now() per item—but that’s purely cosmetic.

Also applies to: 36-37, 58-67

openmeter/billing/worker/worker.go (1)

67-69: Small inconsistency in error message.

The error message still says "billing subscription sync handler is required" but the field is now BillingSubscriptionSync (a service, not a handler). Minor nit, but worth fixing for consistency.

 	if w.BillingSubscriptionSync == nil {
-		return fmt.Errorf("billing subscription sync handler is required")
+		return fmt.Errorf("billing subscription sync service is required")
 	}
openmeter/billing/worker/subscriptionsync/service/handlers.go (1)

16-18: Truncated comment.

The comment on lines 16-17 appears incomplete: "it will make sure that we synchronize the" - it seems like something is missing at the end.

Consider completing the comment to explain what exactly is being synchronized, for example:

 // HandleCancelledEvent is a handler for the subscription cancel event, it will make sure that
-// we synchronize the
+// we synchronize the subscription's billing state up to the cancellation date.
 func (s *Service) HandleCancelledEvent(ctx context.Context, event *subscription.CancelledEvent) error {
openmeter/billing/worker/subscriptionsync/service/sync.go (3)

619-635: discountsToBillingDiscounts doesn't use receiver.

This method also doesn't use the s receiver - it's a pure transformation function. Same reasoning applies as above: keeping it as a method maintains API consistency.

If you want to be pedantic about it, this could be a package-level function since it doesn't use any Service state. But it's fine as-is for consistency.


714-786: getPatchesForExistingLine is well-documented.

The inline comment on line 724 still says "resyncronization" - might want to fix that typo too while you're at it since you're fixing "Syncronize" → "Synchronize" elsewhere.

 	// Manual edits prevent resyncronization so that we preserve the user intent
+	// Manual edits prevent resynchronization so that we preserve the user intent

755-756: Comment typo: "syncronizing".

Line 755 has a comment with "syncronizing" - same typo being fixed elsewhere in this PR.

-	// Let's handle the flat fee prorating (e.g. syncronizing the amount maybe in retrospect)
+	// Let's handle the flat fee prorating (e.g. synchronizing the amount maybe in retrospect)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f54d506 and 8eb6a42.

📒 Files selected for processing (25)
  • app/common/billing.go (3 hunks)
  • app/common/openmeter_billingworker.go (3 hunks)
  • cmd/billing-worker/wire_gen.go (2 hunks)
  • cmd/jobs/billing/subscriptionsync/sync.go (3 hunks)
  • cmd/jobs/internal/wire.go (3 hunks)
  • cmd/jobs/internal/wire_gen.go (7 hunks)
  • cmd/jobs/quickstart/cronjobs.go (2 hunks)
  • openmeter/billing/validators/customer/customer.go (3 hunks)
  • openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (4 hunks)
  • openmeter/billing/worker/subscriptionsync/service.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/feehelper.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/handlers.go (6 hunks)
  • openmeter/billing/worker/subscriptionsync/service/invoiceupdate.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/patch.go (2 hunks)
  • openmeter/billing/worker/subscriptionsync/service/phaseiterator.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/phaseiterator_test.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/service.go (1 hunks)
  • openmeter/billing/worker/subscriptionsync/service/suitebase_test.go (6 hunks)
  • openmeter/billing/worker/subscriptionsync/service/sync.go (31 hunks)
  • openmeter/billing/worker/subscriptionsync/service/sync_test.go (60 hunks)
  • openmeter/billing/worker/subscriptionsync/service/syncbillinganchor_test.go (3 hunks)
  • openmeter/billing/worker/worker.go (7 hunks)
  • test/billing/subscription_test.go (4 hunks)
  • test/subscription/framework_test.go (4 hunks)
  • test/subscription/scenario_firstofmonth_test.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.

Performance should be a priority in critical code paths. Anything related to event ingestion, message processing, database operations (regardless of database) should be vetted for potential performance bottlenecks.

Files:

  • openmeter/billing/worker/subscriptionsync/service/invoiceupdate.go
  • openmeter/billing/worker/subscriptionsync/service/phaseiterator_test.go
  • openmeter/billing/worker/subscriptionsync/service/patch.go
  • test/subscription/framework_test.go
  • openmeter/billing/worker/subscriptionsync/service.go
  • openmeter/billing/worker/subscriptionsync/service/phaseiterator.go
  • openmeter/billing/validators/customer/customer.go
  • cmd/jobs/quickstart/cronjobs.go
  • test/billing/subscription_test.go
  • openmeter/billing/worker/subscriptionsync/service/feehelper.go
  • openmeter/billing/worker/subscriptionsync/service/suitebase_test.go
  • openmeter/billing/worker/subscriptionsync/service/service.go
  • cmd/jobs/internal/wire.go
  • openmeter/billing/worker/subscriptionsync/service/sync_test.go
  • test/subscription/scenario_firstofmonth_test.go
  • cmd/billing-worker/wire_gen.go
  • openmeter/billing/worker/subscriptionsync/service/syncbillinganchor_test.go
  • cmd/jobs/billing/subscriptionsync/sync.go
  • app/common/billing.go
  • app/common/openmeter_billingworker.go
  • openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go
  • openmeter/billing/worker/worker.go
  • cmd/jobs/internal/wire_gen.go
  • openmeter/billing/worker/subscriptionsync/service/sync.go
  • openmeter/billing/worker/subscriptionsync/service/handlers.go
**/*_test.go

⚙️ CodeRabbit configuration file

**/*_test.go: Make sure the tests are comprehensive and cover the changes. Keep a strong focus on unit tests and in-code integration tests.
When appropriate, recommend e2e tests for critical changes.

Files:

  • openmeter/billing/worker/subscriptionsync/service/phaseiterator_test.go
  • test/subscription/framework_test.go
  • test/billing/subscription_test.go
  • openmeter/billing/worker/subscriptionsync/service/suitebase_test.go
  • openmeter/billing/worker/subscriptionsync/service/sync_test.go
  • test/subscription/scenario_firstofmonth_test.go
  • openmeter/billing/worker/subscriptionsync/service/syncbillinganchor_test.go
🧠 Learnings (3)
📚 Learning: 2025-03-07T12:17:43.129Z
Learnt from: GAlexIHU
Repo: openmeterio/openmeter PR: 2383
File: openmeter/entitlement/metered/lateevents_test.go:37-45
Timestamp: 2025-03-07T12:17:43.129Z
Learning: In the OpenMeter codebase, test files like `openmeter/entitlement/metered/lateevents_test.go` may use variables like `meterSlug` and `namespace` without explicit declarations visible in the same file. This appears to be an accepted pattern in their test structure.

Applied to files:

  • openmeter/billing/worker/subscriptionsync/service/phaseiterator_test.go
  • openmeter/billing/worker/subscriptionsync/service/sync_test.go
  • openmeter/billing/worker/subscriptionsync/service/sync.go
📚 Learning: 2025-04-21T08:32:31.689Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2699
File: openmeter/productcatalog/planaddon/service/service_test.go:210-211
Timestamp: 2025-04-21T08:32:31.689Z
Learning: In `productcatalog.UsageBasedRateCard`, the `BillingCadence` field is a non-pointer `isodate.Period`, while in `productcatalog.FlatFeeRateCard`, `BillingCadence` is a pointer type (`*isodate.Period`). This means `MonthPeriod` should be used directly for `UsageBasedRateCard` (not `&MonthPeriod`).

Applied to files:

  • test/subscription/framework_test.go
  • openmeter/billing/worker/subscriptionsync/service/sync.go
📚 Learning: 2025-08-29T12:31:52.802Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 3291
File: app/common/customer.go:88-89
Timestamp: 2025-08-29T12:31:52.802Z
Learning: In Go projects using Google's wire dependency injection framework, named types (without =) should be used instead of type aliases (with =) to work around wire limitations. For example, use `type CustomerSubjectValidatorHook customerservicehooks.SubjectValidatorHook` instead of `type CustomerSubjectValidatorHook = customerservicehooks.SubjectValidatorHook` when wire is involved.

Applied to files:

  • openmeter/billing/validators/customer/customer.go
🧬 Code graph analysis (19)
openmeter/billing/worker/subscriptionsync/service/patch.go (3)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/billing/invoicelinesplitgroup.go (1)
  • LineOrHierarchy (281-285)
test/subscription/framework_test.go (1)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service.go (3)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
openmeter/subscription/events.go (2)
  • CancelledEvent (104-104)
  • SubscriptionSyncEvent (199-201)
openmeter/billing/events.go (1)
  • InvoiceCreatedEvent (93-95)
openmeter/billing/validators/customer/customer.go (4)
openmeter/billing/validators/subscription/validator.go (2)
  • Validator (15-18)
  • NewValidator (20-28)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/subscription/service.go (1)
  • Service (37-40)
cmd/jobs/quickstart/cronjobs.go (1)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (1)
  • ReconcilerAllInput (130-130)
test/billing/subscription_test.go (2)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (3)
  • Service (55-62)
  • New (64-76)
  • Config (20-27)
openmeter/billing/worker/subscriptionsync/service/suitebase_test.go (1)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
cmd/jobs/internal/wire.go (2)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (1)
  • Reconciler (23-28)
app/common/billing.go (1)
  • NewBillingSubscriptionSyncService (124-132)
openmeter/billing/worker/subscriptionsync/service/sync_test.go (3)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
pkg/clock/clock.go (1)
  • Now (14-21)
cmd/billing-worker/wire_gen.go (2)
app/common/billing.go (1)
  • NewBillingSubscriptionSyncService (124-132)
app/common/openmeter_billingworker.go (1)
  • NewBillingWorkerOptions (73-98)
openmeter/billing/worker/subscriptionsync/service/syncbillinganchor_test.go (3)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/testutils/time.go (1)
  • GetRFC3339Time (8-15)
cmd/jobs/billing/subscriptionsync/sync.go (1)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (2)
  • ReconcilerListSubscriptionsInput (70-74)
  • ReconcilerAllInput (130-130)
app/common/billing.go (3)
openmeter/billing/validators/customer/customer.go (1)
  • NewValidator (17-31)
openmeter/billing/worker/subscriptionsync/service/service.go (3)
  • Service (55-62)
  • New (64-76)
  • Config (20-27)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (3)
  • Reconciler (23-28)
  • NewReconciler (57-68)
  • ReconcilerConfig (30-35)
app/common/openmeter_billingworker.go (4)
app/common/billing.go (3)
  • NewBillingSubscriptionSyncService (124-132)
  • BillingService (45-98)
  • BillingAdapter (35-43)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/billing/worker/worker.go (1)
  • WorkerOptions (21-36)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (2)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/billing/worker/worker.go (5)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/billing/worker/asyncadvance/asyncadvance.go (1)
  • Handler (11-14)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
openmeter/subscription/events.go (4)
  • CancelledEvent (104-104)
  • ContinuedEvent (136-136)
  • UpdatedEvent (168-172)
  • SubscriptionSyncEvent (199-201)
cmd/jobs/internal/wire_gen.go (3)
app/common/billing.go (2)
  • NewBillingSubscriptionSyncService (124-132)
  • NewBillingSubscriptionReconciler (115-122)
app/common/notification.go (3)
  • NewNotificationWebhookHandler (126-151)
  • NewNotificationEventHandler (64-97)
  • NewNotificationService (99-118)
openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (1)
  • Reconciler (23-28)
openmeter/billing/worker/subscriptionsync/service/sync.go (3)
openmeter/billing/worker/subscriptionsync/service.go (1)
  • Service (11-14)
openmeter/billing/worker/subscriptionsync/service/invoiceupdate.go (1)
  • NewInvoiceUpdater (24-29)
openmeter/billing/worker/subscriptionsync/service/phaseiterator.go (1)
  • NewPhaseIterator (98-122)
openmeter/billing/worker/subscriptionsync/service/handlers.go (6)
openmeter/billing/worker/subscriptionsync/service/service.go (1)
  • Service (55-62)
openmeter/subscription/events.go (1)
  • CancelledEvent (104-104)
pkg/clock/clock.go (1)
  • Now (14-21)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
openmeter/billing/events.go (1)
  • InvoiceCreatedEvent (93-95)
pkg/models/id.go (1)
  • NamespacedID (7-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: E2E
  • GitHub Check: Quickstart
  • GitHub Check: Lint
  • GitHub Check: Code Generators
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (39)
openmeter/billing/worker/subscriptionsync/service/patch.go (1)

96-185: getDeletePatchesForLine hanging off Service looks consistent with prior behaviour

The receiver switch to *Service is clean, and the logic for both LineOrHierarchyTypeLine and LineOrHierarchyTypeHierarchy is unchanged:

  • Single lines: still skip AnnotationSubscriptionSyncIgnore and emit one line_delete patch.
  • Hierarchies: still optionally emit a split_line_group_delete when the group itself isn’t deleted, then emit line_delete patches for each non‑deleted child line.

The default branch returning an error for unknown LineOrHierarchy types is also preserved. No issues from the refactor here.

app/common/openmeter_billingworker.go (1)

16-94: Billing worker wiring to subscriptionsync.Service looks correct

The switch from a concrete handler to subscriptionsync.Service is wired cleanly:

  • Importing billing/worker/subscriptionsync and adding NewBillingSubscriptionSyncService to the BillingWorker wire set keeps DI centralized.
  • NewBillingWorkerOptions now takes subscriptionSyncService subscriptionsync.Service and threads it through to billingworker.WorkerOptions.BillingSubscriptionSync, alongside the existing router, event bus, billing, adapter, and subscription service fields.

Assuming the wire-generated files have been regenerated, this should keep the worker behaviour identical while giving you the new service abstraction.

openmeter/billing/worker/subscriptionsync/service/phaseiterator.go (1)

1-24: Phase iterator now correctly lives in the service package

Only the package declaration moved; the iterator, period math, and tracing logic are unchanged and are still tested via phaseiterator_test.go. The refactor keeps behaviour intact while aligning namespaces with the new subscriptionsync service layout.

openmeter/billing/worker/subscriptionsync/service/invoiceupdate.go (1)

1-398: Invoice updater package move aligns with the new service layout

The InvoiceUpdater remains functionally the same: it still

  • parses linePatch values into creates/updates/deletes and split-group changes,
  • updates mutable invoices via UpdateInvoice and optionally deletes now-empty invoices, and
  • records validation issues instead of mutating immutable invoices, using the shared flat-fee helpers.

Moving this into the service package just tightens cohesion around subscriptionsync without changing behaviour.

openmeter/billing/worker/subscriptionsync/service/phaseiterator_test.go (1)

1-1277: Phase iterator tests still give strong coverage; package rename is fine

Renaming the test package to service lines it up with the refactored implementation, and the table‑driven tests still cover a wide range of cadence, phase end, cancellation, flat‑fee vs usage‑based, and billing‑anchor scenarios. That should give good confidence that the iterator continues to behave the same under the new package structure.

test/subscription/framework_test.go (1)

20-21: Subscription framework tests now cleanly depend on subscriptionsync.Service

The switch from a concrete worker handler to the subscriptionsync.Service interface in testDeps looks solid:

  • You construct the concrete service via subscriptionsyncservice.New with the same core dependencies (billing service + adapter, subscription service, logger, tracer) that the handler previously used.
  • testDeps now exposes this as subscriptionSyncService subscriptionsync.Service, which keeps the tests aligned with the new abstraction without over‑coupling them to the implementation type.

This should preserve the existing behaviour of the subscription tests while matching the new service-based design.

Also applies to: 33-42, 101-107, 145-154

openmeter/billing/worker/subscriptionsync/service/syncbillinganchor_test.go (1)

141-141: Service-based sync usage looks good

Switching the tests to s.Service.SynchronizeSubscription keeps the behavior the same and matches the new service abstraction. No extra plumbing concerns from the test side.

Also applies to: 338-338

cmd/billing-worker/wire_gen.go (1)

284-295: Wiring in subscriptionsyncService is consistent

The new subscriptionsyncService construction and error handling fits the existing Wire-generated pattern, and passing it into NewBillingWorkerOptions lines up with the updated options struct. All good from a wiring/cleanup perspective.

openmeter/billing/worker/subscriptionsync/service/sync_test.go (1)

58-4632: Nice, call sites now consistently go through the sync service

All the updated tests that previously called a handler now use s.Service.SynchronizeSubscription(...) / SynchronizeSubscriptionAndInvoiceCustomer(...), which lines up with the new service abstraction and keeps the test intent clear. Nothing worrying from a correctness or readability angle here.

cmd/jobs/billing/subscriptionsync/sync.go (1)

10-10: CLI now targets the new reconciler API correctly

The job now imports the reconciler package and passes reconciler.ReconcilerListSubscriptionsInput / ReconcilerAllInput with the expected fields. That lines up cleanly with the refactored subscriptionsync reconciler.

Also applies to: 38-42, 77-81

test/subscription/scenario_firstofmonth_test.go (1)

216-216: Tests are wired to the new subscription sync service nicely

Both sync calls now go through tDeps.subscriptionSyncService, matching the new service abstraction while keeping the original test semantics intact. Looks good.

Also applies to: 425-425

openmeter/billing/worker/subscriptionsync/reconciler/reconciler.go (1)

24-35: Good move to depend on the subscriptionsync.Service interface

The reconciler now takes a subscriptionsync.Service via config, with validation ensuring it’s non-nil, and stores it in subscriptionSync. That’s a nice, decoupled dependency and fits well with the new service-based design.

Also applies to: 57-68

cmd/jobs/internal/wire.go (1)

21-21: Wire setup correctly updated for the new sync service + reconciler

The Application struct now depends on *billingworkersubscriptionreconciler.Reconciler, and the provider set includes common.NewBillingSubscriptionSyncService plus common.NewBillingSubscriptionReconciler. That lines up with the new service interface and keeps the DI graph tidy.

Also applies to: 48-48, 68-86

openmeter/billing/worker/subscriptionsync/service/service.go (1)

15-76: Service config + constructor look solid

Nice setup here: Config.Validate enforces all the important deps (billing, subscription, tx, logger, tracer), FeatureFlags is explicit, and the interface assertion guarantees Service actually satisfies subscriptionsync.Service. The New constructor is minimal and clear, so this should be easy to wire and reason about.

openmeter/billing/worker/subscriptionsync/service.go (1)

11-24: Nice, clean service abstraction

Embedding EventHandler and SyncService into Service keeps the API tidy, and the method signatures line up well with the domain events and SubscriptionView. This should make future consumers straightforward to wire.

cmd/jobs/quickstart/cronjobs.go (1)

12-13: Reconciler wiring update looks consistent

The updated import and the BillingSubscriptionReconciler.All call with reconciler.ReconcilerAllInput match the reconciler’s API and keep the cron job behavior unchanged.

Also applies to: 37-40

test/billing/subscription_test.go (1)

17-19: Tests now match the new service abstraction nicely

Switching the suite to depend on subscriptionsync.Service and constructing it via subscriptionsyncservice.New mirrors the production wiring well, and the SynchronizeSubscription call in createCustomerWithSubscription keeps behavior the same with the new API.

Also applies to: 34-35, 45-56, 333-334

app/common/billing.go (2)

18-21: Good move centralizing subscription sync wiring here

Creating subscriptionSyncService via NewBillingSubscriptionSyncService and feeding it into billingcustomer.NewValidator keeps all the billing-related wiring in one place and cleanly exposes subscriptionsync.Service as the dependency for validators. This should make it much easier to evolve the sync layer (e.g., add DB-backed avoidance) without touching callers.

Also applies to: 76-82


115-121: Reconciler + sync service providers are well-shaped for Wire

NewBillingSubscriptionReconciler and NewBillingSubscriptionSyncService line up with the corresponding config structs and return types, and they’re friendly to Wire-style DI. As long as they’re included in the relevant wire sets, this should keep worker wiring straightforward.

Also applies to: 124-131

cmd/jobs/internal/wire_gen.go (4)

1-37: LGTM on the import and package structure changes.

The import path update to subscriptionsync/reconciler aligns with the new package structure. Since this is Wire-generated code, the changes correctly reflect the underlying wire.go configuration.


343-362: Service wiring looks good.

The two-step initialization pattern is clean:

  1. Create the sync service with NewBillingSubscriptionSyncService
  2. Pass it to NewBillingSubscriptionReconciler

This properly separates the sync service from the reconciler, enabling the future database layer for sync avoidance mentioned in the PR objectives.


406-436: Variable rename is fine.

Renaming webhookHandler to handler works and keeps things simpler. The usage in NewNotificationEventHandler and NewNotificationService is consistent.


511-511: Type update aligns with the refactor.

The BillingSubscriptionReconciler field now correctly uses *reconciler.Reconciler from the new package path.

openmeter/billing/worker/worker.go (5)

14-14: Import update looks good.

Clean switch to the new subscriptionsync package.


29-35: Nice simplification of the options struct.

The field rename from BillingSubscriptionSyncHandler to BillingSubscriptionSync is cleaner, and using the subscriptionsync.Service interface is a good abstraction.


74-83: Struct fields updated consistently.

The Worker struct now correctly holds subscriptionSync subscriptionsync.Service and the field ordering is logical.


99-104: Worker initialization is clean.

Fields are assigned correctly from the options.


142-190: Event handlers updated correctly with typo fix.

The method name correction from SyncronizeSubscriptionAndInvoiceCustomer to SynchronizeSubscriptionAndInvoiceCustomer (fixing the missing "h" in "Synchronize") is applied consistently across all call sites. The handler methods (HandleCancelledEvent, HandleSubscriptionSyncEvent, HandleInvoiceCreation) follow a nice naming convention.

openmeter/billing/worker/subscriptionsync/service/handlers.go (4)

1-1: Package rename looks good.

The new service package name is appropriate for the service-based architecture.


24-41: Handler logic is solid.

The cancellation handling logic is correct:

  • First syncs to current time if ActiveTo is nil (returning an error after sync to ensure at least partial state is captured)
  • Otherwise syncs up to the subscription end date

The method name correction to SynchronizeSubscriptionAndInvoiceCustomer is applied correctly.


45-48: Good defensive nil check.

Adding the nil check for event at the start of HandleInvoiceCreation is a nice safety improvement. This prevents potential nil pointer dereferences downstream.


54-78: Invoice creation handler looks correct.

The logic properly:

  1. Skips gathering invoices (line 50-52)
  2. Collects unique affected subscription IDs
  3. Fetches views and synchronizes each subscription

Using clock.Now() as the reference point (lines 73-75) is appropriate since delayed processing might need to provision additional lines.

openmeter/billing/worker/subscriptionsync/service/sync.go (7)

1-1: Package structure looks good.

The service package is well-organized for the new service-based architecture.


43-63: Receiver updated consistently.

The invoicePendingLines method now uses the *Service receiver and trace span naming is updated accordingly.


65-76: HandleSubscriptionSyncEvent is clean.

Proper nil check, fetches the subscription view, and delegates to SynchronizeSubscriptionAndInvoiceCustomer.


78-100: SynchronizeSubscriptionAndInvoiceCustomer looks good.

The trace span name is updated to reflect the corrected method name. The logic flow is clear: sync the subscription, then invoice pending lines.


102-186: SynchronizeSubscription is well-structured.

The main sync logic is clean:

  1. Early return for subscriptions without billables
  2. Gets billing profile for validation
  3. Acquires lock on customer
  4. Lists invoices and builds lookup map
  5. Compares subscription with existing lines
  6. Generates and applies patches

The receiver changes are consistent throughout.


377-420: Annotation checking methods.

These methods (lineOrHierarchyHasAnnotation, lineHasAnnotation, hierarchyHasAnnotation) don't actually use s (the receiver). They could be standalone functions, but keeping them as methods maintains consistency with the rest of the service pattern and allows for potential future enhancements that might need service dependencies.


788-885: getPatchesForExistingHierarchy is comprehensive.

The hierarchy patch logic handles both expansion (e.g., continue subscription) and shrinking (e.g., cancellation) scenarios. The sorting by Period.End ensures the last child is correctly identified. Good handling of edge cases.

Comment thread openmeter/billing/worker/subscriptionsync/service/sync_test.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants