Skip to content

Commit f533c55

Browse files
authored
Decommission old flying shuttles (#7299)
* Retire old flying shuttles * retire => decommissioned
1 parent 36abf81 commit f533c55

File tree

1 file changed

+70
-13
lines changed

1 file changed

+70
-13
lines changed

packages/next/build/flying-shuttle.ts

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import { recursiveDelete } from '../lib/recursive-delete'
1111
import * as Log from './output/log'
1212

1313
const FILE_BUILD_ID = 'HEAD_BUILD_ID'
14+
const FILE_UPDATED_AT = 'UPDATED_AT'
1415
const DIR_FILES_NAME = 'files'
16+
const MAX_SHUTTLES = 4
1517

1618
const mkdirp = promisify(mkdirpModule)
1719
const fsExists = promisify(fs.exists)
@@ -29,6 +31,63 @@ type ChunkGraphManifest = {
2931
hashes: { [page: string]: string }
3032
}
3133

34+
async function findCachedShuttles(apexShuttleDirectory: string) {
35+
return (await Promise.all(
36+
await fsReadDir(apexShuttleDirectory).then(shuttleFiles =>
37+
shuttleFiles.map(async f => ({
38+
file: f,
39+
stats: await fsLstat(path.join(apexShuttleDirectory, f)),
40+
}))
41+
)
42+
))
43+
.filter(({ stats }) => stats.isDirectory())
44+
.map(({ file }) => file)
45+
}
46+
47+
async function pruneShuttles(apexShuttleDirectory: string) {
48+
const allShuttles = await findCachedShuttles(apexShuttleDirectory)
49+
if (allShuttles.length <= MAX_SHUTTLES) {
50+
return
51+
}
52+
53+
const datedShuttles: { updatedAt: Date; shuttleDirectory: string }[] = []
54+
for (const shuttleId of allShuttles) {
55+
const shuttleDirectory = path.join(apexShuttleDirectory, shuttleId)
56+
const updatedAtPath = path.join(shuttleDirectory, FILE_UPDATED_AT)
57+
58+
let updatedAt: Date
59+
try {
60+
updatedAt = new Date((await fsReadFile(updatedAtPath, 'utf8')).trim())
61+
} catch (err) {
62+
if (err.code === 'ENOENT') {
63+
await recursiveDelete(shuttleDirectory)
64+
continue
65+
}
66+
throw err
67+
}
68+
69+
datedShuttles.push({ updatedAt, shuttleDirectory })
70+
}
71+
72+
const sortedShuttles = datedShuttles.sort((a, b) =>
73+
Math.sign(b.updatedAt.valueOf() - a.updatedAt.valueOf())
74+
)
75+
let prunedShuttles = 0
76+
while (sortedShuttles.length > MAX_SHUTTLES) {
77+
const shuttleDirectory = sortedShuttles.pop()
78+
await recursiveDelete(shuttleDirectory!.shuttleDirectory)
79+
++prunedShuttles
80+
}
81+
82+
if (prunedShuttles) {
83+
Log.info(
84+
`decommissioned ${prunedShuttles} old shuttle${
85+
prunedShuttles > 1 ? 's' : ''
86+
}`
87+
)
88+
}
89+
}
90+
3291
function isShuttleValid({
3392
manifestPath,
3493
pagesDirectory,
@@ -111,20 +170,8 @@ export class FlyingShuttle {
111170
return path.join(this.apexShuttleDirectory, this.flyingShuttleId)
112171
}
113172

114-
private getShuttleIds = async () =>
115-
(await Promise.all(
116-
await fsReadDir(this.apexShuttleDirectory).then(shuttleFiles =>
117-
shuttleFiles.map(async f => ({
118-
file: f,
119-
stats: await fsLstat(path.join(this.apexShuttleDirectory, f)),
120-
}))
121-
)
122-
))
123-
.filter(({ stats }) => stats.isDirectory())
124-
.map(({ file }) => file)
125-
126173
private findShuttleId = async () => {
127-
const shuttles = await this.getShuttleIds()
174+
const shuttles = await findCachedShuttles(this.apexShuttleDirectory)
128175
return shuttles.find(shuttleId => {
129176
try {
130177
const manifestPath = path.join(
@@ -364,6 +411,10 @@ export class FlyingShuttle {
364411
path.join(this.shuttleDirectory, FILE_BUILD_ID),
365412
this.buildId
366413
)
414+
await fsWriteFile(
415+
path.join(this.shuttleDirectory, FILE_UPDATED_AT),
416+
new Date().toISOString()
417+
)
367418

368419
const usedChunks = new Set()
369420
const pages = Object.keys(storeManifest.pageChunks)
@@ -393,5 +444,11 @@ export class FlyingShuttle {
393444

394445
Log.info(`flying shuttle payload: ${usedChunks.size + 2} files`)
395446
Log.ready('flying shuttle docked')
447+
448+
try {
449+
await pruneShuttles(this.apexShuttleDirectory)
450+
} catch (e) {
451+
Log.error('failed to prune old shuttles: ' + e)
452+
}
396453
}
397454
}

0 commit comments

Comments
 (0)