Include latest and earliest record times in state #34
No files matched your search
+19
-6
@@ -96,11 +96,9 @@ func (fs *JSONStore) Load(ctx context.Context) (map[string]*FillerQueue, error)
|
|||||||
// the same directory followed by an atomic rename so a crash mid-write cannot
|
// the same directory followed by an atomic rename so a crash mid-write cannot
|
||||||
// leave a half-written state file.
|
// leave a half-written state file.
|
||||||
func (fs *JSONStore) Save(ctx context.Context, queue map[string]*FillerQueue) error {
|
func (fs *JSONStore) Save(ctx context.Context, queue map[string]*FillerQueue) error {
|
||||||
state := State{
|
queues := make(map[string][]persistedFiller)
|
||||||
Version: StateVersion,
|
latest := time.Time{}
|
||||||
Platform: fs.platform,
|
earliest := time.Time{}
|
||||||
Queues: make(map[string][]persistedFiller),
|
|
||||||
}
|
|
||||||
|
|
||||||
for symbol, q := range queue {
|
for symbol, q := range queue {
|
||||||
if q == nil || q.Len() == 0 {
|
if q == nil || q.Len() == 0 {
|
||||||
@@ -119,8 +117,23 @@ func (fs *JSONStore) Save(ctx context.Context, queue map[string]*FillerQueue) er
|
|||||||
Price: f.Price(),
|
Price: f.Price(),
|
||||||
Filled: f.Filled(),
|
Filled: f.Filled(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.Timestamp().After(latest) {
|
||||||
|
latest = f.Timestamp()
|
||||||
|
}
|
||||||
|
if f.Timestamp().Before(earliest) || earliest.IsZero() {
|
||||||
|
earliest = f.Timestamp()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.Queues[symbol] = persisted
|
queues[symbol] = persisted
|
||||||
|
}
|
||||||
|
|
||||||
|
state := State{
|
||||||
|
Version: StateVersion,
|
||||||
|
Platform: fs.platform,
|
||||||
|
Queues: queues,
|
||||||
|
Latest: latest,
|
||||||
|
Earliest: earliest,
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/nmoniz/any2anexoj/internal"
|
"github.com/nmoniz/any2anexoj/internal"
|
||||||
"github.com/nmoniz/any2anexoj/internal/mocks"
|
"github.com/nmoniz/any2anexoj/internal/mocks"
|
||||||
@@ -84,13 +85,25 @@ func TestJSONStore_RoundTrip(t *testing.T) {
|
|||||||
// confirming the inline form is parseable guards against accidentally
|
// confirming the inline form is parseable guards against accidentally
|
||||||
// double-encoding it again in the future).
|
// double-encoding it again in the future).
|
||||||
var parsed struct {
|
var parsed struct {
|
||||||
Queues map[string][]struct {
|
Latest time.Time `json:"latest"`
|
||||||
|
Earliest time.Time `json:"earliest"`
|
||||||
|
Queues map[string][]struct {
|
||||||
RecordData json.RawMessage `json:"record_data"`
|
RecordData json.RawMessage `json:"record_data"`
|
||||||
} `json:"queues"`
|
} `json:"queues"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, &parsed); err != nil {
|
if err := json.Unmarshal(data, &parsed); err != nil {
|
||||||
t.Fatalf("saved state file is not valid JSON: %v", err)
|
t.Fatalf("saved state file is not valid JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The latest/earliest bounds must be persisted alongside the queues.
|
||||||
|
// Every record in this test returns testRecordTime, so both bounds must
|
||||||
|
// equal it exactly.
|
||||||
|
if !parsed.Latest.Equal(testRecordTime) {
|
||||||
|
t.Errorf("state latest = %v; want %v", parsed.Latest, testRecordTime)
|
||||||
|
}
|
||||||
|
if !parsed.Earliest.Equal(testRecordTime) {
|
||||||
|
t.Errorf("state earliest = %v; want %v", parsed.Earliest, testRecordTime)
|
||||||
|
}
|
||||||
for symbol, lots := range parsed.Queues {
|
for symbol, lots := range parsed.Queues {
|
||||||
for i, lot := range lots {
|
for i, lot := range lots {
|
||||||
if len(lot.RecordData) == 0 {
|
if len(lot.RecordData) == 0 {
|
||||||
@@ -341,13 +354,20 @@ func TestNewJSONStore_ValidatesArguments(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRecord builds a MockRecord whose Symbol() returns the given symbol.
|
// testRecordTime is the deterministic timestamp returned by every record
|
||||||
// The JSONStore only reads Symbol() off the loaded Record during tests
|
// built in this file. Using a fixed value keeps the latest/earliest bounds
|
||||||
// (quantity/price/filled come from the persisted struct fields), so all
|
// written by JSONStore.Save predictable and assertable.
|
||||||
// other Record methods can be left as default-mocked values.
|
var testRecordTime = time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
// newRecord builds a MockRecord whose Symbol() returns the given symbol and
|
||||||
|
// whose Timestamp() returns testRecordTime. The JSONStore only reads Symbol()
|
||||||
|
// and Timestamp() off the Record during tests (quantity/price/filled come
|
||||||
|
// from the persisted struct fields), so all other Record methods can be left
|
||||||
|
// as default-mocked values.
|
||||||
func newRecord(ctrl *gomock.Controller, symbol string) *mocks.MockRecord {
|
func newRecord(ctrl *gomock.Controller, symbol string) *mocks.MockRecord {
|
||||||
r := mocks.NewMockRecord(ctrl)
|
r := mocks.NewMockRecord(ctrl)
|
||||||
r.EXPECT().Symbol().Return(symbol).AnyTimes()
|
r.EXPECT().Symbol().Return(symbol).AnyTimes()
|
||||||
|
r.EXPECT().Timestamp().Return(testRecordTime).AnyTimes()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
)
|
)
|
||||||
@@ -15,6 +16,8 @@ const StateVersion = "2"
|
|||||||
type State struct {
|
type State struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Platform string `json:"platform"`
|
Platform string `json:"platform"`
|
||||||
|
Latest time.Time `json:"latest"`
|
||||||
|
Earliest time.Time `json:"earliest"`
|
||||||
Queues map[string][]persistedFiller `json:"queues"`
|
Queues map[string][]persistedFiller `json:"queues"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user