Skip to content

Commit d91cada

Browse files
committed
fix: increase container startup timeout and optimize zip creation
- Increase entrypoint timeout from 60s to 300s to allow for long migrations/seeds - Add process check in entrypoint to fail fast if backend dies - Offload ZIP creation to worker thread to prevent event loop blocking
1 parent 0720268 commit d91cada

File tree

5 files changed

+110
-24
lines changed

5 files changed

+110
-24
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@ yarn-error.log*
5151

5252
# Files
5353
CLAUDE.md
54+
.clinerules/byterover-rules.md
55+
.kilocode/rules/byterover-rules.md
56+
.roo/rules/byterover-rules.md
57+
.windsurf/rules/byterover-rules.md
58+
.cursor/rules/byterover-rules.mdc
59+
.kiro/steering/byterover-rules.md
60+
.qoder/rules/byterover-rules.md
61+
.augment/rules/byterover-rules.md

CLAUDE.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,25 @@ feature/
306306
- **Frontend Health Monitoring**:
307307
- Luego de iniciar el frontend esperar cierto tiempo para validar si aun se encuentra activo, si no revisa el motivo de la caida del front
308308

309-
Cada cambio que realice agregaro a CHANGELOG.md
309+
Cada cambio que realice agregaro a CHANGELOG.md
310+
311+
[byterover-mcp]
312+
313+
[byterover-mcp]
314+
315+
You are given two tools from Byterover MCP server, including
316+
## 1. `byterover-store-knowledge`
317+
You `MUST` always use this tool when:
318+
319+
+ Learning new patterns, APIs, or architectural decisions from the codebase
320+
+ Encountering error solutions or debugging techniques
321+
+ Finding reusable code patterns or utility functions
322+
+ Completing any significant task or plan implementation
323+
324+
## 2. `byterover-retrieve-knowledge`
325+
You `MUST` always use this tool when:
326+
327+
+ Starting any new task or implementation to gather relevant context
328+
+ Before making architectural decisions to understand existing patterns
329+
+ When debugging issues to check for previous solutions
330+
+ Working with unfamiliar parts of the codebase

backend/src/share/share.service.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import {
66
} from "@nestjs/common";
77
import { JwtService, JwtSignOptions } from "@nestjs/jwt";
88
import { Share, User } from "@prisma/client";
9-
import * as archiver from "archiver";
109
import * as argon from "argon2";
1110
import * as fs from "fs";
1211
import * as moment from "moment";
12+
import * as path from "path";
13+
import { Worker } from "worker_threads";
1314
import { ClamScanService } from "src/clamscan/clamscan.service";
1415
import { ConfigService } from "src/config/config.service";
1516
import { EmailService } from "src/email/email.service";
@@ -32,7 +33,7 @@ export class ShareService {
3233
private jwtService: JwtService,
3334
private reverseShareService: ReverseShareService,
3435
private clamScanService: ClamScanService,
35-
) {}
36+
) { }
3637

