more cart
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s

This commit is contained in:
2026-07-01 22:11:37 +02:00
parent 1c97a4bdb0
commit 26fd6dc970
23 changed files with 1717 additions and 250 deletions
+66
View File
@@ -7,6 +7,7 @@ import (
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
@@ -201,6 +202,9 @@ func (s *DiskStorage[V]) SaveLoop(duration time.Duration) {
}
func (s *DiskStorage[V]) save() {
if s.queue == nil {
return
}
carts := 0
lines := 0
s.queue.Range(func(key, value any) bool {
@@ -295,3 +299,65 @@ func (s *DiskStorage[V]) openWriterCount() int {
defer s.writersMu.Unlock()
return len(s.writers)
}
func (s *DiskStorage[V]) PurgeOlderThan(ctx context.Context, retention time.Duration, isGrainActive func(id uint64) bool) (int, error) {
if err := s.ensureDir(); err != nil {
return 0, err
}
files, err := os.ReadDir(s.path)
if err != nil {
return 0, fmt.Errorf("read storage dir: %w", err)
}
cutoff := time.Now().Add(-retention)
purgedCount := 0
for _, entry := range files {
if entry.IsDir() {
continue
}
name := entry.Name()
if !strings.HasSuffix(name, ".events.log") {
continue
}
var id uint64
_, err := fmt.Sscanf(name, "%d.events.log", &id)
if err != nil {
continue
}
info, err := entry.Info()
if err != nil {
log.Printf("failed to get file info for %s: %v", name, err)
continue
}
if info.ModTime().Before(cutoff) {
// Check if grain is active in memory
if isGrainActive != nil && isGrainActive(id) {
continue
}
// Check if we have an active writer open
s.writersMu.Lock()
_, hasWriter := s.writers[id]
s.writersMu.Unlock()
if hasWriter {
continue
}
// Delete the file
fullPath := filepath.Join(s.path, name)
if err := os.Remove(fullPath); err != nil {
log.Printf("failed to delete expired cart log %s: %v", fullPath, err)
} else {
purgedCount++
}
}
}
return purgedCount, nil
}
+84
View File
@@ -2,6 +2,7 @@ package actor
import (
"context"
"os"
"sync"
"testing"
"time"
@@ -317,3 +318,86 @@ func TestDiskStorageConcurrentWritesDifferentIDs(t *testing.T) {
}
}
}
func TestDiskStoragePurge(t *testing.T) {
reg := diskTestRegistry(t)
dir := t.TempDir()
storage := NewDiskStorage[testState](dir, reg)
t.Cleanup(storage.Close)
ctx := context.Background()
msg := &cart_messages.AddItem{ItemId: 1, Quantity: 1}
// Write mutations to create event logs for 1, 2, 3, 4
ids := []uint64{1, 2, 3, 4}
for _, id := range ids {
if err := storage.AppendMutations(id, msg); err != nil {
t.Fatalf("append %d: %v", id, err)
}
}
// Force save to disk
storage.save()
// 1. Grain 1: Old modification time, inactive, no writer -> should be purged
path1 := storage.logPath(1)
oldTime := time.Now().Add(-10 * 24 * time.Hour)
if err := os.Chtimes(path1, oldTime, oldTime); err != nil {
t.Fatal(err)
}
// We must close the writer for grain 1, 2, 4 to make them eligible for purging (no open writer)
storage.writersMu.Lock()
for _, id := range []uint64{1, 2, 4} {
if w, ok := storage.writers[id]; ok {
w.purged = true
w.file.Close()
delete(storage.writers, id)
}
}
storage.writersMu.Unlock()
// 2. Grain 2: Old mod time, but marked active -> should NOT be purged
path2 := storage.logPath(2)
if err := os.Chtimes(path2, oldTime, oldTime); err != nil {
t.Fatal(err)
}
// 3. Grain 3: Old mod time, but has open writer -> should NOT be purged
path3 := storage.logPath(3)
if err := os.Chtimes(path3, oldTime, oldTime); err != nil {
t.Fatal(err)
}
// 4. Grain 4: Recent mod time -> should NOT be purged
path4 := storage.logPath(4)
activeGrains := map[uint64]bool{2: true}
isGrainActive := func(id uint64) bool {
return activeGrains[id]
}
purged, err := storage.PurgeOlderThan(ctx, 5*24*time.Hour, isGrainActive)
if err != nil {
t.Fatalf("PurgeOlderThan failed: %v", err)
}
if purged != 1 {
t.Errorf("Expected exactly 1 purged file, got %d", purged)
}
// Verify file status
if _, err := os.Stat(path1); !os.IsNotExist(err) {
t.Error("Expected grain 1 log to be deleted")
}
if _, err := os.Stat(path2); err != nil {
t.Error("Expected grain 2 log to exist (active)")
}
if _, err := os.Stat(path3); err != nil {
t.Error("Expected grain 3 log to exist (active writer)")
}
if _, err := os.Stat(path4); err != nil {
t.Error("Expected grain 4 log to exist (recent)")
}
}