Node PG驱动程序停止监听数据库事件

时间:2018-08-13 01:08:52

标签: node.js postgresql node-postgres

我有一个NodeJS服务,该服务使用PostGreSQL的事件通知功能来侦听数据库中的某些操作,然后执行Node函数作为响应。如何设置它已在node-postgres软件包的文档中进行了说明,乍一看似乎功能还不错。

问题在于,运行Node服务(正在侦听我的数据库)一段时间后,它会停止侦听数据库。 “一会儿”是指它足够长,以至于每次发生时我都没有确切注意到我上次使用该服务的时间,但这至少需要一两个小时。可能会更长一些……在2-4小时内?

我可以确定,就Postgres而言,过了一会儿,Node服务不再连接到数据库。但是Node服务似乎从未注意到它没有连接……它没有显示错误,也没有触发通常由断开连接(client.on('end',function(){...}))触发的事件。我无法确定这是由于我在Node中的Pool配置导致的超时,还是数据库正在执行断开连接。但是,我认为因为要实现这一点,在节点代码的设置中存在更多的复杂性,因此在节点侧更有可能。 PostGres端实际上是一些触发函数,它们与node-postgres文档中的基本相同。

当Node服务表现出这种“我不在监听”行为时,它不会显示任何错误甚至消息……而调试Node代码则表明它没有事件跳入处理程序,例如, notificationProcessor()(如下所示)。因此,我对于在哪里进行调查一无所知。调查的一个挑战是要复制行为,我必须将其设置为运行,然后将其放置一段时间。

这是相关的Node代码。我在NodeJS v8.7.0和v9.0.0中看到了这个问题。我正在使用node-postgres v7.4.3。我看到在Mac和Linux上运行Node的问题。 PostGres 9.4.15在运行Node的单独Linux服务器上运行。

// The PostGres driver for Node needs a setting for the Pool ... not sure why.
pg.defaults.poolSize = 1;
// Load the connection details (user, password, etc.)
var pg_connect = "postgres://" + config.pg_user + ":" + config.pg_pass + "@" + config.pg_server + "/" + config.pg_db;

// Setup the connection pool and ... connect to the database.
var pool = new pg.Pool(
    {
        connectionString: pg_connect,
        max: 20,
        idleTimeoutMillis: 30000,
        connectionTimeoutMillis: 2000
    }
);
// Critically, pass callback to the connection ... so start
// running the "executionListener()" when it connects.
pool.connect(executionListener);

pool.on(
    'error', 
    function(err,client) {
        console.log("\n-{Error!}- " + err.message);
        console.log("    \\----> Attempting to re-start Listening!\n");
        pool.connect(executionListener);
    }
);

// What "channels" to monitor in the database for change events,
// and then what function to call when the event is found.
var monitoredChannels = {
    'executions': {
        cname: 'executions',
        chandler: function(msg) {
            console.log("\t[notification on executions] " + JSON.stringify(msg));
            newExecution(msg);
        },
        cquery: {}
    },
    'updates': {
        cname: 'updates',
        chandler: function(msg) {
            console.log("\t[notification on updates] " + JSON.stringify(msg));
            executionUpdate(msg);
        },
        cquery: {}
    }
};

function notificationProcessor(notif) {
    var tpayload = notif.payload.split(',');
    var resultLength = tpayload.length;
    var resultAttributeCount = (resultLength - 1) / 2;
    var payloadPointer = - 1;
    // What DB table is the notification referencing?
    var resultOut = {
        'table': tpayload[0]
    };
    for (var i = 0; i < resultAttributeCount; i++) {
        payloadPointer = payloadPointer + 2;
        resultOut[tpayload[payloadPointer]] = tpayload[payloadPointer + 1];
    }
    // Once the data package from the notification is parsed into
    // a useful form, we look at the array of channels we're watching,
    // and pass the data to the handler for the appropriate channel.
    return (monitoredChannels[notif.channel].chandler(resultOut))
}

// This is the main function used to monitor the database for events and
// react to them.
function executionListener(err, client, done) {
    // Handle errors in connection... if the connection
    // fails, try to reconnect.
    if(err) {
        console.log(err);
        if (done != null) done();
        pool.connect(executionListener);
    }
    console.log("[mPipeRunner] Listening to <" + config.pg_server + "/" + config.pg_db + "/" + config.schema + "> for executions");
    // What to do when a notice (error) is sent by database.
    client.on('notice', (msg) => console.warn('PGSQL Notice:', msg));
    // When PG disconnects for some reason, what to do?
    client.on('end',function(){console.log('client disconnected')});
    // When PG spits out a notification FROM A CHANNEL WE ARE LISTENING TO...
    client.on('notification', notificationProcessor);

    // Loop over the channels and initiate LISTENing to each one.
    for (var chnl in monitoredChannels) {
        var channel = monitoredChannels[chnl];
        channel.query = client.query("LISTEN " + channel.cname);
    }
};

在此先感谢您的见解!

0 个答案:

没有答案