How to shutdown gracefully in sveltekit? - javascript

I am using adapter-node and a mysql pool in a sveltekit web app.
Previously, using just nodejs and express and no sveltekit, I found I needed to shutdown the mysql pool connections cleanly or mysql could hang when restarting the app.
I had something like:
process.on('SIGINT', () => server.close(() => pool.end()));
How would I achieve the same result in a sveltekit app? Or is it not necessary (and why)?
I can see in the sveltekit implementation where it creates the server, but does not seem to have any way to access it so I can call close(). I don't think it would be safe to call pool.end() before the server closes.
I also couldn't find any discussion of graceful shutdown in the sveltekit docs. There was 1 github issue but it was closed over a year ago and that change has since been removed from the code.
I found a similar issue asked in the svelte github. It has no resolution, so there is likely no official solution yet. https://github.com/sveltejs/kit/issues/6841

Disclaimers at the time of writing this answer:
I am brand new to Svelte and SvelteKit and only a couple years into web development in general
SvelteKit is pre-1.0 and subject to change
I am not 100% sure this answer handles all cases
I am using adapter-node
SvelteKit currently recommends doing one-time setup code in src/hooks.server.ts. So when talking about graceful shutdown, I will only worry about shutting down the things I setup in src/hooks.server.ts.
The brief answer is to set up process.on() handlers for exit and SIGINT that do any required cleanup.
An example for setting up and shutting down a mysql database pool when using adapter-node:
// src/hooks.server.ts
// One time setup code
await import('./db.js');
// ... remaining hooks.server.ts code
// src/db.ts
import { PRIVATE_MYSQL_PASSWORD } from '$env/static/private';
import type { Pool, PoolConnection, MysqlError } from 'mysql';
import { createPool } from 'mysql';
const pool = createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: PRIVATE_MYSQL_PASSWORD,
database: 'my_db',
multipleStatements: false,
timezone: 'UTC',
dateStrings: ['DATE', 'DATETIME'],
});
process.on('exit', (code) => end_db_pool(pool));
process.on('SIGINT', () => end_db_pool(pool));
function end_db_pool(pool: any) {
pool.getConnection(function (err, connection) {
connection.query('select 1 from my_table;', function (err, rows) {
connection.release();
// pool.end() only works inside getConnection();
pool.end((err) => {
if (err) log('pool.end err: ' + err);
});
});
});
}
// ... remaining API for DB operations using the pool
Another solution I tried and might still be useful is to create a custom adapter. I copied the adapter-node code into my project and modified files/index.js for some experimenting. For now, I am using the code above and not a custom adapter-node.

Related

Why does creating a new tedious connection keep my program from finishing?

I'm trying to wrap the tedious MSSQL library API with promises to make it easier to use but whenever I make a new Promise that creates a new tedious SQL connection the program never exits and I'm having trouble figuring out why.
This is a stripped down version of my real code with the bare minimum needed to cause the issue.
const {Connection} = require('tedious');
const connect = () =>
new Promise((resolve, reject) =>
{
const config = {
userName: '----',
password: '----',
domain: '----',
server: '----',
options: {
database: '----',
port: 1805,
connectTimeout: 6000,
readOnlyIntent: true,
rowCollectionOnRequestCompletion: true,
encrypt: true
}
};
console.log('Pre new conn');
// const conn = new Connection(config);
console.log('Post new conn');
resolve('Resolved');
});
connect()
.then(conn => console.log(`conn: ${conn}`))
.catch(error => console.log(`Err: ${error}`));
When the connection succeeds I get the following output:
Pre new conn
Post new conn
conn: Resolved
If I uncomment the line const conn = new Connection(config); then I get the exact same output, but the program never exits!
I'm using tedious v2.6.4 and I'm running the program with node v8.11.3.
Node.js keeps track of open network connections, running timers and other things like that that might indicate that your node.js program is not yet done with whatever it was trying to do and when it sees that count is non-zero, it does not automatically exit. If you want it exit in that situation, you have three options:
You can close the connections that you are no longer using.
You can call .unref() on those connections to remove them from the count node.js is keeping. If it's a higher level thing like a database connection, you may need to call .unref() on the actual socket itself (which the DB interface may or may not make available to you) or perhaps the database shares it's own .unref() method for this purpose.
You can manually exit your process with process.exit() when you're doing with everything you wanted to do.

