Skip to content

Commit 68d06ea

Browse files
fix: patch connection.destroy (mysql) (#80)
* patch connection destroy * add esm tests * add comment
1 parent 41b10ed commit 68d06ea

File tree

5 files changed

+174
-57
lines changed

5 files changed

+174
-57
lines changed

src/instrumentation/libraries/mysql/Instrumentation.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ export class MysqlInstrumentation extends TdInstrumentationBase {
189189
}
190190
}
191191

192+
// Wrap Connection.prototype.destroy
193+
if (ConnectionClass.prototype && ConnectionClass.prototype.destroy) {
194+
if (!isWrapped(ConnectionClass.prototype.destroy)) {
195+
this._wrap(ConnectionClass.prototype, "destroy", this._getDestroyPatchFn());
196+
logger.debug(`[MysqlInstrumentation] Wrapped Connection.prototype.destroy`);
197+
}
198+
}
199+
192200
this.markModuleAsPatched(ConnectionClass);
193201
logger.debug(`[MysqlInstrumentation] Connection class patching complete`);
194202

@@ -803,6 +811,25 @@ export class MysqlInstrumentation extends TdInstrumentationBase {
803811
};
804812
}
805813

814+
/**
815+
* Get wrapper function for destroy method
816+
*/
817+
private _getDestroyPatchFn() {
818+
const self = this;
819+
return (originalDestroy: Function) => {
820+
return function destroy(this: any) {
821+
if (self.mode === TuskDriftMode.REPLAY) {
822+
// No-op in replay mode - prevent actual TCP socket destruction
823+
return undefined;
824+
} else if (self.mode === TuskDriftMode.RECORD) {
825+
return originalDestroy.apply(this, arguments);
826+
} else {
827+
return originalDestroy.apply(this, arguments);
828+
}
829+
};
830+
};
831+
}
832+
806833
/**
807834
* Get wrapper function for Pool.end method
808835
*/

src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/run.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ echo " Stream Tests:"
133133
echo " - GET /stream/query-stream-method"
134134
docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/stream/query-stream-method > /dev/null
135135

136+
echo " - GET /test/connection-destroy"
137+
docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-destroy > /dev/null
138+
136139
echo "All endpoints hit successfully."
137140

138141
# Step 5: Wait before stopping server

