sqlite

package
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package sqlite provides a SQLite Store implementation for Ledger using Grove ORM.

Index

Constants

This section is empty.

Variables

View Source
var Migrations = migrate.NewGroup("ledger")

Migrations is the grove migration group for the Ledger store (SQLite).

Functions

This section is empty.

Types

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store implements store.Store using SQLite via Grove ORM.

func New

func New(db *grove.DB) *Store

New creates a new SQLite store backed by Grove ORM.

func (*Store) AdvanceSubscriptionPeriod

func (s *Store) AdvanceSubscriptionPeriod(ctx context.Context, subID id.SubscriptionID, from, start, end, at time.Time) (bool, error)

func (*Store) Aggregate

func (s *Store) Aggregate(ctx context.Context, tenantID, appID, featureKey string, period plan.Period) (int64, error)

func (*Store) AggregateMulti

func (s *Store) AggregateMulti(ctx context.Context, tenantID, appID string, featureKeys []string, period plan.Period) (map[string]int64, error)

func (*Store) ApplyCoupon

func (s *Store) ApplyCoupon(ctx context.Context, subID id.SubscriptionID, couponID id.CouponID) error

ApplyCoupon is a low-level operation kept for callers and tests that need it separately. Engine code redeems through RedeemCoupon, which records the application and increments the redemption count as one unit.

func (*Store) ArchiveFeature

func (s *Store) ArchiveFeature(ctx context.Context, featureID id.FeatureID) error

func (*Store) ArchivePlan

func (s *Store) ArchivePlan(ctx context.Context, planID id.PlanID) error

func (*Store) CancelSubscription

func (s *Store) CancelSubscription(ctx context.Context, subID id.SubscriptionID, immediately bool) (time.Time, error)

func (*Store) ChangeSubscriptionPlan

func (s *Store) ChangeSubscriptionPlan(ctx context.Context, subID id.SubscriptionID, planID id.PlanID, quantity map[string]int64) (bool, error)

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection.

func (*Store) CreateCoupon

func (s *Store) CreateCoupon(ctx context.Context, c *coupon.Coupon) error

func (*Store) CreateFeature

func (s *Store) CreateFeature(ctx context.Context, f *feature.Feature) error

func (*Store) CreateInvoice

func (s *Store) CreateInvoice(ctx context.Context, inv *invoice.Invoice) error

func (*Store) CreatePlan

func (s *Store) CreatePlan(ctx context.Context, p *plan.Plan) error

func (*Store) CreateSubscription

func (s *Store) CreateSubscription(ctx context.Context, sub *subscription.Subscription) error

func (*Store) DB

func (s *Store) DB() *grove.DB

DB returns the underlying grove database for direct access.

func (*Store) DeleteCoupon

func (s *Store) DeleteCoupon(ctx context.Context, couponID id.CouponID) error

func (*Store) DeleteFeature

func (s *Store) DeleteFeature(ctx context.Context, featureID id.FeatureID) error

func (*Store) DeletePlan

func (s *Store) DeletePlan(ctx context.Context, planID id.PlanID) error

func (*Store) EnactSubscriptionCancel

func (s *Store) EnactSubscriptionCancel(ctx context.Context, subID id.SubscriptionID, at time.Time) (bool, error)

func (*Store) EndSubscriptionTrial

func (s *Store) EndSubscriptionTrial(ctx context.Context, subID id.SubscriptionID, at time.Time) (bool, error)

func (*Store) GetActiveSubscription

func (s *Store) GetActiveSubscription(ctx context.Context, tenantID, appID string) (*subscription.Subscription, error)

func (*Store) GetCached

func (s *Store) GetCached(ctx context.Context, tenantID, appID, featureKey string) (*entitlement.Result, error)

func (*Store) GetCoupon

func (s *Store) GetCoupon(ctx context.Context, code, appID string) (*coupon.Coupon, error)

func (*Store) GetCouponByID

func (s *Store) GetCouponByID(ctx context.Context, couponID id.CouponID) (*coupon.Coupon, error)