3738
async create(share: CreateShareDTO, user?: User, reverseShareToken?: string) {
3839
if (!(await this.isShareIdAvailable(share.id)).isAvailable)
@@ -62,7 +63,7 @@ export class ShareService {
6263
maxExpiration.value !== 0 &&
6364
(expiresNever ||
6465
parsedExpiration >
65-
moment().add(maxExpiration.value, maxExpiration.unit).toDate())
66+
moment().add(maxExpiration.value, maxExpiration.unit).toDate())
6667
) {
6768
throw new BadRequestException(
6869
"Expiration date exceeds maximum expiration date",
@@ -109,22 +110,36 @@ export class ShareService {
109110
async createZip(shareId: string) {
110111
if (this.config.get("s3.enabled")) return;
111112

112-
const path = `${SHARE_DIRECTORY}/${shareId}`;
113-
114113
const files = await this.prisma.file.findMany({ where: { shareId } });
115-
const archive = archiver("zip", {
116-
zlib: { level: this.config.get("share.zipCompressionLevel") },
117-
});
118-
const writeStream = fs.createWriteStream(`${path}/archive.zip`);
119114

120-
for (const file of files) {
121-
archive.append(fs.createReadStream(`${path}/${file.id}`), {
122-
name: file.name,
115+
return new Promise((resolve, reject) => {
116+
const isTs = __filename.endsWith(".ts");
117+
const workerFileName = isTs ? "zip.worker.ts" : "zip.worker.js";
118+
const workerPath = path.join(__dirname, workerFileName);
119+
120+
const worker = new Worker(workerPath, {
121+
workerData: {
122+
shareId,
123+
files,
124+
shareDirectory: SHARE_DIRECTORY,
125+
compressionLevel: this.config.get("share.zipCompressionLevel"),
126+
},
127+
execArgv: isTs ? ["-r", "ts-node/register"] : undefined,
123128
});
124-
}
125129

126-
archive.pipe(writeStream);
127-
await archive.finalize();
130+
worker.on("message", (message) => {
131+
if (typeof message === "object" && message.error) {
132+
reject(new Error(message.error));
133+
} else {
134+
resolve(message);
135+
}
136+
});
137+
worker.on("error", reject);
138+
worker.on("exit", (code) => {
139+
if (code !== 0)
140+
reject(new Error(`Worker stopped with exit code ${code}`));
141+
});
142+
});
128143
}
129144

130145
async complete(id: string, reverseShareToken?: string) {
@@ -165,7 +180,7 @@ export class ShareService {
165180

166181
const notifyReverseShareCreator = share.reverseShare
167182
? this.config.get("smtp.enabled") &&
168-
share.reverseShare.sendEmailNotification
183+
share.reverseShare.sendEmailNotification
169184
: undefined;
170185

171186
if (notifyReverseShareCreator) {
@@ -323,7 +338,7 @@ export class ShareService {
323338
// Handle security updates
324339
if (updateData.security) {
325340
hasSecurityUpdates = true;
326-
341+
327342
if (updateData.security.password) {
328343
updateSecurity.password = await argon.hash(updateData.security.password);
329344
}
@@ -341,11 +356,11 @@ export class ShareService {
341356
...(hasSecurityUpdates && {
342357
security: existingShare.security
343358
? {
344-
update: updateSecurity,
345-
}
359+
update: updateSecurity,
360+
}
346361
: {
347-
create: updateSecurity,
348-
},
362+
create: updateSecurity,
363+
},
349364
}),
350365
},
351366
});

backend/src/share/zip.worker.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { parentPort, workerData } from 'worker_threads';
2+
import * as archiver from 'archiver';
3+
import * as fs from 'fs';
4+
5+
async function createZip() {
6+
try {
7+
const { shareId, files, shareDirectory, compressionLevel } = workerData;
8+
const path = `${shareDirectory}/${shareId}`;
9+
10+
const archive = archiver('zip', {
11+
zlib: { level: compressionLevel },
12+
});
13+
const writeStream = fs.createWriteStream(`${path}/archive.zip`);
14+
15+
for (const file of files) {
16+
archive.append(fs.createReadStream(`${path}/${file.id}`), {
17+
name: file.name,
18+
});
19+
}
20+
21+
archive.pipe(writeStream);
22+
await archive.finalize();
23+
24+
if (parentPort) {
25+
parentPort.postMessage('done');
26+
}
27+
} catch (error) {
28+
if (parentPort) {
29+
// Send error message or object
30+
parentPort.postMessage({ error: error instanceof Error ? error.message : String(error) });
31+
}
32+
process.exit(1);
33+
}
34+
}
35+
36+
createZip();

scripts/docker/entrypoint.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ cd ..
1111

1212
# Wait for backend to be ready
1313
echo "Waiting for backend to be ready..."
14-
timeout=60
14+
timeout=300
1515
while [ $timeout -gt 0 ]; do
16+
# Check if backend process is still running
17+
if ! kill -0 $BACKEND_PID 2>/dev/null; then
18+
echo "ERROR: Backend process died while waiting for it to be ready"
19+
exit 1
20+
fi
21+
1622
if curl -sf http://localhost:8080/api/health > /dev/null 2>&1; then
1723
echo "Backend is ready!"
1824
break
@@ -23,7 +29,7 @@ while [ $timeout -gt 0 ]; do
2329
done
2430

2531
if [ $timeout -le 0 ]; then
26-
echo "Backend failed to start within 60 seconds"
32+
echo "Backend failed to start within 300 seconds"
2733
exit 1
2834
fi
2935

0 commit comments

Comments
 (0)