src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/index.ts

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -647,17 +647,17 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => {
647647

648648
// Create a temporary connection for this test
649649
const tempConnection = mysql.createConnection({
650-
host: process.env.MYSQL_HOST || 'mysql',
651-
port: parseInt(process.env.MYSQL_PORT || '3306'),
652-
user: process.env.MYSQL_USER || 'testuser',
653-
password: process.env.MYSQL_PASSWORD || 'testpass',
654-
database: process.env.MYSQL_DB || 'testdb',
650+
host: process.env.MYSQL_HOST || "mysql",
651+
port: parseInt(process.env.MYSQL_PORT || "3306"),
652+
user: process.env.MYSQL_USER || "testuser",
653+
password: process.env.MYSQL_PASSWORD || "testpass",
654+
database: process.env.MYSQL_DB || "testdb",
655655
});
656656

657657
let endEventReceived = false;
658658

659659
// Listen for end event
660-
tempConnection.on('end', () => {
660+
tempConnection.on("end", () => {
661661
console.log("'end' event received");
662662
endEventReceived = true;
663663
});
@@ -701,11 +701,11 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => {
701701

702702
// Create a temporary connection for this test
703703
const tempConnection = mysql.createConnection({
704-
host: process.env.MYSQL_HOST || 'mysql',
705-
port: parseInt(process.env.MYSQL_PORT || '3306'),
706-
user: process.env.MYSQL_USER || 'testuser',
707-
password: process.env.MYSQL_PASSWORD || 'testpass',
708-
database: process.env.MYSQL_DB || 'testdb',
704+
host: process.env.MYSQL_HOST || "mysql",
705+
port: parseInt(process.env.MYSQL_PORT || "3306"),
706+
user: process.env.MYSQL_USER || "testuser",
707+
password: process.env.MYSQL_PASSWORD || "testpass",
708+
database: process.env.MYSQL_DB || "testdb",
709709
});
710710

711711
tempConnection.connect((connectError) => {
@@ -716,9 +716,9 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => {
716716

717717
// Change user to the same credentials (simpler than creating a test user)
718718
const changeUserOptions = {
719-
user: process.env.MYSQL_USER || 'testuser',
720-
password: process.env.MYSQL_PASSWORD || 'testpass',
721-
database: process.env.MYSQL_DB || 'testdb',
719+
user: process.env.MYSQL_USER || "testuser",
720+
password: process.env.MYSQL_PASSWORD || "testpass",
721+
database: process.env.MYSQL_DB || "testdb",
722722
};
723723

724724
tempConnection.changeUser(changeUserOptions, (changeError) => {
@@ -810,11 +810,11 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => {
810810

811811
// Create a temporary pool for this test
812812
const tempPool = mysql.createPool({
813-
host: process.env.MYSQL_HOST || 'mysql',
814-
port: parseInt(process.env.MYSQL_PORT || '3306'),
815-
user: process.env.MYSQL_USER || 'testuser',
816-
password: process.env.MYSQL_PASSWORD || 'testpass',
817-
database: process.env.MYSQL_DB || 'testdb',
813+
host: process.env.MYSQL_HOST || "mysql",
814+
port: parseInt(process.env.MYSQL_PORT || "3306"),
815+
user: process.env.MYSQL_USER || "testuser",
816+
password: process.env.MYSQL_PASSWORD || "testpass",
817+
database: process.env.MYSQL_DB || "testdb",
818818
connectionLimit: 5,
819819
});
820820

@@ -858,15 +858,15 @@ app.get("/events/connect", async (req: Request, res: Response) => {
858858

859859
// Create a new connection to test connect event
860860
const testConnection = mysql.createConnection({
861-
host: process.env.MYSQL_HOST || 'mysql',
862-
port: parseInt(process.env.MYSQL_PORT || '3306'),
863-
user: process.env.MYSQL_USER || 'testuser',
864-
password: process.env.MYSQL_PASSWORD || 'testpass',
865-
database: process.env.MYSQL_DB || 'testdb',
861+
host: process.env.MYSQL_HOST || "mysql",
862+
port: parseInt(process.env.MYSQL_PORT || "3306"),
863+
user: process.env.MYSQL_USER || "testuser",
864+
password: process.env.MYSQL_PASSWORD || "testpass",
865+
database: process.env.MYSQL_DB || "testdb",
866866
});
867867

868868
// Listen for connect event
869-
testConnection.on('connect', () => {
869+
testConnection.on("connect", () => {
870870
console.log("'connect' event received");
871871
connectEventReceived = true;
872872
});
@@ -894,6 +894,47 @@ app.get("/events/connect", async (req: Request, res: Response) => {
894894
}
895895
});
896896

897+
// Test: Connection.destroy() - not patched
898+
app.get("/test/connection-destroy", async (req: Request, res: Response) => {
899+
try {
900+
console.log("Testing connection.destroy()...");
901+
902+
// Create a temporary connection for this test
903+
const tempConnection = mysql.createConnection({
904+
host: process.env.MYSQL_HOST || "mysql",
905+
port: parseInt(process.env.MYSQL_PORT || "3306"),
906+
user: process.env.MYSQL_USER || "testuser",
907+
password: process.env.MYSQL_PASSWORD || "testpass",
908+
database: process.env.MYSQL_DB || "testdb",
909+
});
910+
911+
tempConnection.connect((connectError) => {
912+
if (connectError) {
913+
return res.status(500).json({ error: connectError.message });
914+
}
915+
916+
// Query before destroy
917+
tempConnection.query("SELECT 1 as test", (queryError1, results1) => {
918+
if (queryError1) {
919+
return res.status(500).json({ error: queryError1.message });
920+
}
921+
922+
tempConnection.destroy();
923+
924+
setTimeout(() => {
925+
res.json({
926+
message: "Connection destroyed successfully",
927+
resultBeforeDestroy: results1,
928+
});
929+
}, 100);
930+
});
931+
});
932+
} catch (error: any) {
933+
console.error("Unexpected error in connection-destroy:", error);
934+
res.status(500).json({ error: error.message });
935+
}
936+
});
937+
897938
// ===== STREAM TESTS =====
898939

899940
// Test Query.prototype.stream() method
@@ -909,15 +950,15 @@ app.get("/stream/query-stream-method", async (req: Request, res: Response) => {
909950
const stream = query.stream();
910951

911952
stream
912-
.on('error', (err) => {
953+
.on("error", (err) => {
913954
console.error("Stream error:", err);
914955
res.status(500).json({ error: err.message });
915956
})
916-
.on('data', (row) => {
957+
.on("data", (row) => {
917958
console.log("Received data from stream:", row);
918959
results.push(row);
919960
})
920-
.on('end', () => {
961+
.on("end", () => {
921962
console.log("Stream ended");
922963
res.json({
923964
message: "Stream query executed",
@@ -979,6 +1020,7 @@ const server = app.listen(PORT, async () => {
9791020
console.log("");
9801021
console.log(" Event Tests:");
9811022
console.log(" GET /events/connect - Connect event emission");
1023+
console.log(" GET /test/connection-destroy - Connection destroy");
9821024
console.log("");
9831025
console.log(" Stream Tests:");
9841026
console.log(" GET /stream/query-stream-method - Query.stream() method");

src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/run.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ echo " Stream Tests:"
133133
echo " - GET /stream/query-stream-method"
134134
docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/stream/query-stream-method > /dev/null
135135

136+
echo " - GET /test/connection-destroy"
137+
docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-destroy > /dev/null
138+
136139
echo "All endpoints hit successfully."
137140

138141
# Step 5: Wait before stopping server
@@ -161,7 +164,7 @@ docker compose -p $PROJECT_NAME down
161164

162165
# Step 9: Clean up traces and logs
163166
echo "Step 9: Cleaning up traces and logs..."
164-
# cleanup_tusk_files
167+
cleanup_tusk_files
165168

166169
echo "MySQL ESM E2E test run complete."
167170

0 commit comments

Comments
 (0)