Skip to content

Commit d153729

Browse files
authoredSep 15, 2020
fix(gatsby): resend pageData when socket disconnect (#26868)
1 parent 23d8966 commit d153729

File tree

3 files changed

+97
-86
lines changed

3 files changed

+97
-86
lines changed
 

‎packages/gatsby/cache-dir/socketIo.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1+
import io from "socket.io-client"
12
import { reportError, clearError } from "./error-overlay-handler"
23
import normalizePagePath from "./normalize-page-path"
34

45
let socket = null
56

7+
const inFlightGetPageDataPromiseCache = {}
68
let staticQueryData = {}
79
let pageQueryData = {}
8-
let isInitialized = false
910

1011
export const getStaticQueryData = () => staticQueryData
1112
export const getPageQueryData = () => pageQueryData
12-
export const getIsInitialized = () => isInitialized
1313

1414
export default function socketIo() {
1515
if (process.env.NODE_ENV !== `production`) {
1616
if (!socket) {
1717
// Try to initialize web socket if we didn't do it already
1818
try {
19-
// eslint-disable-next-line no-undef
20-
socket = io()
19+
// force websocket as transport
20+
socket = io({
21+
transports: [`websocket`],
22+
})
23+
24+
// when websocket fails, we'll try polling
25+
socket.on(`reconnect_attempt`, () => {
26+
socket.io.opts.transports = [`polling`, `websocket`]
27+
})
2128

2229
const didDataChange = (msg, queryData) => {
2330
const id =
@@ -30,6 +37,14 @@ export default function socketIo() {
3037
)
3138
}
3239

40+
socket.on(`connect`, () => {
41+
// we might have disconnected so we loop over the page-data requests in flight
42+
// so we can get the data again
43+
Object.keys(inFlightGetPageDataPromiseCache).forEach(pathname => {
44+
socket.emit(`getDataForPath`, pathname)
45+
})
46+
})
47+
3348
socket.on(`message`, msg => {
3449
if (msg.type === `staticQueryResult`) {
3550
if (didDataChange(msg, staticQueryData)) {
@@ -74,7 +89,6 @@ export default function socketIo() {
7489
}
7590
}
7691

77-
const inFlightGetPageDataPromiseCache = {}
7892
function getPageData(pathname) {
7993
pathname = normalizePagePath(pathname)
8094
if (inFlightGetPageDataPromiseCache[pathname]) {

‎packages/gatsby/src/utils/webpack.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,12 @@ module.exports = async (
690690
]
691691
}
692692

693+
if (stage === `develop`) {
694+
config.externals = {
695+
"socket.io-client": `io`,
696+
}
697+
}
698+
693699
store.dispatch(actions.replaceWebpackConfig(config))
694700
const getConfig = () => store.getState().webpack
695701

‎packages/gatsby/src/utils/websocket-manager.ts

+72-81
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from "path"
33
import { store } from "../redux"
44
import { Server as HTTPSServer } from "https"
55
import { Server as HTTPServer } from "http"
6-
import fs from "fs"
6+
import fs from "fs-extra"
77
import { readPageData, IPageDataWithQueryResult } from "../utils/page-data"
88
import telemetry from "gatsby-telemetry"
99
import url from "url"
@@ -25,87 +25,72 @@ type PageResultsMap = Map<string, IPageQueryResult>
2525
type QueryResultsMap = Map<string, IStaticQueryResult>
2626

2727
/**
28-
* Get cached page query result for given page path.
28+
* Get page query result for given page path.
2929
* @param {string} pagePath Path to a page.
3030
*/
31-
const getCachedPageData = async (
32-
pagePath: string
33-
): Promise<IPageQueryResult> => {
31+
async function getPageData(pagePath: string): Promise<IPageQueryResult> {
3432
const { program, pages } = store.getState()
3533
const publicDir = path.join(program.directory, `public`)
34+
35+
const result: IPageQueryResult = {
36+
id: pagePath,
37+
result: undefined,
38+
}
3639
if (pages.has(denormalizePagePath(pagePath)) || pages.has(pagePath)) {
3740
try {
3841
const pageData: IPageDataWithQueryResult = await readPageData(
3942
publicDir,
4043
pagePath
4144
)
4245

43-
return {
44-
result: pageData,
45-
id: pagePath,
46-
}
46+
result.result = pageData
4747
} catch (err) {
4848
throw new Error(
4949
`Error loading a result for the page query in "${pagePath}". Query was not run and no cached result was found.`
5050
)
5151
}
5252
}
5353

54-
return {
55-
id: pagePath,
56-
result: undefined,
57-
}
54+
return result
5855
}
5956

60-
const hashPaths = (
61-
paths?: Array<string>
62-
): undefined | Array<string | undefined> => {
63-
if (!paths) {
64-
return undefined
57+
/**
58+
* Get page query result for given page path.
59+
* @param {string} pagePath Path to a page.
60+
*/
61+
async function getStaticQueryData(
62+
staticQueryId: string
63+
): Promise<IStaticQueryResult> {
64+
const { program } = store.getState()
65+
const publicDir = path.join(program.directory, `public`)
66+
67+
const filePath = path.join(
68+
publicDir,
69+
`page-data`,
70+
`sq`,
71+
`d`,
72+
`${staticQueryId}.json`
73+
)
74+
75+
const result: IStaticQueryResult = {
76+
id: staticQueryId,
77+
result: undefined,
6578
}
66-
return paths.map(path => {
67-
if (!path) {
68-
return undefined
79+
if (await fs.pathExists(filePath)) {
80+
try {
81+
const fileResult = await fs.readJson(filePath)
82+
83+
result.result = fileResult
84+
} catch (err) {
85+
// ignore errors
6986
}
70-
return createHash(`sha256`).update(path).digest(`hex`)
71-
})
87+
}
88+
89+
return result
7290
}
7391

74-
/**
75-
* Get cached StaticQuery results for components that Gatsby didn't run query yet.
76-
* @param {QueryResultsMap} resultsMap Already stored results for queries that don't need to be read from files.
77-
* @param {string} directory Root directory of current project.
78-
*/
79-
const getCachedStaticQueryResults = (
80-
resultsMap: QueryResultsMap,
81-
directory: string
82-
): QueryResultsMap => {
83-
const cachedStaticQueryResults: QueryResultsMap = new Map()
84-
const { staticQueryComponents } = store.getState()
85-
staticQueryComponents.forEach(staticQueryComponent => {
86-
// Don't read from file if results were already passed from query runner
87-
if (resultsMap.has(staticQueryComponent.hash)) return
88-
const filePath = path.join(
89-
directory,
90-
`public`,
91-
`page-data`,
92-
`sq`,
93-
`d`,
94-
`${staticQueryComponent.hash}.json`
95-
)
96-
const fileResult = fs.readFileSync(filePath, `utf-8`)
97-
if (fileResult === `undefined`) {
98-
console.log(
99-
`Error loading a result for the StaticQuery in "${staticQueryComponent.componentPath}". Query was not run and no cached result was found.`
100-
)
101-
return
102-
}
103-
cachedStaticQueryResults.set(staticQueryComponent.hash, {
104-
result: JSON.parse(fileResult),
105-
id: staticQueryComponent.hash,
106-
})
107-
})
108-
return cachedStaticQueryResults
92+
function hashPaths(paths: Array<string>): Array<string> {
93+
return paths.map(path => createHash(`sha256`).update(path).digest(`hex`))
10994
}
11095

11196
const getRoomNameFromPath = (path: string): string => `path-${path}`
@@ -119,22 +104,17 @@ export class WebsocketManager {
119104
websocket: socketIO.Server | undefined
120105

121106
init = ({
122-
directory,
123107
server,
124108
}: {
125109
directory: string
126110
server: HTTPSServer | HTTPServer
127111
}): socketIO.Server => {
128-
const cachedStaticQueryResults = getCachedStaticQueryResults(
129-
this.staticQueryResults,
130-
directory
131-
)
132-
this.staticQueryResults = new Map([
133-
...this.staticQueryResults,
134-
...cachedStaticQueryResults,
135-
])
136-
137-
this.websocket = socketIO(server)
112+
this.websocket = socketIO(server, {
113+
// we see ping-pong timeouts on gatsby-cloud when socket.io is running for a while
114+
// increasing it should help
115+
// @see https://github.com/socketio/socket.io/issues/3259#issuecomment-448058937
116+
pingTimeout: 30000,
117+
})
138118

139119
this.websocket.on(`connection`, socket => {
140120
let activePath: string | null = null
@@ -147,13 +127,6 @@ export class WebsocketManager {
147127
}
148128

149129
this.connectedClients += 1
150-
// Send already existing static query results
151-
this.staticQueryResults.forEach(result => {
152-
socket.send({
153-
type: `staticQueryResult`,
154-
payload: result,
155-
})
156-
})
157130
this.errors.forEach((message, errorID) => {
158131
socket.send({
159132
type: `overlayError`,
@@ -176,22 +149,39 @@ export class WebsocketManager {
176149
}
177150

178151
const getDataForPath = async (path: string): Promise<void> => {
179-
if (!this.pageResults.has(path)) {
152+
let pageData = this.pageResults.get(path)
153+
if (!pageData) {
180154
try {
181-
const result = await getCachedPageData(path)
155+
pageData = await getPageData(path)
182156

183-
this.pageResults.set(path, result)
157+
this.pageResults.set(path, pageData)
184158
} catch (err) {
185159
console.log(err.message)
186-
187160
return
188161
}
189162
}
190163

164+
const staticQueryHashes = pageData.result?.staticQueryHashes ?? []
165+
await Promise.all(
166+
staticQueryHashes.map(async queryId => {
167+
let staticQueryResult = this.staticQueryResults.get(queryId)
168+
169+
if (!staticQueryResult) {
170+
staticQueryResult = await getStaticQueryData(queryId)
171+
this.staticQueryResults.set(queryId, staticQueryResult)
172+
}
173+
174+
socket.send({
175+
type: `staticQueryResult`,
176+
payload: staticQueryResult,
177+
})
178+
})
179+
)
180+
191181
socket.send({
192182
type: `pageQueryResult`,
193183
why: `getDataForPath`,
194-
payload: this.pageResults.get(path),
184+
payload: pageData,
195185
})
196186

197187
if (this.connectedClients > 0) {
@@ -236,6 +226,7 @@ export class WebsocketManager {
236226

237227
if (this.websocket) {
238228
this.websocket.send({ type: `staticQueryResult`, payload: data })
229+
239230
if (this.connectedClients > 0) {
240231
telemetry.trackCli(
241232
`WEBSOCKET_EMIT_STATIC_PAGE_DATA_UPDATE`,

0 commit comments

Comments
 (0)
Please sign in to comment.