Skip to content

Commit 8a9f1ce

Browse files
committed
feat(cache-analytics): add DataSource dimension to analytics events
Plumbs the owning subgraph name through every analytics event so a single entity instance resolved by multiple subgraphs can be attributed correctly in dashboards (a User id=42 may have N L2 cache entries — one per subgraph that contributes fields — and EntityType alone cannot disambiguate them). - DataSource string field on EntityFieldHash, MutationEvent, entitySourceRecord - HashFieldValue, RecordEntitySource, walkCachedResponseForSources signatures gain a dataSource parameter - New EntityDataSource(entityType, keyJSON) lookup mirrors EntitySource - Resolvable.currentEntityDataSource is saved/restored across nested entities; resolved from the loader-phase entitySources record, falling back to obj.SourceName for cold subgraph fetches that never hit L1/L2
1 parent e8fdd31 commit 8a9f1ce

4 files changed

Lines changed: 173 additions & 41 deletions

File tree

v2/pkg/engine/resolve/cache_analytics.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ type EntityFieldHash struct {
107107
KeyRaw string // raw key JSON e.g. {"id":"1234"} (when HashKeys=false)
108108
KeyHash uint64 // xxhash of key JSON (when HashKeys=true)
109109
Source FieldSource // where the entity data came from (L1/L2/Subgraph)
110+
DataSource string // subgraph name that owns this entity (empty if not resolvable)
110111
}
111112

112113
// EntityTypeInfo holds the entity type name and its instance count.
@@ -128,6 +129,7 @@ type entitySourceRecord struct {
128129
entityType string
129130
keyJSON string
130131
source FieldSource
132+
dataSource string // subgraph name (empty if unknown)
131133
}
132134