Is it safe to use a single Mongoose database from two files/processes?

I've been working on a server and a push notification daemon that will both run simultaneously and interact with the same database. The idea behind this is that if one goes down, the other will still function.
I normally use Swift but for this project I'm writing it in Node, using Mongoose as my database. I've created a helper class that I import in both my server.js file and my notifier.js file.
const Mongoose = require('mongoose');
const Device = require('./device'); // This is a Schema
var uri = 'mongodb://localhost/devices';
function Database() {
Mongoose.connect(uri, { useMongoClient: true }, function(err) {
console.log('connected: ' + err);
});
}
Database.prototype.findDevice = function(params, callback) {
Device.findOne(params, function(err, device) {
// etc...
});
};
module.exports = Database;
Then separately from both server.js and notifier.js I create objects and query the database:
const Database = require('./db');
const db = new Database();
db.findDevice(params, function(err, device) {
// Simplified, but I edit and save things back to the database via db
device.token = 'blah';
device.save();
});
Is this safe to do? When working with Swift (and Objective-C) I'm always concerned about making things thread safe. Is this a concern? Should I be worried about race conditions and modifying the same files at the same time?
Also, bonus question: How does Mongoose share a connection between files (or processes?). For example Mongoose.connection.readyState returns the same thing from different files.
The short answer is "safe enough."
The long answer has to do with understanding what sort of consistency guarantees your system needs, how you've configured MongoDB, and whether there's any sharding or replication going on.
For the latter, you'll want to read about atomicity and consistency and perhaps also peek at write concern.
A good way to answer these questions, even when you think you've figured it out, is to test scenarios: Hammer a duplicate of your system with fake data and events and see if what happen is OK or not.

Real time notifications node.js

I'm developing a calendar application with Node.js, express.js and Sequelize.
The application is simple, you can create tasks in your calendar, but you can also assign some tasks to others users of the system
I need to create a notification system with socket.io, but I don't have experience with websockets. My big doubt is how can I make my server send a notification to the user that you assign the task?
My ports configurations is on a folder called bin/www, my express routes are defined on a file called server.js
Any Idea?
I want to introduce you to ready to use backend system that enables you to easily build modern web applications with cool functionalities:
Persisted data: store your data and perform advanced searches on it.
Real-time notifications: subscribe to fine-grained subsets of data.
User Management: login, logout and security rules are no more a burden.
With this, you can focus to your main application development.
You can look at Kuzzle, wich is one project I working on:
First, start the service:
http://docs.kuzzle.io/guide/getting-started/#running-kuzzle-automagically
Then in your calendar application you can the javascript sdk
At this point you can create a document:
const
Kuzzle = require('kuzzle-sdk'),
kuzzle = new Kuzzle('http://localhost:7512');
const filter = {
equals: {
user: 'username'
}
}
// Subscribe every changes in calendar collection containing a field `user` equals to `username`
kuzzle
.collection('calendar', 'myproject')
.subscribe(filter, function(error, result) {
// triggered each time a document is updated/created !
// Here you can display a message in your application for instance
console.log('message received from kuzzle:', result)
})
// Each time you have to create a new task in your calendar, you can create a document that represent your task and persist it with kuzzle
const task = {
date: '2017-07-19T16:07:21.520Z',
title: 'my new task',
user: 'username'
}
// Creating a document from another app will notify all subscribers
kuzzle
.collection('calendar', 'myproject')
.createDocument(task)
I think this can help you :)
Documents are served though socket.io or native websockets when available
Don't hesitate to ask question ;)
As far as I can understand you need to pass your socket.io instance to other files, right ?
var sio = require('socket.io');
var io = sio();
app.io = io;
And you simply attach it to your server in your bin/www file
var io = app.io
io.attach(server);
Or what else I like to do, is adding socket.io middleware for express
// Socket.io middleware
app.use((req, res, next) => {
req.io = io;
next();
});
So you can access it in some of your router files
req.io.emit('newMsg', {
success: true
});

