Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,953 changes: 1,953 additions & 0 deletions execution/engine/execution_engine_cost_test.go

Large diffs are not rendered by default.

2,167 changes: 104 additions & 2,063 deletions execution/engine/execution_engine_test.go

Large diffs are not rendered by default.

97 changes: 93 additions & 4 deletions execution/engine/federation_caching_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,11 @@ func cachingTestQueryPath(name string) string {
}

type CacheLogEntry struct {
Operation string // "get", "set", "delete"
Keys []string // Keys involved in the operation
Hits []bool // For Get: whether each key was a hit (true) or miss (false)
Caller string // Fetch identity when debug enabled: "accounts: entity(User)" or "products: rootField(Query.topProducts)"
Operation string // "get", "set", "delete"
Keys []string // Keys involved in the operation
Hits []bool // For Get: whether each key was a hit (true) or miss (false)
TTL time.Duration // For Set: the TTL used
Caller string // Fetch identity when debug enabled: "accounts: entity(User)" or "products: rootField(Query.topProducts)"
}

// sortCacheLogKeys sorts the keys (and corresponding hits) in each cache log entry.
Expand Down Expand Up @@ -289,6 +290,93 @@ func sortCacheLogKeysWithCaller(log []CacheLogEntry) []CacheLogEntry {
return sorted
}

// sortCacheLogEntries sorts both the entries (by operation+first key) and the keys within entries.
// Use this when log entry order is non-deterministic (e.g., split datasources executing in parallel).
func sortCacheLogEntries(log []CacheLogEntry) []CacheLogEntry {
sorted := sortCacheLogKeys(log)
sort.Slice(sorted, func(a, b int) bool {
if sorted[a].Operation != sorted[b].Operation {
return sorted[a].Operation < sorted[b].Operation
}
keyA, keyB := "", ""
if len(sorted[a].Keys) > 0 {
keyA = sorted[a].Keys[0]
}
if len(sorted[b].Keys) > 0 {
keyB = sorted[b].Keys[0]
}
return keyA < keyB
})
return sorted
}

// sortCacheLogKeysWithTTL is like sortCacheLogKeys but preserves the TTL field.
// Use this when assertions need to verify TTL values on set operations.
func sortCacheLogKeysWithTTL(log []CacheLogEntry) []CacheLogEntry {
sorted := make([]CacheLogEntry, len(log))
for i, entry := range log {
if len(entry.Keys) <= 1 {
sorted[i] = CacheLogEntry{
Operation: entry.Operation,
Keys: entry.Keys,
Hits: entry.Hits,
TTL: entry.TTL,
}
continue
}

pairs := make([]struct {
key string
hit bool
}, len(entry.Keys))
for j := range entry.Keys {
pairs[j].key = entry.Keys[j]
if entry.Hits != nil && j < len(entry.Hits) {
pairs[j].hit = entry.Hits[j]
}
}
sort.Slice(pairs, func(a, b int) bool {
return pairs[a].key < pairs[b].key
})
sorted[i] = CacheLogEntry{
Operation: entry.Operation,
Keys: make([]string, len(pairs)),
Hits: nil,
TTL: entry.TTL,
}
if len(entry.Hits) > 0 {
sorted[i].Hits = make([]bool, len(pairs))
}
for j := range pairs {
sorted[i].Keys[j] = pairs[j].key
if sorted[i].Hits != nil {
sorted[i].Hits[j] = pairs[j].hit
}
}
}
return sorted
}

// sortCacheLogEntriesWithTTL sorts both entries and keys while preserving TTL.
// Use this when entry order is non-deterministic and TTL values need to be verified.
func sortCacheLogEntriesWithTTL(log []CacheLogEntry) []CacheLogEntry {
sorted := sortCacheLogKeysWithTTL(log)
sort.Slice(sorted, func(a, b int) bool {
if sorted[a].Operation != sorted[b].Operation {
return sorted[a].Operation < sorted[b].Operation
}
keyA, keyB := "", ""
if len(sorted[a].Keys) > 0 {
keyA = sorted[a].Keys[0]
}
if len(sorted[b].Keys) > 0 {
keyB = sorted[b].Keys[0]
}
return keyA < keyB
})
return sorted
}

type cacheEntry struct {
data []byte
expiresAt *time.Time
Expand Down Expand Up @@ -405,6 +493,7 @@ func (f *FakeLoaderCache) Set(ctx context.Context, entries []*resolve.CacheEntry
Operation: "set",
Keys: keys,
Hits: nil, // Set operations don't have hits/misses
TTL: ttl,
Caller: caller,
})

Expand Down
Loading