133135
// ShadowComparisonEvent records a comparison between cached and fresh data in shadow mode.
@@ -152,6 +154,7 @@ type MutationEvent struct {
152154
Timestamp time.Time // when the mutation was observed; stamped by RecordMutationEvent if zero
153155
MutationRootField string // e.g., "updateUsername"
154156
EntityType string // e.g., "User"
157+
DataSource string // subgraph name that owns the mutated entity (empty when not resolvable)
155158
EntityCacheKey string // display key e.g. {"__typename":"User","key":{"id":"1234"}}
156159
HadCachedValue bool // true if L2 had a cached value for this entity
157160
IsStale bool // true if cached value differs from mutation response (always false when HadCachedValue=false)
@@ -313,8 +316,9 @@ func (c *CacheAnalyticsCollector) RecordWrite(event CacheWriteEvent) {
313316
}
314317

315318
// HashFieldValue computes an xxhash of the given field value bytes and records it
316-
// as an EntityFieldHash with entity key and source information.
317-
func (c *CacheAnalyticsCollector) HashFieldValue(entityType, fieldName string, valueBytes []byte, keyRaw string, keyHash uint64, source FieldSource) {
319+
// as an EntityFieldHash with entity key and source information. dataSource is
320+
// the subgraph name that owns the entity (may be empty if not resolvable).
321+
func (c *CacheAnalyticsCollector) HashFieldValue(entityType, fieldName string, valueBytes []byte, keyRaw string, keyHash uint64, source FieldSource, dataSource string) {
318322
c.xxh.Reset()
319323
_, _ = c.xxh.Write(valueBytes)
320324
hash := c.xxh.Sum64()
@@ -327,6 +331,7 @@ func (c *CacheAnalyticsCollector) HashFieldValue(entityType, fieldName string, v
327331
KeyRaw: keyRaw,
328332
KeyHash: keyHash,
329333
Source: source,
334+
DataSource: dataSource,
330335
})
331336
}
332337

@@ -353,12 +358,13 @@ func (c *CacheAnalyticsCollector) IncrementEntityCount(typeName string, keyJSON
353358
}
354359

355360
// RecordEntitySource records the source of data for a specific entity instance.
356-
// Main thread only.
357-
func (c *CacheAnalyticsCollector) RecordEntitySource(entityType, keyJSON string, source FieldSource) {
361+
// Main thread only. dataSource is the subgraph name (may be empty).
362+
func (c *CacheAnalyticsCollector) RecordEntitySource(entityType, keyJSON, dataSource string, source FieldSource) {
358363
c.entitySources = append(c.entitySources, entitySourceRecord{
359364
entityType: entityType,
360365
keyJSON: keyJSON,
361366
source: source,
367+
dataSource: dataSource,
362368
})
363369
}
364370

@@ -448,6 +454,17 @@ func (c *CacheAnalyticsCollector) EntitySource(entityType, keyJSON string) Field
448454
return FieldSourceSubgraph
449455
}
450456

457+
// EntityDataSource returns the subgraph name that owns a given entity instance.
458+
// Returns "" if no record is found.
459+
func (c *CacheAnalyticsCollector) EntityDataSource(entityType, keyJSON string) string {
460+
for i := len(c.entitySources) - 1; i >= 0; i-- {
461+
if c.entitySources[i].entityType == entityType && c.entitySources[i].keyJSON == keyJSON {
462+
return c.entitySources[i].dataSource
463+
}
464+
}
465+
return ""
466+
}
467+
451468
// Snapshot produces a read-only CacheAnalyticsSnapshot from the collected data.
452469
// Duplicate events (same cache key appearing multiple times due to entity batch positions)
453470
// are consolidated: consumers see one event per unique (CacheKey, Kind) for reads,
@@ -1065,14 +1082,15 @@ func appendKeyFieldsJSON(buf []byte, value *astjson.Value, keyFields []KeyField)
10651082

10661083
// walkCachedResponseForSources walks a cached JSON value to find entity instances
10671084
// and accumulates their source records on a per-result slice (goroutine-safe).
1068-
func walkCachedResponseForSources(value *astjson.Value, keyFields []KeyField, entityType string, source FieldSource, out *[]entitySourceRecord) {
1085+
// dataSource is the subgraph name the cached response originated from (may be empty).
1086+
func walkCachedResponseForSources(value *astjson.Value, keyFields []KeyField, entityType, dataSource string, source FieldSource, out *[]entitySourceRecord) {
10691087
if value == nil {
10701088
return
10711089
}
10721090
switch value.Type() {
10731091
case astjson.TypeArray:
10741092
for _, item := range value.GetArray() {
1075-
walkCachedResponseForSources(item, keyFields, entityType, source, out)
1093+
walkCachedResponseForSources(item, keyFields, entityType, dataSource, source, out)
10761094
}
10771095
case astjson.TypeObject:
10781096
keyJSON := buildEntityKeyJSON(value, keyFields)
@@ -1081,6 +1099,7 @@ func walkCachedResponseForSources(value *astjson.Value, keyFields []KeyField, en
10811099
entityType: entityType,
10821100
keyJSON: string(keyJSON),
10831101
source: source,
1102+
dataSource: dataSource,
10841103
})
10851104
}
10861105
}

v2/pkg/engine/resolve/cache_analytics_test.go

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ func TestCacheAnalyticsCollector_FieldHashing(t *testing.T) {
136136
t.Run("same input produces same hash", func(t *testing.T) {
137137
c := NewCacheAnalyticsCollector()
138138

139-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph)
140-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph)
139+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph, "")
140+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph, "")
141141

