Hi Leute,
ich bastele ja nun schon einige Zeit mit Docker und node.js. Als Basis
benutze ich dieses Tutorial und alles funktioniert inzwischen prima. Ich
habe auch die Datenbank so eingebunden wie hier beschrieben.
https://bezkoder.com/node-js-rest-api-express-mysql/
Gestern Abend habe ich dann gesehen, dass es plötzlich (ohne dass ich
auf die Datenbank zugegriffen hätte) diesen Fehler gab (siehe unten).
Aber da der ja nicht von meinem code oder meinen Aktivitäten ausging -
wie kann ich den abfangen / behandeln? c_db ist der Datenbank-Container
und c_node der für node
Try-catch kenne ich und habe es auch schon benutzt. Das Ding ist hier:
Ich wusste nicht, wo ich es hätte hinschreiben sollen - um die gesamte
server.js? Ich hatte inzwischen erstmal so probiert und den Fehler
gefangen (Deine Vorgabe ist spezifischer)
1
process.on('uncaughtException', function(err) {
2
console.error(err.stack);
3
console.log('catched - NODE can run on.');
4
});
So wie ich das sehe, wird das connection-"Objekt?" in der db.js erzeugt
und dann nur in der "customer.model.js" benutzt, also die server.js weiß
davon gar nichts?
Jedenfalls - was ich erreichen will: Derzeit (ohne die o.g. Änderung)
stürzt das Programm ab, sobald die Datenbank nur kurz nicht zur
Verfügung steht. Ich würde in dem Fall gerne nach einer Minute
versuchen, neu zu verbinden.
Für mein Verständnis müsste ich dann in der o.g. Funktion ein callback
in einer Minute vereinbaren, und da neu verbinden. Aber wenn der
Programmteil in der server.js steht, dann weiß die ja gar nichts von der
Objektinstanz innerhalb des Moduls..?
PS: Ich bin ja auf dem langen Weg vom Abschreiben zum Verstehen und Du
hast mit schon sehr geholfen, danke!
https://www.npmjs.com/package/mysql#error-handling> Last but not least: If a fatal errors occurs and there are no pending> callbacks, or a normal error occurs which has no callback belonging to it,> the error is emitted as an 'error' event on the connection object. This is> demonstrated in the example below:
1
#!/usr/bin/env node
2
connection.on('error',function(err){
3
console.log(err.code);// 'ER_BAD_DB_ERROR'
4
});
Oft ist es aber von Vorteil, einen Connection Pool zu haben. Da hat man
dann immer ein paar Verbindungen auf Vorrat. Man holt sich daraus dann
immer erst eine Verbindung, wenn man gerade eine braucht. Wenn dann da
zwischendrin mal eine Abbricht, ist das oft nicht so schlimm.
https://www.npmjs.com/package/mysql#pooling-connections oder
https://www.npmjs.com/package/generic-pool
Man sollte dann aber auch noch mit Transactions arbeiten, damit man bei
Komplexeren Sachen nichts halbfertiges hat. Fast alles in Transactions
wird bei Fehlern und Verbindungsabbrüchen rückgängig gemacht / bzw. erst
beim Commit übernommen. Wobei, bei sowas simplem wie in deinem
Beispiellink (nur ein Query pro Funktion / Rest call) kann man sich das
auch sparen. Das Beispiel ist aber auch sehr schlecht geeignet, da
nachträglich noch Transaktionen nachzurüsten, weil man da die DB
Verbindung mit der Transaktion nicht mitgeben kann.
Das holen von Verbindungen, das Verwalten von Transactions, usw. packe
ich mir gerne in Hilfsfunktionen, damit ich aquire, release, commit,
rollback, etc. nicht vergesse. Ich mache dann alle DB Funktionen gerne
async. z.B.
1
// (alles ungetestet)
2
3
constpf=(t,f)=>promisify((...x)=>t[f](...x));// Like promisify, but preserve this
connection.destroy();// Something went very wrong. Terminate connection, that should roll back any pending transactions, and makes sure nothing else is done on this connection
71
}finally{
72
thrownewError("Unexpected savepoint. Make sure not to call these functions in paralel, and don't forget to use await everywhere!");
73
}
74
}
Und dann:
1
asyncfunctiondo_something(c,bla){
2
returnconnection_helper(c,asyncc=>{
3
letresult=awaitpf(db,'query')("SELECT bla FROM bla WHERE x=?",[bla]);
zu "connection pool" hatte ich etwas gelesen, das hilft mir aber nicht,
wenn die Datenbank ein paar Minuten "weg" ist (z.B. Server wird neu
gestartet) und dann wiederkommt.
Man muss sich halt die Verbindung erst dann holen, wenn man sie auch
braucht, und danach wieder freigeben oder schliessen. Wenn die DB weg
ist, man was machen will, und es deshalb Fehler gibt, muss man es dann
halt später nochmal versuchen, wenn die DB wieder da ist.
Ich habe jetzt mal ein Mini-Testprogramm geschrieben, db.js ist gebause
wie im Tutorial oben:
[code]
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded());
// catch database connection errors. ToDo: do more specific on
ECONNRESET
process.on('uncaughtException', function(err) {
console.error(err.stack);
console.log('catched - NODE can run on.');
})
// db connection
var sql = require("./models/db.js");
app.get("/", (req, res) => {
sql.query("SELECT * FROM customers", function(err, result) {
if (err) {res.json({err})} else {res.json({ result })};
});
});
// set port, listen for requests
app.listen(3133, () => {
console.log("Server is running on port 3133.");
});
[code]
Das funktioniert, und wenn ich die Datenbank abschalte, gibt es sofort
einen Fehler, den ich ja abfange, also der "server" läuft weiter. Aber
was muss ich wie mit der Connection tun, damit es wieder funktioniert,
sobald die DB wieder da ist?
Momentan erhalte ich da den Fehler: PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR
habe schon versucht vor sql.query zu schreiben
sql.destroy();
sql.connect();
aber das hat nicht funktioniert..
Was ich inzwischen noch gelesen habe: Man muss nach einem solchen Fehler
ein neues Connection-Objekt benutzen - aber wie mache ich das (wie
kriege ich es ganz tot), bzw. wie stelle ich sicher, dass er nicht immer
wieder durch die Modul-Verwendung das alte benutzt?
Aua schrieb:> Was ich inzwischen noch gelesen habe: Man muss nach einem solchen Fehler> ein neues Connection-Objekt benutzen
das "Wie" stand weiter oben:
🐧 DPA 🐧 schrieb:> Oft ist es aber von Vorteil, einen Connection Pool zu haben.
da kümmert sich dann das "Pool-Verwaltungs-Objekt" darum, dass du immer
eine funktionierende Verbindung bekommst.
https://www.npmjs.com/package/mysql#pooling-connections
Indem du bei db.js keine bereits geöffnete Verbindung exportierst,
sondern stattdessen Funktionen, mit denen du dir eine Verbindung holst,
und/oder sie wieder frei gibst.
Eine ganz Primitive Methode (mit ES5 old style callbacks stat
async/await):
1
#!node
2
functionaquireDBConnection(callback){
3
constconnection=mysql.createConnection({
4
host:dbConfig.HOST,
5
user:dbConfig.USER,
6
password:dbConfig.PASSWORD,
7
database:dbConfig.DB
8
});
9
10
// open the MySQL connection
11
connection.connect(error=>{
12
if (error)returncallback(error);
13
console.log("Successfully connected to the database.");