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

[v2] Hulksmash build slowdowns on larger sites #6226

Merged
merged 32 commits into from
Jul 11, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ac4a874
Hulksmash slowdowns on larger sites
KyleAMathews Jun 29, 2018
cb33713
Switch more existsSync to use caching version
KyleAMathews Jun 30, 2018
253e353
Convert node reducer to use Map
KyleAMathews Jun 30, 2018
84c516a
Fix most lint errors
KyleAMathews Jun 30, 2018
b647bb1
Merge branch 'master' into speed-large-site
KyleAMathews Jul 3, 2018
266f5ae
Split writing page data json files into 999 folders to speed writes
KyleAMathews Jul 4, 2018
110b99e
Use forEach instead of reduce when prepping page data
KyleAMathews Jul 5, 2018
710d9d8
Profiled code and fixed hot functions
KyleAMathews Jul 6, 2018
b039a64
Merge remote-tracking branch 'origin/master' into speed-large-site
KyleAMathews Jul 6, 2018
6a8e9a0
WIP commit to dramatically speed up graphql queries
KyleAMathews Jul 7, 2018
044e7b6
Handle picking directory to write StaticQuery results
KyleAMathews Jul 9, 2018
f231e08
Speed up resolving queries when the query is querying a node by id
KyleAMathews Jul 9, 2018
0c43b29
Show pages rendered / second while building HTML
KyleAMathews Jul 10, 2018
109d691
Add sites for benchmarking
KyleAMathews Jul 10, 2018
272e6b0
Persist activity status at end and show queries/second for graphql
KyleAMathews Jul 10, 2018
19f6c3b
Restore creating SitePage nodes as no longer slow
KyleAMathews Jul 10, 2018
a46c207
Correct storing/using/deleting nodes
KyleAMathews Jul 10, 2018
fe4125b
Disable profiling
KyleAMathews Jul 10, 2018
d3a95a0
Remove extra dependencies
KyleAMathews Jul 10, 2018
d6eefc5
Format + fix linting
KyleAMathews Jul 10, 2018
b729410
remove console.log and unused profile
KyleAMathews Jul 11, 2018
34db11f
Remove another profile
KyleAMathews Jul 11, 2018
daf690f
Fix tests (hopefully)
KyleAMathews Jul 11, 2018
db319af
Merge remote-tracking branch 'origin/master' into speed-large-site
KyleAMathews Jul 11, 2018
d3acb9e
Check if nodes exists, sometimes a test fails otherwise
KyleAMathews Jul 11, 2018
145a34b
debugging...
KyleAMathews Jul 11, 2018
c85d41e
Work around...
KyleAMathews Jul 11, 2018
2cb28c7
Don't need to run bootstrap on tests as jest compiles code
KyleAMathews Jul 11, 2018
b7ebd8e
Lodash uses Object.entries which fails on node 6... :-"
KyleAMathews Jul 11, 2018
a4a1391
Ok, do need bootstrap to link packages
KyleAMathews Jul 11, 2018
aa47043
Try try again
KyleAMathews Jul 11, 2018
fa32e5d
Ugh, problem was Object.entries isn't in Node 6
KyleAMathews Jul 11, 2018
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 packages/gatsby-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"core-js": "^2.5.0",
"envinfo": "^5.8.1",
"execa": "^0.8.0",
"fs-exists-cached": "^1.0.0",
"fs-extra": "^4.0.1",
"hosted-git-info": "^2.6.0",
"lodash": "^4.17.4",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-cli/src/create-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const path = require(`path`)
const resolveCwd = require(`resolve-cwd`)
const yargs = require(`yargs`)
const report = require(`./reporter`)
const fs = require(`fs`)
const envinfo = require(`envinfo`)
const existsSync = require(`fs-exists-cached`).sync

const DEFAULT_BROWSERS = [`>0.25%`, `not dead`]