142142
snap := c.Snapshot()
143143
assert.Equal(t, 2, len(snap.FieldHashes))
@@ -151,8 +151,8 @@ func TestCacheAnalyticsCollector_FieldHashing(t *testing.T) {
151151
t.Run("different input produces different hash", func(t *testing.T) {
152152
c := NewCacheAnalyticsCollector()
153153

154-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph)
155-
c.HashFieldValue("User", "name", []byte(`"Bob"`), `{"id":"2"}`, 0, FieldSourceSubgraph)
154+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph, "")
155+
c.HashFieldValue("User", "name", []byte(`"Bob"`), `{"id":"2"}`, 0, FieldSourceSubgraph, "")
156156

157157
snap := c.Snapshot()
158158
assert.Equal(t, 2, len(snap.FieldHashes))
@@ -162,7 +162,7 @@ func TestCacheAnalyticsCollector_FieldHashing(t *testing.T) {
162162
t.Run("hashed keys mode", func(t *testing.T) {
163163
c := NewCacheAnalyticsCollector()
164164

165-
c.HashFieldValue("User", "name", []byte(`"Alice"`), "", 12345, FieldSourceL1)
165+
c.HashFieldValue("User", "name", []byte(`"Alice"`), "", 12345, FieldSourceL1, "")
166166

167167
snap := c.Snapshot()
168168
assert.Equal(t, 1, len(snap.FieldHashes))
@@ -174,9 +174,9 @@ func TestCacheAnalyticsCollector_FieldHashing(t *testing.T) {
174174
t.Run("field source tracking", func(t *testing.T) {
175175
c := NewCacheAnalyticsCollector()
176176

177-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph)
178-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceL1)
179-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceL2)
177+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph, "")
178+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceL1, "")
179+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceL2, "")
180180

181181
snap := c.Snapshot()
182182
assert.Equal(t, 3, len(snap.FieldHashes))
@@ -234,9 +234,9 @@ func TestCacheAnalyticsCollector_EntityCounts(t *testing.T) {
234234
func TestCacheAnalyticsCollector_EntitySourceTracking(t *testing.T) {
235235
c := NewCacheAnalyticsCollector()
236236

237-
c.RecordEntitySource("User", `{"id":"1"}`, FieldSourceSubgraph)
238-
c.RecordEntitySource("User", `{"id":"2"}`, FieldSourceL1)
239-
c.RecordEntitySource("Product", `{"upc":"top-1"}`, FieldSourceL2)
237+
c.RecordEntitySource("User", `{"id":"1"}`, "", FieldSourceSubgraph)
238+
c.RecordEntitySource("User", `{"id":"2"}`, "", FieldSourceL1)
239+
c.RecordEntitySource("Product", `{"upc":"top-1"}`, "", FieldSourceL2)
240240

241241
assert.Equal(t, FieldSourceSubgraph, c.EntitySource("User", `{"id":"1"}`))
242242
assert.Equal(t, FieldSourceL1, c.EntitySource("User", `{"id":"2"}`))
@@ -1564,7 +1564,7 @@ func TestFieldSourceShadowCached(t *testing.T) {
15641564
t.Run("HashFieldValue with FieldSourceShadowCached", func(t *testing.T) {
15651565
c := NewCacheAnalyticsCollector()
15661566

1567-
c.HashFieldValue("User", "username", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceShadowCached)
1567+
c.HashFieldValue("User", "username", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceShadowCached, "")
15681568

15691569
snap := c.Snapshot()
15701570
require.Equal(t, 1, len(snap.FieldHashes))
@@ -1577,8 +1577,8 @@ func TestFieldSourceShadowCached(t *testing.T) {
15771577
t.Run("can distinguish from other sources", func(t *testing.T) {
15781578
c := NewCacheAnalyticsCollector()
15791579

1580-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph)
1581-
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceShadowCached)
1580+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceSubgraph, "")
1581+
c.HashFieldValue("User", "name", []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceShadowCached, "")
15821582

15831583
snap := c.Snapshot()
15841584
require.Equal(t, 2, len(snap.FieldHashes))
@@ -1755,7 +1755,7 @@ func BenchmarkFieldHashing(b *testing.B) {
17551755

17561756
b.ResetTimer()
17571757
for b.Loop() {
1758-
c.HashFieldValue("User", "id", value, `{"id":"1"}`, 0, FieldSourceSubgraph)
1758+
c.HashFieldValue("User", "id", value, `{"id":"1"}`, 0, FieldSourceSubgraph, "")
17591759
}
17601760
}
17611761

@@ -2056,7 +2056,7 @@ func TestSnapshotSlicesAreIndependent(t *testing.T) {
20562056
c.RecordError(SubgraphErrorEvent{DataSource: "ds-orig"})
20572057
c.RecordMutationEvent(MutationEvent{EntityType: "User-orig"})
20582058
c.RecordCacheOperationError(CacheOperationError{DataSource: "ds-orig"})
2059-
c.HashFieldValue("User-orig", "name", []byte(`"a"`), "k-orig", 1, FieldSourceL1)
2059+
c.HashFieldValue("User-orig", "name", []byte(`"a"`), "k-orig", 1, FieldSourceL1, "")
20602060

20612061
snap := c.Snapshot()
20622062

@@ -2078,7 +2078,7 @@ func TestSnapshotSlicesAreIndependent(t *testing.T) {
20782078
c.RecordError(SubgraphErrorEvent{DataSource: "ds-new"})
20792079
c.RecordMutationEvent(MutationEvent{EntityType: "User-new"})
20802080
c.RecordCacheOperationError(CacheOperationError{DataSource: "ds-new"})
2081-
c.HashFieldValue("User-new", "name", []byte(`"z"`), "k-new", 2, FieldSourceL2)
2081+
c.HashFieldValue("User-new", "name", []byte(`"z"`), "k-new", 2, FieldSourceL2, "")
20822082
}
20832083

20842084
// Full-slice assertions — snapshot must still show the original events.
@@ -2175,3 +2175,88 @@ func TestCacheAnalyticsCollector_TimestampsPreservePreSet(t *testing.T) {
21752175
}
21762176
}
21772177

2178+
// TestCacheAnalyticsCollector_DataSourceOnHashFieldValue verifies that the
2179+
// dataSource argument is recorded on EntityFieldHash. Without this, dashboards
2180+
// cannot attribute a field hash to the subgraph that produced the data when
2181+
// multiple subgraphs contribute fields to the same entity.
2182+
func TestCacheAnalyticsCollector_DataSourceOnHashFieldValue(t *testing.T) {
2183+
c := NewCacheAnalyticsCollector()
2184+
2185+
// Same entity (User id=1) resolved by two subgraphs — the field-hash
2186+
// records must distinguish which subgraph each value came from.
2187+
c.HashFieldValue("User", "name", nil, []byte(`"Alice"`), `{"id":"1"}`, 0, FieldSourceL2, "accounts")
2188+
c.HashFieldValue("User", "reviews", nil, []byte(`[]`), `{"id":"1"}`, 0, FieldSourceSubgraph, "reviews")
2189+
2190+
snap := c.Snapshot()
2191+
require.Equal(t, 2, len(snap.FieldHashes))
2192+
assert.Equal(t, "accounts", snap.FieldHashes[0].DataSource, "first hash attributes to accounts subgraph")
2193+
assert.Equal(t, "reviews", snap.FieldHashes[1].DataSource, "second hash attributes to reviews subgraph")
2194+
}
2195+
2196+
// TestCacheAnalyticsCollector_EntityDataSource verifies the EntityDataSource
2197+
// reverse lookup returns the recorded subgraph and "" for unknown entities.
2198+
// Last-write-wins on duplicate keys, mirroring EntitySource semantics.
2199+
func TestCacheAnalyticsCollector_EntityDataSource(t *testing.T) {
2200+
c := NewCacheAnalyticsCollector()
2201+
2202+
// Three distinct entities owned by three subgraphs.
2203+
c.RecordEntitySource("User", `{"id":"1"}`, "accounts", FieldSourceSubgraph)
2204+
c.RecordEntitySource("Product", `{"upc":"top-1"}`, "products", FieldSourceL2)
2205+
c.RecordEntitySource("Review", `{"id":"r1"}`, "reviews", FieldSourceL1)
2206+
2207+
assert.Equal(t, "accounts", c.EntityDataSource("User", `{"id":"1"}`))
2208+
assert.Equal(t, "products", c.EntityDataSource("Product", `{"upc":"top-1"}`))
2209+
assert.Equal(t, "reviews", c.EntityDataSource("Review", `{"id":"r1"}`))
2210+
assert.Equal(t, "", c.EntityDataSource("Unknown", `{"id":"99"}`), "unknown entity returns empty string")
2211+
2212+
// Last-write-wins: a second record for the same (type, key) overrides.
2213+
c.RecordEntitySource("User", `{"id":"1"}`, "accounts-v2", FieldSourceL1)
2214+
assert.Equal(t, "accounts-v2", c.EntityDataSource("User", `{"id":"1"}`), "most recent record wins")
2215+
}
2216+
2217+
// TestCacheAnalyticsCollector_MutationEventDataSource verifies that DataSource
2218+
// is preserved through RecordMutationEvent. Mutation analytics need to know
2219+
// which subgraph owns the mutated entity for cross-subgraph dashboards.
2220+
func TestCacheAnalyticsCollector_MutationEventDataSource(t *testing.T) {
2221+
c := NewCacheAnalyticsCollector()
2222+
2223+
c.RecordMutationEvent(MutationEvent{
2224+
MutationRootField: "updateUsername",
2225+
EntityType: "User",
2226+
DataSource: "accounts",
2227+
EntityCacheKey: `{"__typename":"User","key":{"id":"1"}}`,
2228+
HadCachedValue: true,
2229+
IsStale: true,
2230+
})
2231+
2232+
snap := c.Snapshot()
2233+
require.Equal(t, 1, len(snap.MutationEvents))
2234+
assert.Equal(t, "accounts", snap.MutationEvents[0].DataSource)
2235+
}
2236+
2237+
// TestWalkCachedResponseForSources_DataSource verifies that
2238+
// walkCachedResponseForSources propagates the dataSource argument to every
2239+
// entity record it produces while traversing a cached JSON value. Without
2240+
// this propagation, L2 cache hits would land in entitySources without
2241+
// subgraph attribution and the resolvable would have to fall back to
2242+
// obj.SourceName for every entity rendered from cache.
2243+
func TestWalkCachedResponseForSources_DataSource(t *testing.T) {
2244+
a := arena.NewMonotonicArena(arena.WithMinBufferSize(1024))
2245+
parser := &astjson.Parser{}
2246+
2247+
value, err := parser.ParseBytesWithArena(a, []byte(`[{"id":"1","name":"Alice"},{"id":"2","name":"Bob"}]`))
2248+
require.NoError(t, err)
2249+
2250+
keyFields := []KeyField{{Name: "id"}}
2251+
var sources []entitySourceRecord
2252+
2253+
walkCachedResponseForSources(value, keyFields, "User", "accounts", FieldSourceL2, &sources)
2254+
2255+
require.Equal(t, 2, len(sources))
2256+
for i, want := range []string{`{"id":"1"}`, `{"id":"2"}`} {
2257+
assert.Equal(t, "User", sources[i].entityType)
2258+
assert.Equal(t, want, sources[i].keyJSON)
2259+
assert.Equal(t, FieldSourceL2, sources[i].source)
2260+
assert.Equal(t, "accounts", sources[i].dataSource, "every entity record must carry the dataSource")
2261+
}
2262+
}

v2/pkg/engine/resolve/loader_cache.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ func (l *Loader) tryL1CacheLoad(info *FetchInfo, cacheKeys []*CacheKey, res *res
958958
if len(res.cacheConfig.KeyFields) > 0 {
959959
keyJSON := buildEntityKeyJSON(cachedValue, res.cacheConfig.KeyFields)
960960
if len(keyJSON) > 0 {
961-
l.ctx.cacheAnalytics.RecordEntitySource(entityType, string(keyJSON), FieldSourceL1)
961+
l.ctx.cacheAnalytics.RecordEntitySource(entityType, string(keyJSON), dataSource, FieldSourceL1)
962962
}
963963
}
964964
}
@@ -1515,6 +1515,17 @@ func (l *Loader) applyEntityFetchL2Results(info *FetchInfo, res *result, state l
15151515
res.l2CacheKeys[i].fromCacheRemainingTTL = res.l1CacheKeys[i].fromCacheRemainingTTL
15161516
res.l2CacheKeys[i].fromCacheNeedsWriteback = res.l1CacheKeys[i].fromCacheNeedsWriteback
15171517

1518+
// Extract the entity key from the schema-shape value BEFORE
1519+
// denormalization. cacheConfig.KeyFields are schema names; once
1520+
// aliases are reapplied, an `id` key field would no longer match a
1521+
// `userId: id` aliased field and the entitySourceRecord would be
1522+
// silently dropped, leaving EntitySource / EntityDataSource lookups
1523+
// to fall back to defaults for cached alias reads.
1524+
var preDenormKeyJSON []byte
1525+
if state.analyticsEnabled && len(res.cacheConfig.KeyFields) > 0 && len(res.l1CacheKeys[i].Keys) > 0 {
1526+
preDenormKeyJSON = buildEntityKeyJSON(res.l1CacheKeys[i].FromCache, res.cacheConfig.KeyFields)
1527+
}
1528+
15181529
if state.hasAliases {
15191530
res.l1CacheKeys[i].FromCache = l.structuralCopyDenormalizedPassthrough(res.l1CacheKeys[i].FromCache, res.providesData)
15201531
}
@@ -1534,13 +1545,10 @@ func (l *Loader) applyEntityFetchL2Results(info *FetchInfo, res *result, state l
15341545
Kind: CacheKeyHit, DataSource: state.dataSource, ByteSize: byteSize,
15351546
CacheAgeMs: cacheAgeMs, Shadow: state.shadowMode,
15361547
})
1537-
if len(res.cacheConfig.KeyFields) > 0 {
1538-
keyJSON := buildEntityKeyJSON(res.l1CacheKeys[i].FromCache, res.cacheConfig.KeyFields)
1539-
if len(keyJSON) > 0 {
1540-
res.l2EntitySources = append(res.l2EntitySources, entitySourceRecord{
1541-
entityType: state.entityType, keyJSON: string(keyJSON), source: FieldSourceL2,
1542-
})
1543-
}
1548+
if len(preDenormKeyJSON) > 0 {
1549+
res.l2EntitySources = append(res.l2EntitySources, entitySourceRecord{
1550+
entityType: state.entityType, keyJSON: string(preDenormKeyJSON), source: FieldSourceL2, dataSource: state.dataSource,
1551+
})
15441552
}
15451553
}
15461554

@@ -1652,6 +1660,16 @@ func (l *Loader) applyRootFetchL2Results(info *FetchInfo, res *result, state l2C
16521660
continue
16531661
}
16541662

1663+
// Walk the schema-shape value for entitySourceRecord emission BEFORE
1664+
// denormalization. cacheConfig.KeyFields are schema names; once
1665+
// aliases are reapplied, an `id` key field would no longer match a
1666+
// `userId: id` aliased field and entity-source records would be
1667+
// silently dropped, leaving EntitySource / EntityDataSource lookups
1668+
// to fall back to defaults for cached alias reads.
1669+
if state.analyticsEnabled && len(res.cacheConfig.KeyFields) > 0 && len(ck.Keys) > 0 {
1670+
walkCachedResponseForSources(ck.FromCache, res.cacheConfig.KeyFields, state.entityType, state.dataSource, FieldSourceL2, &res.l2EntitySources)
1671+
}
1672+
16551673
if state.hasAliases {
16561674
if res.batchEntityKeyMode && state.batchEntityProvidesData != nil {
16571675
res.l2CacheKeys[i].FromCache = l.structuralCopyDenormalized(ck.FromCache, state.batchEntityProvidesData)
@@ -1672,9 +1690,6 @@ func (l *Loader) applyRootFetchL2Results(info *FetchInfo, res *result, state l2C
16721690
Kind: CacheKeyHit, DataSource: state.dataSource, ByteSize: byteSize,
16731691
CacheAgeMs: cacheAgeMs, Shadow: state.shadowMode,
16741692
})
1675-
if len(res.cacheConfig.KeyFields) > 0 {
1676-
walkCachedResponseForSources(res.l2CacheKeys[i].FromCache, res.cacheConfig.KeyFields, state.entityType, FieldSourceL2, &res.l2EntitySources)
1677-
}
16781693
}
16791694

16801695
if state.tracingCache {
@@ -2557,7 +2572,7 @@ func (l *Loader) compareShadowValues(res *result, info *FetchInfo) {
25572572
fieldBytes := fieldVal.MarshalTo(nil)
25582573
l.ctx.cacheAnalytics.HashFieldValue(
25592574
entityType, fieldName, fieldBytes,
2560-
keyRaw, 0, FieldSourceShadowCached,
2575+
keyRaw, 0, FieldSourceShadowCached, dataSource,
25612576
)
25622577
}
25632578
}
@@ -2724,6 +2739,7 @@ func (l *Loader) detectSingleMutationEntityImpact(
27242739
l.ctx.cacheAnalytics.RecordMutationEvent(MutationEvent{
27252740
MutationRootField: mutationFieldName,
27262741
EntityType: cfg.EntityTypeName,
2742+
DataSource: info.DataSourceName,
27272743
EntityCacheKey: displayKey,
27282744
HadCachedValue: false,
27292745
IsStale: false,

0 commit comments

Comments
 (0)