In my app I need to synchronize some resources nightly. The synchronization is started by sending PUT request to /api/sync/
with a JSON array of objects in the body. My task is to mirror the data in JSON in my app, so firstly I must resolve what needs to be added, updated and deleted, then issue appropriate queries to the database. That could take a while so I don't like any other request to get in a way.
So I need to close Express' connections, do the synchronization and start listening again. But there are 2 problems:
http.Server.close()
prevents receiving new connections but waits for already executing to finish- it is possible that just after the
PUT /api/sync/
there could be another request received just before previous one can invokeclose()
. Thus, 2 incomingPUT /api/sync/
request would be accepted into processing.
At the moment I create the Express app this way:
// index.js
const app = express();
app.server = new Server(app);
app.server.start();
// Server.ts
export class Server {
public readonly server: http.Server;
private readonly close: () => Promise<http.Server>;
private readonly listen: (port: number) => Promise<http.Server>;
constructor(public readonly app: express.Application) {
this.server = http.createServer(app);
this.close = promisify<http.Server>(this.server.close.bind(this.server));
this.listen = promisify<http.Server>(this.server.listen.bind(this.server));
}
async start() {
console.log('START');
await this.listen(Number(process.env.PORT));
}
async stop() {
console.log('STOP');
if (!this.server.listening) {
throw new Error('Server is not listening');
}
await this.close();
}
}
// middleware
module.exports = fn => (req, res, next) => {
console.log('START MAINTENANCE');
req.app.server.stop()
.then(() => {
const endMaintenance = (err) => {
console.log('END MAINTENANCE');
req.app.server.start().then(() => next(err));
};
Promise.resolve(fn(req, res)).then(() => endMaintenance())
.catch(err => endMaintenance(err));
});
};
// route definition
routes.put(
'/',
exclusiveRun(
async (req: Request, res: Response) => {
await dataSync.synchronize(req.body);
res.sendStatus(204);
}
)
);
But still, when I send 2 requests to /api/sync
endpoint in a row, Express accepts them and only then one of them throws "Server is not listening" error. Where do I make an error? Is that approach enough to prevent disrupting the sync process (or otherwise causing trouble when sync is underway)?