mirror of
https://github.com/hanzoai/compute.git
synced 2026-10-30 14:50:41 +00:00
104 lines
3.1 KiB
Go
104 lines
3.1 KiB
Go
// Copyright 2026 Hanzo Industries Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package service
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Ledger keeps, per hosted machine and per thing it is billed for — its running
|
|
// hours, its stopped disk — the last clock hour ("YYYYMMDDHH") billed. The
|
|
// launch, a start and the hourly sweep each move a mark only over hours whose
|
|
// debit landed, so one hour is charged once whichever of them reaches it first,
|
|
// and hours a sweep missed — a restart, an outage, a failed tick — are charged
|
|
// by the next one. The sweep also keeps each org's balance reads here
|
|
// (balanceOf).
|
|
//
|
|
// The rows live in the store (object), which service cannot import, so the
|
|
// store registers itself here the way it registers credentials.
|
|
type Ledger interface {
|
|
// Through returns key's mark; the zero Mark when it was never billed.
|
|
Through(key string) (Mark, error)
|
|
// Advance moves each key's mark forward to the one given, durably, and
|
|
// reports which moved. A mark at or past the hour given does not move: that
|
|
// hour is already billed.
|
|
Advance(marks map[string]Mark) (map[string]bool, error)
|
|
}
|
|
|
|
// Mark is a key's last hour, and for an org's balance run (readKey) its length.
|
|
type Mark struct {
|
|
Hour string
|
|
Streak int64
|
|
}
|
|
|
|
// book is the registered Ledger.
|
|
var (
|
|
bookMu sync.RWMutex
|
|
book Ledger = newMemoryLedger()
|
|
)
|
|
|
|
// RegisterLedger installs where billed hours are kept. Unregistered, they are
|
|
// kept in this process only, which is what a test or a local run wants and what
|
|
// a deployment must not be left with: a restart forgets them.
|
|
func RegisterLedger(l Ledger) {
|
|
bookMu.Lock()
|
|
defer bookMu.Unlock()
|
|
book = l
|
|
}
|
|
|
|
func billed() Ledger {
|
|
bookMu.RLock()
|
|
defer bookMu.RUnlock()
|
|
return book
|
|
}
|
|
|
|
// memoryLedger is a Ledger held in this process.
|
|
type memoryLedger struct {
|
|
mu sync.Mutex
|
|
marks map[string]Mark
|
|
}
|
|
|
|
func newMemoryLedger() *memoryLedger { return &memoryLedger{marks: map[string]Mark{}} }
|
|
|
|
func (l *memoryLedger) Through(key string) (Mark, error) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.marks[key], nil
|
|
}
|
|
|
|
func (l *memoryLedger) Advance(marks map[string]Mark) (map[string]bool, error) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
moved := map[string]bool{}
|
|
for key, m := range marks {
|
|
if l.marks[key].Hour >= m.Hour {
|
|
continue
|
|
}
|
|
l.marks[key] = m
|
|
moved[key] = true
|
|
}
|
|
return moved, nil
|
|
}
|
|
|
|
// hourOf is the clock hour t falls in, as a ledger mark.
|
|
func hourOf(t time.Time) string { return HourStamp(t) }
|
|
|
|
// parseHour reads a ledger mark back into the start of its hour.
|
|
func parseHour(stamp string) (time.Time, bool) {
|
|
t, err := time.Parse("2006010215", stamp)
|
|
return t, err == nil
|
|
}
|