feat: enforce tiered storage reservations
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChargeFormulaVersionAndOverflow_HP_STORE_04(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if ChargeFormulaVersion != 1 {
|
||||
t.Fatalf("charge formula version = %d", ChargeFormulaVersion)
|
||||
}
|
||||
charge, err := EstimateCharge(ChargeInput{EncodedBytes: 100, SQLiteRows: 2, IndexEntries: 3})
|
||||
want := uint64(100 + 2*ChargeSQLiteRowBytes + 3*ChargeIndexEntryBytes)
|
||||
if err != nil || charge != want {
|
||||
t.Fatalf("EstimateCharge = (%d, %v), want %d", charge, err, want)
|
||||
}
|
||||
for _, input := range []ChargeInput{
|
||||
{SQLiteRows: math.MaxUint64},
|
||||
{IndexEntries: math.MaxUint64},
|
||||
{EncodedBytes: math.MaxUint64, SQLiteRows: 1},
|
||||
} {
|
||||
if _, err := EstimateCharge(input); !errors.Is(err, ErrCapacityExhausted) {
|
||||
t.Errorf("EstimateCharge(%+v) error = %v", input, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTieredReservationOrderAndBoundaries_HP_STORE_05(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
limits := quotaTestLimits()
|
||||
base := ReservationState{
|
||||
CommandOutputCharged: 10, CommandTotalCharged: 20, ClientTotalCharged: 30,
|
||||
ServerTotalCharged: 40, CloseoutRemaining: 20, FilesystemFreeBytes: 1000,
|
||||
}
|
||||
decision, err := CheckReservation(limits, base, ReservationRequest{ChargedBytes: 10, PhysicalBytes: 5, Output: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decision.CommandOutputCharged != 20 || decision.CommandTotalCharged != 30 || decision.ClientTotalCharged != 40 || decision.ServerTotalCharged != 50 || decision.CloseoutRemaining != 20 {
|
||||
t.Fatalf("decision = %+v", decision)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
state ReservationState
|
||||
request ReservationRequest
|
||||
tier CapacityTier
|
||||
}{
|
||||
{"hard maximum first", base, ReservationRequest{ChargedBytes: 101, PhysicalBytes: 1, Output: true}, CapacityTierHardMaximum},
|
||||
{"output", withState(base, 95, 20, 30, 40, 20, 1000), ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1, Output: true}, CapacityTierCommandOut},
|
||||
{"command reserve", withState(base, 10, 175, 175, 175, 20, 1000), ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1}, CapacityTierCommand},
|
||||
{"client", withState(base, 10, 20, 295, 295, 20, 1000), ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1}, CapacityTierClient},
|
||||
{"server", withState(base, 10, 20, 30, 395, 20, 1000), ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1}, CapacityTierServer},
|
||||
{"filesystem", withState(base, 10, 20, 30, 40, 20, 54), ReservationRequest{ChargedBytes: 10, PhysicalBytes: 5}, CapacityTierFilesystem},
|
||||
}
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := CheckReservation(limits, test.state, test.request)
|
||||
var capacity *CapacityError
|
||||
if !errors.As(err, &capacity) || capacity.Tier != test.tier {
|
||||
t.Fatalf("error = %v, want tier %s", err, test.tier)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseoutReservationCanBeConsumed_BH_STORE_06(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
limits := quotaTestLimits()
|
||||
state := ReservationState{
|
||||
CommandOutputCharged: 90, CommandTotalCharged: 190, ClientTotalCharged: 200,
|
||||
ServerTotalCharged: 200, CloseoutRemaining: 10, FilesystemFreeBytes: 1000,
|
||||
}
|
||||
if _, err := CheckReservation(limits, state, ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1}); err == nil {
|
||||
t.Fatal("normal allocation consumed closeout headroom")
|
||||
}
|
||||
decision, err := CheckReservation(limits, state, ReservationRequest{ChargedBytes: 10, PhysicalBytes: 1, UseCloseout: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decision.CommandTotalCharged != 200 || decision.CloseoutRemaining != 0 {
|
||||
t.Fatalf("closeout decision = %+v", decision)
|
||||
}
|
||||
if _, err := CheckReservation(limits, state, ReservationRequest{ChargedBytes: math.MaxUint64, PhysicalBytes: 1, UseCloseout: true}); !errors.Is(err, ErrCapacityExhausted) {
|
||||
t.Fatalf("overflow error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func quotaTestLimits() QuotaLimits {
|
||||
return QuotaLimits{
|
||||
HardAllocationBytes: 100, CommandOutputBytes: 100, CommandTotalBytes: 200,
|
||||
ClientTotalBytes: 300, ServerTotalBytes: 400, CloseoutReserveBytes: 20, FilesystemFloorBytes: 50,
|
||||
}
|
||||
}
|
||||
|
||||
func withState(base ReservationState, output, command, client, server, closeout, free uint64) ReservationState {
|
||||
base.CommandOutputCharged = output
|
||||
base.CommandTotalCharged = command
|
||||
base.ClientTotalCharged = client
|
||||
base.ServerTotalCharged = server
|
||||
base.CloseoutRemaining = closeout
|
||||
base.FilesystemFreeBytes = free
|
||||
return base
|
||||
}
|
||||
Reference in New Issue
Block a user