Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stale #849

Merged
merged 21 commits into from
Jan 30, 2023
Merged

stale #849

Show file tree
Hide file tree
Changes from 2 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 change: 1 addition & 0 deletions e2e/sveltekit/src/lib/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const routes = {
fetching_route_1: '/fetching/route_1',
lists_all: '/lists-all?limit=15',
union_result: '/union-result',
Stale_nodes: '/stale/nodes',

Stores_SSR: '/stores/ssr',
Stores_Network: '/stores/network',
Expand Down
5 changes: 5 additions & 0 deletions e2e/sveltekit/src/routes/stale/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<a href="nodes">Nodes</a>
<a href="add">Add</a>

<div />
<slot />
65 changes: 65 additions & 0 deletions e2e/sveltekit/src/routes/stale/add/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script>
import { cache, graphql } from '$houdini';

const addUser = graphql(`
mutation AddUserNode_FeatStale {
addUser(name: "New User", birthDate: 531747620000, snapshot: "UserNodes_FeatStale") {
id
}
}
`);

const markStale_everything = async () => {
await addUser.mutate(null);
cache.markStale();
};

const markStale_type = async () => {
await addUser.mutate(null);
cache.markStale('UserNodes');
};

const markStale_type_field = async () => {
await addUser.mutate(null);
cache.markStale('UserNodes', { field: 'totalCount' });
};

const markStale_subtype = async () => {
await addUser.mutate(null);
cache.markStale('User');
};

const markStale_entry = async () => {
await addUser.mutate(null);
const user = cache.get('User', { id: '1' });
user.markStale();
};

const markStale_entry_field = async () => {
await addUser.mutate(null);
const user = cache.get('User', { id: '1' });
user.markStale({ field: 'name' });
jycouet marked this conversation as resolved.
Show resolved Hide resolved
};

const markStale_entry_field_when = async () => {
await addUser.mutate(null);
const user = cache.get('User', { id: '1' });
user.markStale({ field: 'name', args: { name: 'New User' } });
};
</script>

<br />
<button on:click={markStale_everything}>markStale_everything</button>
<br />
<button on:click={markStale_type}>markStale_type</button>
<br />
<button on:click={markStale_type_field}>markStale_type_field</button>
<br />
<button on:click={markStale_subtype}>markStale_subtype</button>
<br />
<button on:click={markStale_entry}>markStale_entry</button>
<br />
<button on:click={markStale_entry_field}>markStale_entry_field</button>
<br />
<button on:click={markStale_entry_field_when}>markStale_entry_field_when</button>
<br />
23 changes: 23 additions & 0 deletions e2e/sveltekit/src/routes/stale/nodes/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script>
import { graphql } from '$houdini';
const store = graphql(`
query UserNodes_FeatStale {
userNodes(limit: 3, snapshot: "UserNodes_FeatStale") {
totalCount
nodes {
id
name
}
}
}
`);
</script>

<h4>Total count</h4>
<div id="result">
{$store.data?.userNodes.totalCount}
</div>
<h4>Info first 3</h4>
{#each $store.data?.userNodes.nodes ?? [] as user}
<div>{user?.name}</div>
{/each}
68 changes: 61 additions & 7 deletions packages/houdini/src/runtime/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { GarbageCollector } from './gc'
import type { ListCollection } from './lists'
import { ListManager } from './lists'
import { SchemaManager } from './schema'
import { StaleManager } from './staleManager'
import type { Layer, LayerID } from './storage'
import { InMemoryStorage } from './storage'
import { evaluateKey, flattenList } from './stuff'
Expand All @@ -30,6 +31,7 @@ export class Cache {
subscriptions: new InMemorySubscriptions(this),
lists: new ListManager(this, rootID),
lifetimes: new GarbageCollector(this),
staleManager: new StaleManager(this),
schema: new SchemaManager(this),
})

Expand All @@ -53,6 +55,7 @@ export class Cache {
applyUpdates?: boolean
notifySubscribers?: SubscriptionSpec[]
forceNotify?: boolean
forceStale?: boolean
}): SubscriptionSpec[] {
// find the correct layer
const layer = layerID
Expand Down Expand Up @@ -88,15 +91,16 @@ export class Cache {

// reconstruct an object with the fields/relations specified by a selection
read(...args: Parameters<CacheInternal['getSelection']>) {
const { data, partial, hasData } = this._internal_unstable.getSelection(...args)
const { data, partial, stale, hasData } = this._internal_unstable.getSelection(...args)

if (!hasData) {
return { data: null, partial: false }
return { data: null, partial: false, stale: false }
}

return {
data,
partial,
stale,
}
}

Expand Down Expand Up @@ -171,6 +175,7 @@ class CacheInternal {
lists: ListManager
cache: Cache
lifetimes: GarbageCollector
staleManager: StaleManager
schema: SchemaManager

constructor({
Expand All @@ -179,20 +184,23 @@ class CacheInternal {
lists,
cache,
lifetimes,
staleManager,
schema,
}: {
storage: InMemoryStorage
subscriptions: InMemorySubscriptions
lists: ListManager
cache: Cache
lifetimes: GarbageCollector
staleManager: StaleManager
schema: SchemaManager
}) {
this.storage = storage
this.subscriptions = subscriptions
this.lists = lists
this.cache = cache
this.lifetimes = lifetimes
this.staleManager = staleManager
this.schema = schema

// the cache should always be disabled on the server, unless we're testing
Expand All @@ -219,6 +227,7 @@ class CacheInternal {
layer,
toNotify = [],
forceNotify,
forceStale,
}: {
data: { [key: string]: GraphQLValue }
selection: SubscriptionSelection
Expand All @@ -229,6 +238,7 @@ class CacheInternal {
toNotify?: FieldSelection[]
applyUpdates?: boolean
forceNotify?: boolean
forceStale?: boolean
}): FieldSelection[] {
// if the cache is disabled, dont do anything
if (this._disabled) {
Expand Down Expand Up @@ -290,7 +300,22 @@ class CacheInternal {

// if we are writing to the display layer we need to refresh the lifetime of the value
if (displayLayer) {
this.lifetimes.resetLifetime(parent, key)
// JYC TODO: Type for parentType is not correct with linkedType. How to get parent type?
this.lifetimes.resetLifetime(linkedType, parent, key)

if (forceStale) {
this.cache._internal_unstable.staleManager.markFieldStale(
jycouet marked this conversation as resolved.
Show resolved Hide resolved
linkedType,
parent,
key
)
} else {
this.cache._internal_unstable.staleManager.setFieldTimeToNow(
linkedType,
parent,
key
)
}
}

// any scalar is defined as a field with no selection
Expand Down Expand Up @@ -688,10 +713,10 @@ class CacheInternal {
parent?: string
variables?: {}
stepsFromConnection?: number | null
}): { data: GraphQLObject | null; partial: boolean; hasData: boolean } {
}): { data: GraphQLObject | null; partial: boolean; stale: boolean; hasData: boolean } {
// we could be asking for values of null
if (parent === null) {
return { data: null, partial: false, hasData: true }
return { data: null, partial: false, stale: false, hasData: true }
}

const target = {} as GraphQLObject
Expand All @@ -705,6 +730,9 @@ class CacheInternal {
// that happens after we process every field to determine if its a partial null
let cascadeNull = false

// Check if we have at least one stale data
let stale = false

// if we have abstract fields, grab the __typename and include them in the list
const typename = this.storage.get(parent, '__typename').value as string
// collect all of the fields that we need to write
Expand All @@ -720,6 +748,12 @@ class CacheInternal {
// look up the value in our store
const { value } = this.storage.get(parent, key)

// If we have an explicite null, that mean that it's stale and the we should do a network call
const dt_field = this.staleManager.getFieldTime(typename, parent, key)
if (dt_field === null) {
stale = true
}

// in order to avoid falsey identifying the `cursor` field of a connection edge
// as missing non-nullable data (and therefor cascading null to the response) we need to
// count the number of steps since we saw a connection field and if we are at the
Expand Down Expand Up @@ -796,6 +830,10 @@ class CacheInternal {
partial = true
}

if (listValue.stale) {
stale = true
}

if (listValue.hasData || value.length === 0) {
hasData = true
}
Expand All @@ -819,6 +857,10 @@ class CacheInternal {
partial = true
}

if (objectFields.stale) {
stale = true
}

if (objectFields.hasData) {
hasData = true
}
Expand All @@ -836,6 +878,7 @@ class CacheInternal {
// our value is considered true if there is some data but not everything
// has a full value
partial: hasData && partial,
stale: hasData && stale,
hasData,
}
}
Expand Down Expand Up @@ -878,12 +921,13 @@ class CacheInternal {
variables?: {}
linkedList: LinkedList
stepsFromConnection: number | null
}): { data: LinkedList<GraphQLValue>; partial: boolean; hasData: boolean } {
}): { data: LinkedList<GraphQLValue>; partial: boolean; stale: boolean; hasData: boolean } {
// the linked list could be a deeply nested thing, we need to call getData for each record
// we can't mutate the lists because that would change the id references in the listLinks map
// to the corresponding record. can't have that now, can we?
const result: LinkedList<GraphQLValue> = []
let partialData = false
let stale = false
let hasValues = false

for (const entry of linkedList) {
Expand All @@ -909,7 +953,12 @@ class CacheInternal {
}

// look up the data for the record
const { data, partial, hasData } = this.getSelection({
const {
data,
partial,
stale: local_stale,
hasData,
} = this.getSelection({
parent: entry,
selection: fields,
variables,
Expand All @@ -922,6 +971,10 @@ class CacheInternal {
partialData = true
}

if (local_stale) {
stale = true
}

if (hasData) {
hasValues = true
}
Expand All @@ -930,6 +983,7 @@ class CacheInternal {
return {
data: result,
partial: partialData,
stale,
hasData: hasValues,
}
}
Expand Down
Loading