Expand All @@ -19,7 +19,7 @@ function buildLocalCommands(cli, isLocalSite) {
const directory = path.resolve(`.`)

let siteInfo = { directory, browserslist: DEFAULT_BROWSERS }
const useYarn = fs.existsSync(path.join(directory, `yarn.lock`))
const useYarn = existsSync(path.join(directory, `yarn.lock`))
if (isLocalSite) {
const json = require(path.join(directory, `package.json`))
siteInfo.sitePackageJson = json
Expand Down
5 changes: 3 additions & 2 deletions packages/gatsby-cli/src/init-starter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fs = require(`fs-extra`)
const sysPath = require(`path`)
const report = require(`./reporter`)
const url = require(`url`)
const existsSync = require(`fs-exists-cached`).sync

const spawn = (cmd: string) => {
const [file, ...args] = cmd.split(/\s+/)
Expand Down Expand Up @@ -49,7 +50,7 @@ const copy = async (starterPath: string, rootPath: string) => {
// 493 = parseInt('755', 8)
await fs.mkdirp(rootPath, { mode: 493 })

if (!fs.existsSync(starterPath)) {
if (!existsSync(starterPath)) {
throw new Error(`starter ${starterPath} doesn't exist`)
}

Expand Down Expand Up @@ -117,7 +118,7 @@ module.exports = async (starter: string, options: InitOptions = {}) => {
return
}

if (fs.existsSync(sysPath.join(rootPath, `package.json`))) {
if (existsSync(sysPath.join(rootPath, `package.json`))) {
report.panic(`Directory ${rootPath} is already an npm project`)
return
}
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-plugin-netlify/src/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ exports.onPostBuild = async ({ store, pathPrefix }, userPluginOptions) => {
let rewrites = []
if (pluginOptions.generateMatchPathRewrites) {
const { pages } = store.getState()
rewrites = pages
rewrites = Array.from(pages.values())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to gain some more speed using reduce:

rewrites = Array.from(pages.values())
	.reduce((acc, page) => {
  	if (page.matchPath && page.matchPath !== page.path) {
    	acc.push({
        fromPath: page.matchPath,
        toPath: page.path,
      })
    }
    
    return acc;
  }, [])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would reducing be faster than filtering?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You get to avoid using map to iterate through the generated array from the filter. With reduce you generate the end result in a single iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah. Generally there'll only a handful of pages w/ matchPath so making the change should make a negligible difference 🤷‍♂️

That being said, if you'd like to make the PR, happy to take it! Thanks for reading through the PR!

.filter(page => page.matchPath && page.matchPath !== page.path)
.map(page => {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-plugin-page-creator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@babel/runtime": "7.0.0-beta.51",
"bluebird": "^3.5.0",
"chokidar": "^1.7.0",
"fs-exists-cached": "^1.0.0",
"glob": "^7.1.1",
"lodash": "^4.17.4",
"parse-filepath": "^1.0.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-page-creator/src/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const Promise = require(`bluebird`)
const _ = require(`lodash`)
const chokidar = require(`chokidar`)
const systemPath = require(`path`)
const fs = require(`fs`)
const existsSync = require(`fs-exists-cached`).sync

const glob = Promise.promisify(globCB)

Expand Down Expand Up @@ -35,7 +35,7 @@ exports.createPagesStatefully = async (
}

// Validate that the path exists.
if (pathCheck && !fs.existsSync(pagesPath)) {
if (pathCheck && !existsSync(pagesPath)) {
reporter.panic(
`
The path passed to gatsby-plugin-page-creator does not exist on your file system:
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-plugin-sharp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@babel/runtime": "7.0.0-beta.51",
"async": "^2.1.2",
"bluebird": "^3.5.0",
"fs-exists-cached": "^1.0.0",
"imagemin": "^5.2.2",
"imagemin-pngquant": "^5.1.0",
"imagemin-webp": "^4.0.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/gatsby-plugin-sharp/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const imageminPngquant = require(`imagemin-pngquant`)
const imageminWebp = require(`imagemin-webp`)
const queue = require(`async/queue`)
const path = require(`path`)
const existsSync = require(`fs-exists-cached`).sync

const imageSizeCache = new Map()
const getImageSize = file => {
Expand Down Expand Up @@ -245,7 +246,7 @@ const queueJob = (job, reporter) => {
}

// Check if the output file already exists so we don't redo work.
if (fs.existsSync(job.outputPath)) {
if (existsSync(job.outputPath)) {
return
}

Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby-transformer-remark/src/extend-node-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const remark2retext = require(`remark-retext`)
const stripPosition = require(`unist-util-remove-position`)
const hastReparseRaw = require(`hast-util-raw`)

global.mdToHTML = []

let pluginsCacheStr = ``
let pathPrefixCacheStr = ``
const astCacheKey = node =>
Expand Down Expand Up @@ -280,6 +282,7 @@ module.exports = (
if (cachedHTML) {
return cachedHTML
} else {
const start = process.hrtime()
const ast = await getHTMLAst(markdownNode)
// Save new HTML to cache and return
const html = hastToHTML(ast, {
Expand All @@ -288,6 +291,9 @@ module.exports = (

// Save new HTML to cache and return
cache.set(htmlCacheKey(markdownNode), html)
global.mdToHTML.push(
require(`convert-hrtime`)(process.hrtime(start)).milliseconds
)
return html
}
}
Expand Down
6 changes: 5 additions & 1 deletion packages/gatsby/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const apiRunner = require(`./api-runner-ssr`)
const syncRequires = require(`./sync-requires`)
const { dataPaths, pages } = require(`./data.json`)

// Speed up looking up pages.
const pagesObjectMap = new Map()
pages.forEach(p => pagesObjectMap.set(p.path, p))

const stats = JSON.parse(
fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`)
)
Expand Down Expand Up @@ -48,7 +52,7 @@ function urlJoin(...parts) {
}, ``)
}

const getPage = path => pages.find(page => page.path === path)
const getPage = path => pagesObjectMap.get(path)

const createElement = React.createElement

Expand Down
2 changes: 2 additions & 0 deletions packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"graphql-relay": "^0.5.5",
"graphql-skip-limit": "^2.0.0-beta.2",
"graphql-type-json": "^0.2.1",
"hash-mod": "^0.0.5",
"history": "^4.6.2",
"invariant": "^2.2.4",
"is-relative": "^1.0.0",
Expand Down Expand Up @@ -117,6 +118,7 @@
"socket.io": "^2.0.3",
"string-similarity": "^1.2.0",
"style-loader": "^0.19.1",
"treeify": "^1.1.0",
"type-of": "^2.0.1",
"uglifyjs-webpack-plugin": "^1.2.4",
"url-loader": "^1.0.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/gatsby/src/bootstrap/get-config-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const testRequireError = require(`../utils/test-require-error`).default
const report = require(`gatsby-cli/lib/reporter`)
const chalk = require(`chalk`)
const path = require(`path`)
const existsSync = require(`fs-exists-cached`).sync

function isNearMatch(
fileName: string,
Expand Down Expand Up @@ -45,7 +46,7 @@ module.exports = async function getConfigFile(
)
console.log(``)
process.exit(1)
} else if (fs.existsSync(path.join(rootDir, `src`, configName))) {
} else if (existsSync(path.join(rootDir, `src`, configName))) {
console.log(``)
report.error(
`Your ${configName} file is in the wrong place. You've placed it in the src/ directory. It must instead be at the root of your site next to your package.json file.`
Expand Down
9 changes: 7 additions & 2 deletions packages/gatsby/src/bootstrap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ module.exports = async (args: BootstrapArgs) => {
// directory.
initCache()

// Ensure the public/static directory is created.
await fs.ensureDirSync(`${program.directory}/public/static/d`)
// Ensure the public/static directory and data subdirectories are created.
await fs.ensureDir(`${program.directory}/public/static`)
await Promise.all(
_.range(0, 999).map(i =>
fs.ensureDir(`${program.directory}/public/static/d/${i}`)
)
)

// Copy our site files to the root of the site.
activity = report.activityTimer(`copy gatsby files`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Array [
"name": "internal-data-bridge",
"nodeAPIs": Array [
"sourceNodes",
"onCreatePage",
],
"pluginOptions": Object {
"plugins": Array [],
Expand Down Expand Up @@ -140,7 +139,6 @@ Array [
"name": "internal-data-bridge",
"nodeAPIs": Array [
"sourceNodes",
"onCreatePage",
],
"pluginOptions": Object {
"plugins": Array [],
Expand Down
7 changes: 4 additions & 3 deletions packages/gatsby/src/bootstrap/load-plugins/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require(`path`)
const crypto = require(`crypto`)
const glob = require(`glob`)
const { store } = require(`../../redux`)
const existsSync = require(`fs-exists-cached`).sync

function createFileContentHash(root, globPattern) {
const hash = crypto.createHash(`md5`)
Expand Down Expand Up @@ -34,12 +35,12 @@ function createFileContentHash(root, globPattern) {
*/
function resolvePlugin(pluginName) {
// Only find plugins when we're not given an absolute path
if (!fs.existsSync(pluginName)) {
if (!existsSync(pluginName)) {
// Find the plugin in the local plugins folder
const resolvedPath = slash(path.resolve(`./plugins/${pluginName}`))

if (fs.existsSync(resolvedPath)) {
if (fs.existsSync(`${resolvedPath}/package.json`)) {
if (existsSync(resolvedPath)) {
if (existsSync(`${resolvedPath}/package.json`)) {
const packageJSON = JSON.parse(
fs.readFileSync(`${resolvedPath}/package.json`, `utf-8`)
)
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby/src/commands/build-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = async (program: any) => {

debug(`generating static HTML`)
// Reduce pages objects to an array of paths.
const pages = store.getState().pages.map(page => page.path)
const pages = Array.from(store.getState().pages.values(), page => page.path)

// Static site generation.
const compilerConfig = await webpackConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function transformPackageJson(json) {
return json
}

const createPageId = path => `SitePage ${path}`
exports.sourceNodes = ({ actions, store }) => {
const { createNode } = actions
const state = store.getState()
Expand Down Expand Up @@ -136,37 +137,3 @@ exports.sourceNodes = ({ actions, store }) => {
}
})
}

const createPageId = path => `SitePage ${path}`

exports.onCreatePage = ({ page, actions }) => {
const { createNode } = actions
// eslint-disable-next-line
const { updatedAt, ...pageWithoutUpdated } = page

// Add page.
createNode({
...pageWithoutUpdated,
id: createPageId(page.path),
parent: `SOURCE`,
children: [],
internal: {
type: `SitePage`,
contentDigest: crypto
.createHash(`md5`)
.update(JSON.stringify(page))
.digest(`hex`),
description:
page.pluginCreatorId === `Plugin default-site-plugin`
? `Your site's "gatsby-node.js"`
: page.pluginCreatorId,
},
})
}

// Listen for DELETE_PAGE and delete page nodes.
emitter.on(`DELETE_PAGE`, action => {
const nodeId = createPageId(action.payload.path)
const node = getNode(nodeId)
boundActionCreators.deleteNode({ node })
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require(`path`)
const json5 = require(`json5`)
const report = require(`gatsby-cli/lib/reporter`)
const { actionifyBabelrc, addDefaultPluginsPresets } = require(`./utils`)
const existsSync = require(`fs-exists-cached`).sync

const testRequireError = require(`../../utils/test-require-error`).default

Expand All @@ -15,7 +16,7 @@ const testRequireError = require(`../../utils/test-require-error`).default
*/
function findBabelrc(directory) {
const babelrcPath = path.join(directory, `.babelrc`)
if (fs.existsSync(babelrcPath)) {
if (existsSync(babelrcPath)) {
try {
const babelrc = fs.readFileSync(babelrcPath, `utf-8`)
return json5.parse(babelrc)
Expand Down
21 changes: 15 additions & 6 deletions packages/gatsby/src/internal-plugins/query-runner/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ const { watchComponent } = require(`./query-watcher`)
let components = {}

exports.onCreatePage = ({ page, store }) => {
const component = store.getState().components.get(page.componentPath)
// In development, watch the component to detect query changes.
if (process.env.NODE_ENV !== `production`) {
const component = store.getState().components.get(page.componentPath)

if (components[component.componentPath]) {
return
}
if (!component) {
console.log({ components: store.getState().components })
console.log({ page })
console.log({ component })
process.exit()
}

if (components[component.componentPath]) {
return
}

// Watch the component to detect query changes.
watchComponent(component.componentPath)
watchComponent(component.componentPath)
}
}
Loading