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] improve how we load GraphQL query results in development/production (aka Ludicrous Mode) #4555

Merged
merged 38 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e218c1f
Create placeholder JSON store
m-allanson Mar 15, 2018
0894947
Rename
m-allanson Mar 15, 2018
4ca7dda
Websocket placeholder
m-allanson Mar 15, 2018
cf55f6b
Push query results JSON over websockets
m-allanson Mar 15, 2018
293f36a
More descriptive variable name
m-allanson Mar 16, 2018
7e852cf
Fix queries being overwritten
m-allanson Mar 16, 2018
2adaffe
Remove eslint-disable flag
m-allanson Mar 16, 2018
c03cc34
Remove junk
m-allanson Mar 16, 2018
ca50615
test require error fix for windows
pieh Mar 15, 2018
460ca7d
dont require json data in sync-require
pieh Mar 15, 2018
f4c92e5
dont add layout data to json array multiple times
pieh Mar 16, 2018
e6239ee
initial async loading
pieh Mar 16, 2018
4e064d7
revert saving json directly to public for now
pieh Mar 16, 2018
1801fa7
updated production-app to sync with prop name change in ComponentRend…
pieh Mar 16, 2018
53bc172
we load json data via json-loader component in develop and not handli…
pieh Mar 16, 2018
adc3ce1
hashes for json files
pieh Mar 17, 2018
9008479
fix preloading, use xhr instead of fetch - for some reason can't forc…
pieh Mar 20, 2018
3050620
dont use full paths in dataPath - remove static/d/ path and .json ext…
pieh Mar 21, 2018
46e5235
Merge pull request #4635 from pieh/json-loader
m-allanson Mar 21, 2018
a3b6aee
Enable cached query results to be loaded
m-allanson Mar 22, 2018
66e462c
Don't dump all query results out to the client
m-allanson Mar 26, 2018
8196649
Merge pull request #4658 from m-allanson/load-develop-query-results
m-allanson Mar 26, 2018
7602a73
Merge remote-tracking branch 'upstream/v2' into json-loader-w4
pieh Mar 26, 2018
f0b3b0c
fix preload link to json data
pieh Mar 26, 2018
c268376
Merge remote-tracking branch 'upstream/v2' into json-loader
pieh Mar 28, 2018
2d9e286
remove not used function
pieh Mar 28, 2018
95fcd4a
remove more not used code
pieh Mar 28, 2018
20252dc
Update to latest webpack/mini-css-extract-plugin
KyleAMathews Mar 29, 2018
44797c3
don't write new (a)sync-requires.js if components didn't change (#4759)
pieh Mar 29, 2018
467a792
create just one websocket client (#4763)
pieh Mar 29, 2018
dd442c7
Filter out duplicate query jobs and create secondary queue for jobs i…
KyleAMathews Mar 29, 2018
abcb58c
[json-loader] Don't emit new file node until previous is finished pro…
KyleAMathews Apr 3, 2018
e374818
[json-loader] Only log file events if we're past bootstrap (#4826)
KyleAMathews Apr 3, 2018
62754de
[json-loader] dont recompile on data change - part 2 (#4837)
pieh Apr 5, 2018
4a09f74
[json-loader] develop - reading results from file improvments (#4850)
pieh Apr 5, 2018
868ef8b
Add query prioritization based on what page(s) user(s) are on
KyleAMathews Apr 5, 2018
b4fdecb
Add initial forward slash
KyleAMathews Apr 6, 2018
1bb7cc4
Actually this is how we add back the initial forward slash
KyleAMathews Apr 6, 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
Prev Previous commit
Next Next commit
Don't dump all query results out to the client
Instead only push results out if the data is for a path that's currently
being viewed in a client.
  • Loading branch information
m-allanson committed Mar 26, 2018
commit 66e462c50f2b95ea85206444977e2029c89ae88b
44 changes: 31 additions & 13 deletions packages/gatsby/cache-dir/json-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,61 @@ class JSONStore extends React.Component {
super(props)
this.state = {
data: {},
receivedData: false,
}
try {
this.socket = window.io()
} catch (err) {
console.error(`Could not connect to socket.io on dev server.`)
}
this.setJsonData = this.setJsonData.bind(this)
this.setPageData = this.setPageData.bind(this)
this.getPageData = this.getPageData.bind(this)
}

componentDidMount() {
this.socket.on(`queryResult`, this.setJsonData)
this.socket.on(`queryResult`, this.setPageData)
}

setJsonData(newData) {
shouldComponentUpdate(nextProps, nextState) {
if (nextProps !== this.props) return true

// if json for nextState is not available
const nextJsonId = get(nextProps.pageResources, `page.jsonName`)
if (!nextState.data[nextJsonId]) return false

// if nextState json is the same as current state json
const sameDataPath =
get(nextState, `data[${nextJsonId}].dataPath`) ===
get(this, `state.data[${nextJsonId}].dataPath`)

if (sameDataPath) return false

return true
}

setPageData(newData) {
this.setState({
data: { ...this.state.data, ...newData },
receivedData: true,
data: { [newData.path]: newData },
})
}

getPageData(path) {
const ob = this.state.data[path]
if (!ob) {
console.log(`Missing JSON: ${path}`)
return {}
}
return JSON.parse(ob)
const res = this.state.data[path]

// always check for fresh data
this.socket.emit(`getPageData`, path)

if (!res || !res.data) return false
return JSON.parse(res.data)
}

render() {
if (!this.state.receivedData) return ``
const { isPage, pages, pageResources } = this.props
const propsWithoutPages = omit(this.props, `pages`)

if (isPage) {
const jsonId = get(pageResources, `page.jsonName`)
const pageData = this.getPageData(jsonId)
if (pageData === false) return ``
return createElement(ComponentRenderer, {
key: `normal-page`,
...propsWithoutPages,
Expand Down
35 changes: 2 additions & 33 deletions packages/gatsby/src/commands/develop.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const chalk = require(`chalk`)
const address = require(`address`)
const sourceNodes = require(`../utils/source-nodes`)
const websocketManager = require(`../utils/websocket-manager`)
const path = require(`path`)

// const isInteractive = process.stdout.isTTY

Expand All @@ -44,26 +43,6 @@ rlInterface.on(`SIGINT`, () => {
process.exit()
})

// Read query results from cached json files
const getCachedQueryResults = ({ pages, jsonDataPaths, directory }) => {
let data = {}

pages.forEach(page => {
const dataPath = jsonDataPaths[page.jsonName]
if (typeof dataPath === `undefined`) return
const filePath = path.join(
directory,
`public`,
`static`,
`d`,
`${dataPath}.json`
)
data[page.jsonName] = fs.readFileSync(filePath, `utf-8`)
})

return data
}

async function startServer(program) {
const directory = program.directory
const directoryPath = withBasePath(directory)
Expand Down Expand Up @@ -218,18 +197,8 @@ async function startServer(program) {
* Set up the HTTP server and socket.io.
**/
const server = require(`http`).Server(app)
const state = store.getState()
const cachedQueryResults = getCachedQueryResults({
pages: state.pages,
jsonDataPaths: state.jsonDataPaths,
directory: program.directory,
})
websocketManager.pushResults(cachedQueryResults)
websocketManager.init(server)
const socket = websocketManager.instance()
socket.on(`connection`, s => {
s.join(`clients`)
})
websocketManager.init({ server, directory: program.directory })
const socket = websocketManager.getSocket()

const listener = server.listen(program.port, program.host, err => {
if (err) {
Expand Down
24 changes: 11 additions & 13 deletions packages/gatsby/src/internal-plugins/query-runner/query-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,23 @@ module.exports = async (pageOrLayout, component) => {

if (resultHashes[pageOrLayout.jsonName] !== resultHash) {
resultHashes[pageOrLayout.jsonName] = resultHash
const programType = program._[0]

if (programType === `develop`) {
const result = { [pageOrLayout.jsonName]: resultJSON }
const socket = websocketManager.instance()
if (socket) {
// push result straight to client
socket.emit(`queryResult`, result)
} else {
// queue results up until socket is available
websocketManager.pushResults(result)
}
}

// Always write file to public/static/d/ folder.
const dataPath = `${generatePathChunkName(
pageOrLayout.jsonName
)}-${resultHash}`

const programType = program._[0]

if (programType === `develop`) {
const data = {
dataPath,
data: resultJSON,
path: pageOrLayout.jsonName,
}
websocketManager.emitData({ data })
}

const resultPath = path.join(
program.directory,
`public`,
Expand Down
93 changes: 78 additions & 15 deletions packages/gatsby/src/utils/websocket-manager.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,80 @@
let websocket
let isInitialised = false
let results = {}

module.exports = {
init: server => {
websocket = require(`socket.io`)(server)
websocket.on(`connection`, s => {
s.emit(`queryResult`, results)
const path = require(`path`)
const { store } = require(`../redux`)
const fs = require(`fs`)

const getCachedPageData = (jsonName, directory) => {
const jsonDataPaths = store.getState().jsonDataPaths
const dataPath = jsonDataPaths[jsonName]
if (typeof dataPath === `undefined`) return undefined
const filePath = path.join(
directory,
`public`,
`static`,
`d`,
`${dataPath}.json`
)
const data = fs.readFileSync(filePath, `utf-8`)
return {
data,
dataPath,
path: jsonName,
}
}

class WebsocketManager {
constructor() {
this.isInitialised = false
this.results = {}
this.activePaths = new Map()
this.websocket
this.programDir

this.init = this.init.bind(this)
this.getSocket = this.getSocket.bind(this)
this.getPageData = this.getPageData.bind(this)
this.emitData = this.emitData.bind(this)
}

init({ server, directory }) {
this.programDir = directory
this.websocket = require(`socket.io`)(server)

this.websocket.on(`connection`, s => {
const clientId = s.id
s.join(`clients`)
s.on(`getPageData`, path => {
this.getPageData(path, clientId)
})
s.on(`disconnect`, s => {
this.activePaths.delete(clientId)
})
})
isInitialised = true
},
instance: () => isInitialised && websocket,
pushResults: result => {
Object.assign(results, result)
},

this.isInitialised = true
}

getSocket() {
return this.isInitialised && this.websocket
}

getPageData(path, clientId) {
// track the active path for each connected client
this.activePaths.set(clientId, path)
const data = getCachedPageData(path, this.programDir)
this.emitData({ data, forceEmit: true })
}

emitData({ data, forceEmit = false }) {
const isActivePath =
data.path && Array.from(this.activePaths.values()).includes(data.path)

// push results if this path is active on a client
if (isActivePath || forceEmit) {
this.websocket.emit(`queryResult`, data)
}
}
}

const manager = new WebsocketManager()

module.exports = manager