func (*Store) GetFeature

func (s *Store) GetFeature(ctx context.Context, featureID id.FeatureID) (*feature.Feature, error)

func (*Store) GetFeatureByKey

func (s *Store) GetFeatureByKey(ctx context.Context, key, appID string) (*feature.Feature, error)

func (*Store) GetInvoice

func (s *Store) GetInvoice(ctx context.Context, invID id.InvoiceID) (*invoice.Invoice, error)

func (*Store) GetInvoiceByPeriod

func (s *Store) GetInvoiceByPeriod(ctx context.Context, tenantID, appID string, periodStart, periodEnd time.Time) (*invoice.Invoice, error)

func (*Store) GetPlan

func (s *Store) GetPlan(ctx context.Context, planID id.PlanID) (*plan.Plan, error)

func (*Store) GetPlanBySlug

func (s *Store) GetPlanBySlug(ctx context.Context, slug, appID string) (*plan.Plan, error)

func (*Store) GetSubscription

func (s *Store) GetSubscription(ctx context.Context, subID id.SubscriptionID) (*subscription.Subscription, error)

func (*Store) IncrementCouponRedemptions

func (s *Store) IncrementCouponRedemptions(ctx context.Context, couponID id.CouponID) error

IncrementCouponRedemptions is a low-level operation kept for callers and tests that need it separately. Engine code redeems through RedeemCoupon, which increments the count and records the application as one unit.

func (*Store) IngestBatch

func (s *Store) IngestBatch(ctx context.Context, events []*meter.UsageEvent) error

func (*Store) Invalidate

func (s *Store) Invalidate(ctx context.Context, tenantID, appID string) error

func (*Store) InvalidateFeature

func (s *Store) InvalidateFeature(ctx context.Context, tenantID, appID, featureKey string) error

func (*Store) ListAppliedCoupons

func (s *Store) ListAppliedCoupons(ctx context.Context, subID id.SubscriptionID) ([]*coupon.Coupon, error)

func (*Store) ListCoupons

func (s *Store) ListCoupons(ctx context.Context, appID string, opts coupon.ListOpts) ([]*coupon.Coupon, error)

func (*Store) ListDueSubscriptions

func (s *Store) ListDueSubscriptions(ctx context.Context, opts subscription.DueOpts) ([]*subscription.Subscription, error)

func (*Store) ListFeatures

func (s *Store) ListFeatures(ctx context.Context, appID string, opts feature.ListOpts) ([]*feature.Feature, error)

func (*Store) ListGlobalFeatures

func (s *Store) ListGlobalFeatures(ctx context.Context, opts feature.ListOpts) ([]*feature.Feature, error)

func (*Store) ListInvoices

func (s *Store) ListInvoices(ctx context.Context, tenantID, appID string, opts invoice.ListOpts) ([]*invoice.Invoice, error)

func (*Store) ListOverdueInvoices

func (s *Store) ListOverdueInvoices(ctx context.Context, opts invoice.OverdueOpts) ([]*invoice.Invoice, error)

func (*Store) ListPendingInvoices

func (s *Store) ListPendingInvoices(ctx context.Context, appID string) ([]*invoice.Invoice, error)

func (*Store) ListPlans

func (s *Store) ListPlans(ctx context.Context, appID string, opts plan.ListOpts) ([]*plan.Plan, error)

func (*Store) ListSubscriptions

func (s *Store) ListSubscriptions(ctx context.Context, tenantID, appID string, opts subscription.ListOpts) ([]*subscription.Subscription, error)

func (*Store) MarkInvoicePaid

func (s *Store) MarkInvoicePaid(ctx context.Context, invID id.InvoiceID, paidAt time.Time, paymentRef string) error

func (*Store) MarkInvoicePastDue

func (s *Store) MarkInvoicePastDue(ctx context.Context, invID id.InvoiceID, at time.Time) (bool, error)

func (*Store) MarkInvoiceVoided

