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

feat(gatsby, gatsby-source-contentful): add public action to disable stale node checks #37782

Merged
merged 44 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
73fc998
add touch nodes optout action
TylerBarnes Mar 20, 2023
3a90c4e
cleanup
TylerBarnes Mar 24, 2023
bc10de5
revert formatting
TylerBarnes Mar 27, 2023
b7856b1
remove unused variable
TylerBarnes Mar 27, 2023
e3b5356
remove unused var
TylerBarnes Mar 27, 2023
f4de579
switch from opting out types from stale nodes to opting out entire pl…
TylerBarnes Mar 29, 2023
05bb75b
add no nodes warning
TylerBarnes Mar 29, 2023
0eae24b
fix bug where double bound actions are ignored when there are no args
TylerBarnes Mar 29, 2023
711e5f1
update progress mock
TylerBarnes Mar 29, 2023
006781d
get owner from plugin
TylerBarnes Mar 29, 2023
ed37dbe
rename type
TylerBarnes Mar 29, 2023
34afa79
rename types
TylerBarnes Mar 29, 2023
d643f22
use redux types instead of pulling from lmdb
TylerBarnes Mar 29, 2023
9edecac
remove unused line
TylerBarnes Mar 29, 2023
ba945e9
Update source-nodes.ts
TylerBarnes Mar 29, 2023
7db6014
use CREATE_NODE action instead of adding a new type owner action
TylerBarnes Mar 29, 2023
8000e0f
Merge branch 'master' into feat/disable-node-gc-by-type
TylerBarnes Mar 29, 2023
a0914e0
add typeowners test
TylerBarnes Mar 29, 2023
bc48550
test touchNodes and enableStatefulSourceNodes()
TylerBarnes Mar 30, 2023
fa7b00c
fix contentful tests
TylerBarnes Mar 30, 2023
4422dce
snapshot updates
TylerBarnes Mar 30, 2023
d5db06b
chore(changelogs): update changelogs (#37808)
gatsbybot Mar 30, 2023
c2bb5d5
fix(deps): update starters and examples - gatsby to ^5.8.1 (#37806)
renovate[bot] Mar 30, 2023
b55761d
fix(gatsby): Validate sub plugins options (#37804)
Talaxy009 Mar 30, 2023
95a2944
fix(create-gatsby): Use correct name in summary message (#37809)
LekoArts Mar 30, 2023
2fbc24d
chore(release): Publish next
pieh Mar 30, 2023
8be2932
Merge branch 'master' into feat/disable-node-gc-by-type
TylerBarnes Mar 30, 2023
70e77ff
remove testing timeout
TylerBarnes Mar 30, 2023
de0a235
minimal docs
TylerBarnes Mar 31, 2023
bdcc992
reword
TylerBarnes Mar 31, 2023
a89bb13
add comment
TylerBarnes Mar 31, 2023
b83d888
reportOnce instead of throwing an error
TylerBarnes Mar 31, 2023
e859aa8
consolidate typeOwners
TylerBarnes Mar 31, 2023
6ebba79
use new typesToPlugins Map keys instead of pluginsToTypes Map values
TylerBarnes Mar 31, 2023
13df0c2
consolidate remaining typeOwners object checks into new typeOwners re…
TylerBarnes Apr 3, 2023
c7195ef
fix missing owner error
TylerBarnes Apr 3, 2023
acb13d9
fix type errors and incorrect plugin object reference
TylerBarnes Apr 3, 2023
236f29f
maybe fix unit tests
pieh Apr 4, 2023
1ddaa40
make SitePage nodes owned by internal-data-bridge
pieh Apr 4, 2023
1405646
add missing fields on some TOUCH_NODE actions
pieh Apr 4, 2023
b121925
skip owner checks when deleting child nodes
pieh Apr 4, 2023
7760b01
sp
TylerBarnes Apr 4, 2023
11ff633
Merge branch 'master' into feat/disable-node-gc-by-type
TylerBarnes Apr 4, 2023
f86ca48
Update yarn.lock
TylerBarnes Apr 4, 2023
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
3 changes: 2 additions & 1 deletion packages/gatsby-source-contentful/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { getGatsbyVersion } from "gatsby-core-utils"
import { lt, prerelease } from "semver"

const typePrefix = `Contentful`
const makeTypeName = type => _.upperFirst(_.camelCase(`${typePrefix} ${type}`))
export const makeTypeName = type =>
_.upperFirst(_.camelCase(`${typePrefix} ${type}`))

const GATSBY_VERSION_MANIFEST_V2 = `4.3.0`
const gatsbyVersion =
Expand Down
25 changes: 20 additions & 5 deletions packages/gatsby-source-contentful/src/source-nodes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
import { hasFeature } from "gatsby-plugin-utils/has-feature"
import isOnline from "is-online"
import _ from "lodash"

Expand All @@ -11,6 +12,7 @@ import {
createAssetNodes,
createNodesForContentType,
makeId,
makeTypeName,
} from "./normalize"
import { createPluginConfig } from "./plugin-options"
import { CODES } from "./report"
Expand Down Expand Up @@ -40,7 +42,7 @@ const CONTENT_DIGEST_COUNTER_SEPARATOR = `_COUNT_`
* or the fallback field or the default field.
*/

let isFirstSource = true
let isFirstSourceNodesCallOfCurrentNodeProcess = true
export async function sourceNodes(
{
actions,
Expand All @@ -55,14 +57,26 @@ export async function sourceNodes(
},
pluginOptions
) {
const { createNode, touchNode, deleteNode, unstable_createNodeManifest } =
actions
const hasStatefulSourceNodes = hasFeature(`stateful-source-nodes`)
const needToTouchNodes = !hasStatefulSourceNodes

const {
createNode,
touchNode,
deleteNode,
unstable_createNodeManifest,
enableStatefulSourceNodes,
} = actions

const online = await isOnline()

if (hasStatefulSourceNodes) {
enableStatefulSourceNodes()
}
// Gatsby only checks if a node has been touched on the first sourcing.
// As iterating and touching nodes can grow quite expensive on larger sites with
// 1000s of nodes, we'll skip doing this on subsequent sources.
if (isFirstSource) {
else if (isFirstSourceNodesCallOfCurrentNodeProcess && needToTouchNodes) {
getNodes().forEach(node => {
if (node.internal.owner !== `gatsby-source-contentful`) {
return
Expand All @@ -73,9 +87,10 @@ export async function sourceNodes(
touchNode(getNode(node.fields.localFile))
}
})
isFirstSource = false
}

isFirstSourceNodesCallOfCurrentNodeProcess = false

if (
!online &&
process.env.GATSBY_CONTENTFUL_OFFLINE === `true` &&
Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type AvailableFeatures =
| "image-cdn"
| "graphql-typegen"
| "content-file-path"
| "stateful-source-nodes"

export {
Link,
Expand Down Expand Up @@ -433,6 +434,11 @@ export interface GatsbyNode<
calllback: PluginCallback<void>
): void | Promise<void>

/**
* Marks the source plugin that called this function as stateful. Gatsby will not check for stale nodes for any plugin that calls this.
*/
enableStatefulSourceNodes?(this: void, plugin?: ActionPlugin)

/**
* Called when a new node is created. Plugins wishing to extend or
* transform nodes created by other plugins should implement this API.
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby/scripts/output-api-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function outputFile() {
}, {})

/** @type {Array<import("../index").AvailableFeatures>} */
output.features = ["image-cdn", "graphql-typegen", "content-file-path", "slices"];
output.features = ["image-cdn", "graphql-typegen", "content-file-path", "slices", "stateful-source-nodes"];

return fs.writeFile(
path.resolve(OUTPUT_FILE_NAME),
Expand Down
7 changes: 7 additions & 0 deletions packages/gatsby/src/query/__tests__/data-tracking.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ jest.mock(`gatsby-cli/lib/reporter`, () => {
end: jest.fn(),
}
},
createProgress: () => {
return {
start: jest.fn(),
tick: jest.fn(),
end: jest.fn(),
}
},
phantomActivity: () => {
return {
start: jest.fn(),
Expand Down
28 changes: 22 additions & 6 deletions packages/gatsby/src/redux/actions/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,6 @@ ${reservedFields.map(f => ` * "${f}"`).join(`\n`)}
return actions
}

const deleteNodeDeprecationWarningDisplayedMessages = new Set()
TylerBarnes marked this conversation as resolved.
Show resolved Hide resolved

/**
* Delete a node
* @param {object} node A node object. See the "createNode" action for more information about the node object details.
Expand Down Expand Up @@ -879,10 +877,18 @@ const createNode = (
}
}

const setTypeOwnerAction = {
type: `SET_TYPE_OWNER`,
plugin,
payload: {
typeName: node.internal.type,
},
}
TylerBarnes marked this conversation as resolved.
Show resolved Hide resolved

if (deleteActions && deleteActions.length) {
return [...deleteActions, updateNodeAction]
return [...deleteActions, updateNodeAction, setTypeOwnerAction]
} else {
return updateNodeAction
return [updateNodeAction, setTypeOwnerAction]
}
}

Expand Down Expand Up @@ -921,8 +927,6 @@ actions.createNode =
}
}

const touchNodeDeprecationWarningDisplayedMessages = new Set()
TylerBarnes marked this conversation as resolved.
Show resolved Hide resolved

/**
* "Touch" a node. Tells Gatsby a node still exists and shouldn't
* be garbage collected. Primarily useful for source plugins fetching
Expand Down Expand Up @@ -1513,6 +1517,18 @@ actions.unstable_createNodeManifest = (
}
}

/**
* Marks a source plugin as "stateful" which disables automatically deleting untouched nodes. Stateful source plugins manage deleting their own nodes without stale node checks in Gatsby.
*
* @param {void} $0
*/
actions.enableStatefulSourceNodes = (plugin: Plugin) => {
return {
type: `ENABLE_STATEFUL_SOURCE_PLUGIN`,
plugin,
}
}

/**
* Stores request headers for a given domain to be later used when making requests for Image CDN (and potentially other features).
*
Expand Down
2 changes: 2 additions & 0 deletions packages/gatsby/src/redux/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export const saveState = (): void => {

return writeToCache({
nodes: state.nodes,
pluginNamesToOwnedNodeTypes: state.pluginNamesToOwnedNodeTypes,
statefulSourcePlugins: state.statefulSourcePlugins,
status: state.status,
components: state.components,
jobsV2: state.jobsV2,
Expand Down
4 changes: 4 additions & 0 deletions packages/gatsby/src/redux/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { pluginNamesToOwnedNodeTypesReducer } from "./type-owners"
import { nodesReducer } from "./nodes"
import { reducer as logReducer } from "gatsby-cli/lib/reporter/redux/reducers/logs"
import { pagesReducer } from "./pages"
Expand Down Expand Up @@ -32,6 +33,7 @@ import { telemetryReducer } from "./telemetry"
import { nodeManifestReducer } from "./node-manifest"
import { reducer as pageTreeReducer } from "gatsby-cli/lib/reporter/redux/reducers/page-tree"
import { setRequestHeadersReducer } from "./set-request-headers"
import { statefulSourcePluginsReducer } from "./stateful-source-plugins"
import { slicesReducer } from "./slices"
import { componentsUsingSlicesReducer } from "./components-using-slices"
import { slicesByTemplateReducer } from "./slices-by-template"
Expand All @@ -42,6 +44,7 @@ import { slicesByTemplateReducer } from "./slices-by-template"
export {
definitionsReducer as definitions,
programReducer as program,
pluginNamesToOwnedNodeTypesReducer as pluginNamesToOwnedNodeTypes,
nodesReducer as nodes,
nodesByTypeReducer as nodesByType,
resolvedNodesCacheReducer as resolvedNodesCache,
Expand Down Expand Up @@ -73,6 +76,7 @@ export {
nodeManifestReducer as nodeManifests,
pageTreeReducer as pageTree,
setRequestHeadersReducer as requestHeaders,
statefulSourcePluginsReducer as statefulSourcePlugins,
slicesReducer as slices,
componentsUsingSlicesReducer as componentsUsingSlices,
slicesByTemplateReducer as slicesByTemplate,
Expand Down
19 changes: 19 additions & 0 deletions packages/gatsby/src/redux/reducers/stateful-source-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IGatsbyState, IEnableStatefulSourcePluginAction } from "../types"

/**
* Flags a source plugin as being "stateful" which means it manages its own data updates and Gatsby doesn't look for "stale" nodes after each `sourceNodes` run.
*/
export const statefulSourcePluginsReducer = (
statefulSourcePlugins: IGatsbyState["statefulSourcePlugins"] = new Set(),
action: IEnableStatefulSourcePluginAction
): IGatsbyState["statefulSourcePlugins"] => {
switch (action.type) {
case `ENABLE_STATEFUL_SOURCE_PLUGIN`: {
statefulSourcePlugins.add(action.plugin.name)

return statefulSourcePlugins
}
default:
return statefulSourcePlugins
}
}
26 changes: 26 additions & 0 deletions packages/gatsby/src/redux/reducers/type-owners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ActionsUnion, IGatsbyState } from "../types"

export const pluginNamesToOwnedNodeTypesReducer = (
pluginNamesToOwnedNodeTypes: IGatsbyState["pluginNamesToOwnedNodeTypes"] = new Map(),
action: ActionsUnion
): IGatsbyState["pluginNamesToOwnedNodeTypes"] => {
switch (action.type) {
case `SET_TYPE_OWNER`: {
const { typeName } = action.payload
const owner = action.plugin.name

const existingOwner = pluginNamesToOwnedNodeTypes.get(owner)

if (!existingOwner) {
pluginNamesToOwnedNodeTypes.set(owner, new Set([typeName]))
} else {
existingOwner.add(typeName)
}

return pluginNamesToOwnedNodeTypes
}

default:
return pluginNamesToOwnedNodeTypes
}
}
22 changes: 22 additions & 0 deletions packages/gatsby/src/redux/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,13 @@ export interface IGatsbyState {
nodesByType: Map<string, GatsbyNodes>
resolvedNodesCache: Map<string, any> // TODO
nodesTouched: Set<string>
pluginNamesToOwnedNodeTypes: Map<
IGatsbyPlugin[`name`],
Set<IGatsbyNode[`internal`][`type`]>
>
nodeManifests: Array<INodeManifest>
requestHeaders: Map<string, { [header: string]: string }>
statefulSourcePlugins: Set<string>
telemetry: ITelemetry
lastAction: ActionsUnion
flattenedPlugins: Array<{
Expand Down Expand Up @@ -397,6 +402,8 @@ export type GatsbyStateKeys = keyof IGatsbyState

export interface ICachedReduxState {
nodes?: IGatsbyState["nodes"]
pluginNamesToOwnedNodeTypes?: IGatsbyState["pluginNamesToOwnedNodeTypes"]
statefulSourcePlugins?: IGatsbyState["statefulSourcePlugins"]
status: IGatsbyState["status"]
components: IGatsbyState["components"]
jobsV2: IGatsbyState["jobsV2"]
Expand All @@ -418,6 +425,7 @@ export type ActionsUnion =
| IAddThirdPartySchema
| IApiFinishedAction
| ICreateFieldExtension
| ISetTypeOwnerAction
| ICreateNodeAction
| ICreatePageAction
| ICreatePageDependencyAction
Expand Down Expand Up @@ -487,6 +495,7 @@ export type ActionsUnion =
| ISetJobV2Context
| IClearJobV2Context
| ISetDomainRequestHeaders
| IEnableStatefulSourcePluginAction
| ICreateSliceAction
| IDeleteSliceAction
| ISetSSRTemplateWebpackCompilationHashAction
Expand Down Expand Up @@ -984,6 +993,14 @@ export interface ISetSiteFunctions {
payload: IGatsbyState["functions"]
}

export interface ISetTypeOwnerAction {
type: `SET_TYPE_OWNER`
plugin: IGatsbyPlugin
payload: {
typeName: IGatsbyNode["internal"]["type"]
}
}

export interface ICreateNodeAction {
type: `CREATE_NODE`
payload: IGatsbyNode
Expand Down Expand Up @@ -1135,6 +1152,11 @@ export interface ISetDomainRequestHeaders {
}
}

export interface IEnableStatefulSourcePluginAction {
type: `ENABLE_STATEFUL_SOURCE_PLUGIN`
plugin: IGatsbyPlugin
}

export interface IProcessGatsbyImageSourceUrlAction {
type: `PROCESS_GATSBY_IMAGE_SOURCE_URL`
payload: {
Expand Down
10 changes: 8 additions & 2 deletions packages/gatsby/src/utils/api-runner-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,20 @@ const doubleBind = (boundActionCreators, api, plugin, actionOptions) => {
const boundActionCreator = boundActionCreators[key]
if (typeof boundActionCreator === `function`) {
doubleBoundActionCreators[key] = (...args) => {
if (args.length === 0) {
return boundActionCreator(plugin, actionOptions)
}
// Let action callers override who the plugin is. Shouldn't be
// used that often.
if (args.length === 1) {
else if (args.length === 1) {
return boundActionCreator(args[0], plugin, actionOptions)
} else if (args.length === 2) {
return boundActionCreator(args[0], args[1], actionOptions)
}
return undefined

throw new Error(
`Unhandled redux action: ${key}, in plugin: ${plugin.name}`
)
TylerBarnes marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Expand Down
Loading