othiym23 / node-continuation-local-storage

implementation of https://github.com/joyent/node/issues/5243
BSD 2-Clause "Simplified" License
1.13k stars 207 forks source link

namespace set values are getting lost in http socket close callback #145

Open queston02 opened 4 years ago

queston02 commented 4 years ago

We are creating the namespace and setting a variable called gtid in one of the middle ware and then trying to read that value for logging. We are getting the values in most of the places but some places it's undefined. One of the place is in the callback of the socket close event. Below is the code to provide some context.

var express = require('express');
var app = express();
var http = require('http');
var continuationLocalStorage = require('continuation-local-storage');
var namespace = continuationLocalStorage.createNamespace('namespace');

app.use(function (req, res, next) {
    namespace.run(function () {
        namespace.set('gtid', 'some unique value');
        return next();
    });
});

var req = protocol.request(request, function (response) {
    response.setEncoding('utf-8');
    var responseBody = '';

    response.on('data', function (data) {
        responseBody += data;
    });

    response.on('error', function (e) {
        e.code = response.statusCode;
        return callback(e);
    });

    response.on('end', function () {
        return callback(null, responseBody);
    });
});

req.on('socket', function (socket) {
    if (!socket.name) {
        socket.name = new Date().getTime();

        socket.on('close', function () {
            var namespace = getNamespace('namespace');
            console.log(namespace.get('gtid'))
        });
    }
});

req.on('error', function (e) {
    return callback(e);
});

if (request.body) {
    req.write(request.body);
}
req.end();
queston02 commented 4 years ago

There is one more place where variable set in namespace is coming as undefined. It's in the callbacks defined in the express.router. Please see below for some context.

const express = require('express');
const router = express.Router();

router.post('/dummy', sessionValidator, setJson.bind(dummyProduct, 'getDummyProducts'));

function sessionValidator(req, res, next) {
    var namespace = getNamespace('namespace');
    console.log(namespace.get('gtid'))
}
avico81 commented 3 years ago

I encountered the same issue. The problem is that next callback finishes before the other stuff get called, so I looked at the Namespace.prototype.run source and took what I needed from it. Beware though, I'm reusing the same namespace without destroying it which means that there's a small chance for a race condition, in which a request overwrites the 'req' value before the previous request is done with it (for me it wasn't a concern):

const CLS = require('continuation-local-storage');

function cls(req, res, next) {
  let namespace = CLS.getNamespace('api') || CLS.createNamespace('api');
  let context = namespace.createContext();
  namespace.enter(context);
  try{
    namespace.set('req', req);
    namespace.set('res', res);
    return next();
  } catch (e) {
    console.log('CLS ERR: ' + e);
  }
}

...
app.use('/api/*', cls);