Node.js: initiate pg.Pool()

As per example (db.js)
const pg = require('pg');
const client_config = {...};
const pool = new pg.Pool(client_config);
pool.on('error', function(err, client) {
console.error('idle client error', err.mesae, err.stack);
});
module.exports.query = function(text, values, callback) {
return pool.query(text, values, callback);
};
module.exports.connect = function(callback) {
return pool.connect(callback);
};
and within an express (generated) application, do I have to initiate/require the Pool (db.js) in my app.js/on app start-up or do I simply require the db.js within my data models (respectively required in my routes)? Intuitively I would initiate the Pool on start-up rather than on each connection to the routes to avoid multiple initiations, but I am fairly new to Node.js.
Scroll a little further -- there are usage examples.
The reason this works is thanks to Node's module caching. The first time db.js is required, all the init code executes immediately. Subsequent require calls return the already-initialized module from the cache so the pool is already connected. In Express, you can avoid requiring db.js all over the place by using app.set('db', db); to attach the module to the Express application. You can then invoke req.app.get('db').query(...) in your route code.
If your data needs are complex enough to involve models, you may want to look into higher-level data access libraries since pg is more of a driver (think JDBC if you've done any Java). There are a lot of options ranging from minimal data mappers (I maintain MassiveJS) to query builders (Knex) to full-scale ORMs (Sequelize).

How to push notifications with angular.js?

I have been building a simple application to learn angular.js. So far I hooked up all the pieces in the MEAN stack and I am able to save and retrieve data from Mongo.
The app is essentially a todo list. The user can create a project and inside the project create "cards" with "todos" which can then be moved from state to state ("backlog", "in progress", "complete", etc.)
I would like to be able to push the notifications to all the people who are connected to tell their apps that a refresh is needed to get the latest todos. In other words, let's say that user A adds a new card to project A, I would like to send a message out to all users who are currently watching project A so that their application issues a project refresh to get the latest and greatest.
Any suggestions on how to proceed? Which technology, if any, I need to add to the MEAN stack to be able to do something like this?
Thanks in advance
Since you're on the MEAN stack, the standard recommendation in Node would be to use the Socket.IO API.
They provide the following example of two way messaging (which would facilitate your push messages very easily):
Client
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
Server
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
It will use websockets where possible, and fallback to AJAX long polling or Flash polling in browsers where there is no websocket support.
As for integrating with Angular, here's a good blog post on Socket.IO and Angular:
I'll be writing about how to integrate Socket.IO to add real-time
features to an AngularJS application. In this tutorial, I'm going to
walk through writing a instant messaging app.
If you're already working with Express, you should check out express.io.
It has a bunch of cool features like Session support and the ability to forward normal HTTP routes to realtime routes.
Here is a module we have written for getting AngularJS push notifications working in PhoneGap / Cordava (with full instructions):
http://www.scorchsoft.com/blog/free-angularjs-cordova-push-notification-plugin/
Simply download the example code and install. There is also code included for setting up the pushing component in PHP.
Why not with HTML5 Notification API....
export default class NotificationService {
/**
* Constructor of the class.
*
*/
constructor() {}
showPushNotification(title: string = '', message: string, iconPush) {
if (window.Notification && Notification.permission !== "denied") {
Notification.requestPermission(function(status) {
var n = new Notification(title, {
body: message,
icon: iconPush
});
});
}
}
}

Categories