func (s *Store) MarkInvoiceVoided(ctx context.Context, invID id.InvoiceID, reason string) error

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context) error

Migrate creates the required tables and indexes using the grove orchestrator.

func (*Store) PauseSubscription

func (s *Store) PauseSubscription(ctx context.Context, subID id.SubscriptionID, at time.Time) (bool, error)

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping checks database connectivity.

func (*Store) PurgeUsage

func (s *Store) PurgeUsage(ctx context.Context, before time.Time) (int64, error)

func (*Store) QueryUsage

func (s *Store) QueryUsage(ctx context.Context, tenantID, appID string, opts meter.QueryOpts) ([]*meter.UsageEvent, error)

func (*Store) RedeemCoupon

func (s *Store) RedeemCoupon(ctx context.Context, subID id.SubscriptionID, couponID id.CouponID) error

RedeemCoupon records a coupon's application to a subscription and increments its redemption count in one transaction, committing only when both writes land. The cap (MaxRedemptions) is enforced by a conditional UPDATE - `times_redeemed = times_redeemed + 1 WHERE id = ? AND (max_redemptions <= 0 OR times_redeemed < max_redemptions)` - rather than a read of TimesRedeemed followed by a separate write, so two concurrent transactions racing the same coupon toward its cap cannot both read the count before either writes it: the second transaction's UPDATE blocks until the first commits or rolls back, and its WHERE then re-evaluates against whatever count the first one left behind. SQLite has no row-level locking - a writing transaction holds a database-wide RESERVED/EXCLUSIVE lock, not a lock on this one row - but the effect on this method is the same: the second UPDATE waits rather than racing the first's read.

The INSERT of the application row is deliberately the FIRST statement in the transaction, not a SELECT. A transaction that opens with a read starts deferred and only holds a SHARED lock; if it later needs to write, SQLite must upgrade that SHARED lock to RESERVED, and two transactions racing the same coupon can both be sitting on SHARED when they attempt that upgrade at the same time. SQLite treats that as an unresolvable mutual wait and fails it immediately with SQLITE_BUSY rather than queuing it behind busy_timeout - regardless of how long busy_timeout is set to, and regardless of DSN flags. Opening with a write instead means the RESERVED lock is acquired up front, with no later upgrade to fail. This is why the coupon's existence is no longer confirmed with a SELECT before the INSERT: that SELECT was the read this method used to open with.

Unknown coupon and exhausted cap now share one signal - the conditional UPDATE affects zero rows - and are told apart only after the fact: zero rows means either the coupon was never there or its cap was reached, and an existence check run at that point (with the write lock already held by the INSERT above, so it cannot itself trigger a lock-upgrade BUSY) decides which.

func (*Store) ResumeSubscription

func (s *Store) ResumeSubscription(ctx context.Context, subID id.SubscriptionID, r subscription.Resume) (bool, error)

func (*Store) SetCached

func (s *Store) SetCached(ctx context.Context, tenantID, appID, featureKey string, result *entitlement.Result, ttl time.Duration) error

func (*Store) SetInvoiceProvider

func (s *Store) SetInvoiceProvider(ctx context.Context, invID id.InvoiceID, providerID, providerName string) error

func (*Store) SetSubscriptionProvider

func (s *Store) SetSubscriptionProvider(ctx context.Context, subID id.SubscriptionID, providerID, providerName string) error

func (*Store) UpdateCoupon

func (s *Store) UpdateCoupon(ctx context.Context, c *coupon.Coupon) error

func (*Store) UpdateFeature

func (s *Store) UpdateFeature(ctx context.Context, f *feature.Feature) error

func (*Store) UpdateInvoice

func (s *Store) UpdateInvoice(ctx context.Context, inv *invoice.Invoice) error

func (*Store) UpdatePlan

func (s *Store) UpdatePlan(ctx context.Context, p *plan.Plan) error

func (*Store) UpdateSubscription

func (s *Store) UpdateSubscription(ctx context.Context, sub *subscription.Subscription) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL