pricing

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package pricing reads a pricing model: the versioned document that says what a unit of every priced metric costs, per platform and resource type. It takes the YAML file an operator writes, checks it against the JSON Schema embedded here, and returns the typed model the rating pass multiplies usage by.

A file becomes a canonical JSON document on the way in, and that document is what a version is stored from. Parse hands it back beside the model and ParseDocument reads it again, so a model that came from a file and one that came back from the database go through one code path: a stored version is held to the schema its file was held to. Every scalar keeps the literal text the file spells it with and every mapping key is written in sorted order, so the same file yields the same bytes however often it is imported. What the database holds is JSONB, which Postgres parses on the way in and writes back with its own key order and number formatting, so a stored version is never the bytes Parse returned: a re-import is compared as a parsed model rather than byte for byte.

Prices never touch float64. A scalar reaches decimal.NewFromString as text, whether the file spells a price as a number or as a string, because a price that went through a float would carry its rounding error into every amount rated from it. The forbidigo rules in .golangci.yml keep it that way.

The normative specification is roadmap/03-phase-3-metering-rating.md, WP 3.5.

Index

Constants

View Source
const (
	// TypeTimeGauge is billed over the time a quantity was held, so the
	// dimension carries a price per unit and hour.
	TypeTimeGauge = "time_gauge"
	// TypeCounter is billed over a quantity the counters pass measured, so the
	// dimension carries a price per unit.
	TypeCounter = "counter"
)

Variables

View Source
var ErrNoModel = errors.New("no pricing model is valid for this period")

ErrNoModel is what ForPeriod returns for a period that begins before every stored version. Nothing prices it, and rating it against no model would bill every metered resource at zero rather than say what is missing.

View Source
var ErrVersionConflict = errors.New(
	"this version is already imported and prices something else, and a corrected price belongs in a new version")

ErrVersionConflict is what Import returns for a version the database already holds under other prices. An invoice names the version it was rated from and has to stay reproducible from it, so a stored version keeps pricing what it priced when the invoice was written.

View Source
var ErrVersionNotFound = errors.New("no pricing model is stored under this version")

ErrVersionNotFound is what ByVersion returns for a version no stored model carries. A correction rates with the version its finalized run recorded, and a version the database does not hold prices nothing, so the correction is refused before it opens a run rather than rated against no model.

Functions

func Import

func Import(ctx context.Context, q *sqlcgen.Queries, m Model, doc []byte) (bool, error)

Import stores one version of the pricing model and reports whether the database already held it. It only ever inserts: the document a version was rated from is the document that stays stored under it, and a price that changes is imported under a new version instead of over an old one.

A version the database does not hold is written and Import returns false. A version it holds is read back and compared against m: equal models make the import a replay, which returns true and leaves the row alone, and unequal ones an error wrapping ErrVersionConflict. A second version claiming the valid_from of a stored one is refused too, because one instant is priced by one version. That collision is its own error rather than a version conflict: the version is new, it is the instant it starts at that is taken.

Types

type Dimension

type Dimension struct {
	Metric           string
	Type             string
	PricePerUnitHour decimal.Decimal
	PricePerUnit     decimal.Decimal
}

Dimension is one priced metric of a resource type. Type decides which of the two prices carries the value: PricePerUnitHour for TypeTimeGauge, PricePerUnit for TypeCounter. The other one is zero.

type Model

type Model struct {
	Version   string
	ValidFrom time.Time
	Currency  string
	Pricing   map[string]map[string]ResourcePricing
}

Model is one version of the pricing model. Pricing is keyed by platform and then by resource type; a resource type the map does not hold is not priced.

func ByVersion

func ByVersion(ctx context.Context, q *sqlcgen.Queries, version string) (Model, error)

ByVersion returns the stored model of one version. It is what a correction rates with: a correction rates a finalized month with the version the finalized run recorded, whatever was imported since, so it corrects the usage of that month and leaves its prices where they were (D6).

A version no stored model carries yields an error wrapping ErrVersionNotFound.

func ForPeriod

func ForPeriod(ctx context.Context, q *sqlcgen.Queries, periodFrom time.Time) (Model, error)

ForPeriod returns the version that prices the period beginning at periodFrom: the newest one whose valid_from is at or before that instant. A version that becomes valid later prices later periods only, so importing the prices of April does not reprice March.

A period that begins before every stored version yields an error wrapping ErrNoModel.

func Parse

func Parse(data []byte) (Model, []byte, error)

Parse reads a pricing model file. It returns the typed model and the canonical JSON document the model was built from, which is the form a version is stored in.

The document is canonical in that the same file always yields the same bytes: mapping keys come out sorted, and every scalar keeps the text the file spells it with. That is what lets a re-import be compared against the stored version instead of being decided by the order a map was walked in.

func ParseDocument

func ParseDocument(doc []byte) (Model, error)

ParseDocument reads a canonical JSON document into a Model. It is the path a document read back from the database takes, and the path Parse takes once it has turned a file into one.

The document is checked against the embedded schema first, so what the model is built from is a document the schema accepts. Two rules the schema cannot state are checked afterwards: valid_from has to be an RFC 3339 timestamp, and no resource type may price one metric twice.

func (Model) Equal

func (m Model) Equal(other Model) bool

Equal reports whether both models price the same thing. Decimals are compared by value, so a price the file respells as 0.50 rather than "0.5" leaves the model equal, which is what a re-import of an unchanged version has to be. The dimensions of a resource type are compared in order: reordering them reorders the rated records they produce, so it is a different model.

type ResourcePricing

type ResourcePricing struct {
	Dimensions     []Dimension
	StateModifiers map[string]decimal.Decimal
	TypeModifiers  map[string]decimal.Decimal
}

ResourcePricing is what one resource type of one platform costs. Dimensions are in the order the file lists them, which is the order the rated records of a resource are written in. The modifier maps are empty where the file sets none, and a state or type the map does not hold is billed unmodified.

Jump to

Keyboard shortcuts

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