SlideShare a Scribd company logo
Node.js Connector
Diego DUPIN
MariaDB Connector Engineer
MariaDB Corporation
PROGRAM
- Getting started
- Best practice
- Advanced topic
- ORM
NODE.JS CONNECTORS
Existing community connectors
● mysqljs/mysql (npm mysql)
(+ Promise-mysql to have promise)
● sidorares/node-mysql2 (npm mysql2)
● mscdex/node-mariasql (npm mariasql)
New connector
● MariaDB/mariadb-connector-nodejs (npm
mariadb)
WHY A NEW CONNECTOR
● New functionalities
● Better performance
Benchmark : “SELECT * FROM mysql.user LIMIT 1” on local DB
Getting started
QUICK START - INSTALLATION
through npm.
$ npm install mariadb
To use the Connector, you need to import the package into your application code.
const mariadb = require('mariadb'); //promise implementation
or
const mariadb = require('mariadb/callback'); //callback implementation
QUICK START - CONNECTION
Promise
const mariadb = require("mariadb");
mariadb.createConnection({
user: "root",
database: "db",
host: "localhost",
port: 3306
})
.then(conn => {
console.log("Connected successful");
conn.end();
})
.catch(err => {
console.log("Error: " + err.message);
});
const mariadb = require("mariadb/callback");
const conn = mariadb.createConnection({
user: "root",
database: "db",
host: "localhost",
port: 3306
});
conn.connect(err => {
if (err) {
console.log("Error: " + err.message);
} else {
console.log("Connected successful");
conn.end();
}
});
Callback
QUICK START - INSERT
conn.query(
"CREATE TEMPORARY TABLE myTable " +
"(id int NOT NULL AUTO_INCREMENT, firstName varchar(256), lastName varchar(256), " +
" PRIMARY KEY (id))"
)
.then(() => {
return conn.query("INSERT INTO myTable(firstName, lastName) VALUES (?, ?)", [
"john",
"smith"
]);
})
.then(res => {
console.log(res); //{ affectedRows: 1, insertId: 1, warningStatus: 0 }
conn.end();
})
.catch(err => { });
QUICK START - SELECT
conn.query(
"SELECT ID,COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS " +
"WHERE CHARACTER_SET_NAME = ? LIMIT 2",
["utf8mb4"]
)
.then(res => {
console.log(res);
/* resultset is an array of rows, represented by JSON object
[
{ ID: 45, COLLATION_NAME: 'utf8mb4_general_ci' },
{ ID: 46, COLLATION_NAME: 'utf8mb4_bin' }
]
*/
conn.end();
})
.catch(err => { … });
CONNECTION ESSENTIAL API
● connection.query(sql[, values]) → Promise: Executes a query.
● connection.queryStream(sql[, values]) → Emitter: Executes a query, streaming results.
● connection.batch(sql, values) → Promise: fast batch processing.
● connection.beginTransaction() → Promise: Begins a transaction.
● connection.commit() → Promise: Commits the current transaction, if any.
● connection.rollback() → Promise: Rolls back the current transaction, if any.
● connection.ping() → Promise: Sends a 1 byte packet to the database to validate the connection.
● connection.end() → Promise: Gracefully close the connection.
SSL
SSL
One-Way SSL Authentication or Two-Way SSL Authentication
const fs = require("fs");
const mariadb = require('mariadb');
//reading certificates from file
const serverCert = [fs.readFileSync("server.pem", "utf8")];
//connecting
mariadb
.createConnection({
user: "myUser",
host: "myHost.com",
ssl: {
ca: serverCert
}
}).then(conn => {})
Pooling
POOLING
Basically a database connection cache implementation
Connections are expensive. On local DB:
● 2.4ms for a basic connection
● 0.05ms for a simple query
Problem : correctness and reliability
POOLING
Event implementation
- Pool handle new connection creation one by one
- New connection are added to idle connection queue
On connection failure:
- Pool revalidate all other connections
- Continue creating new connections creation one by one
Connection request are queued separately
Idle
Connections
POOLING
Connection request
Idle
ConnectionsConnection request
Connection request
Request
queue
POOLING
- Implementation to handle query pikes
Example with a pool that is configured to have a maximum of 50 connections. actual
connection number is 5.
With a basis of a connection creation taking 2.4ms, and query taking 0.05ms (example
on a local server).
Everything is quiet, and then ... Boom! ... 100 queries on the pool at once, wanting a
connection.
POOLING
POOLING
POOLING
Connection pools SIZING error
Example 10 000 user simultaneously, 20 000 transaction per second.
What value to connectionLimit (max connection number in pool) ?
100 ? 500 ? 1000 ?
POOLING - CONFIGURATION
acquireTimeout
t
acquireTimeout Timeout to get a new connection from pool.
Maximum number of connection in pool.
Delay to avoid connection validation
Disabling connection control
connectionLimit
minDelayValidation
noControlAfterUse
POOL API
● pool.getConnection() → Promise : Creates a new connection.
● pool.query(sql[, values]) → Promise: Executes a query.
● pool.batch(sql, values) → Promise: Executes a batch
Stats
● pool.activeConnections() → Number: Gets current active connection number.
● pool.totalConnections() → Number: Gets current total connection number.
● pool.idleConnections() → Number: Gets current idle connection number.
● pool.taskQueueSize() → Number: Gets current stacked request.
POOLING - CONFIGURATION
const pool = mariadb.createPool({ user: "root", database: "testn", host: "localhost", port: 3306,
sessionVariables: { wait_timeout: 31536000 },
acquireTimeout: 5000, connectionLimit: 8,
});
pool
.getConnection()
.then(conn => {
console.log("Connected successfully to server");
conn.release();
})
.catch(err => {
console.log("Error during connection: " + err.message);
});
Server expect connection to be used (@wait_timeout)
MULTI-SERVER
MULTI-HOST - ESSENTIAL API
“Cluster” offer handle multiple pools permitting load balancing and high availability
const mariadb = require("mariadb");
const cluster = mariadb.createPoolCluster();
cluster.add("master", { host: "mydb1.com", ... });
cluster.add("slave1", { host: "mydb2.com", ... });
cluster.add("slave2", { host: "mydb3.com", ... });
cluster
.getConnection("slave*", "RR") //RR: ROUND-ROBIN
.then(conn => { })
.catch(err => { });
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST - ESSENTIAL API
client
Master
Slave B
Slave A
Cluster.getConnection("slave*", "RR")
MULTI-HOST
SELECTORS
● RR (round-robin)
● RANDOM
● ORDER
MULTI-HOST - ESSENTIAL API
● poolCluster.add(id, config) : add a pool to cluster.
● poolCluster.remove(pattern) : remove and end pool according to pattern.
● poolCluster.end() → Promise : end cluster.
● poolCluster.getConnection(pattern, selector) → Promise : return a connection from
cluster.
● poolCluster.of(pattern, selector) → FilteredPoolCluster : return a subset of cluster.
MULTI-HOST - ESSENTIAL API
poolCluster.of(pattern, selector) → FilteredPoolCluster : return a subset of cluster.
const cluster = mariadb.createPoolCluster();
cluster.add("master", { host: "mydb1.com", ... });
cluster.add("slave1", { host: "mydb2.com", ... });
cluster.add("slave2", { host: "mydb3.com", ... });
const slaves = cluster.of(/^slave?/, 'RANDOM');
slaves.getConnection().then( ... );
slaves.query(sql, values).then( ... );
slaves.batch(sql, values).then( ... );
New features
Pipelining
PIPELINING
Pipelining is a technique in which
multiple requests are sent on a single
TCP connection without waiting for the
corresponding responses.
This saves round trip time.
PIPELINING - without
const uuid = uuidv1(); //generate new UUID
conn
.beginTransaction()
.then(() => {
return conn.query("INSERT INTO basket(basketId) values (?)", uuid);
})
.then(() => {
return conn.query(
"INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)",
[ uuid, 100 ]);
})
.then(() => {
return conn.query(
"INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)",
[ uuid, 101 ]);
})
.then(() => { conn.commit(); })
.catch(err => { conn.rollback(); });
PIPELINING - with
const uuid = uuidv1(); //generate new UUID
conn
.beginTransaction()
.then(() => {
return Promise.all([
conn.query("INSERT INTO basket(basketId) values (?)", uuid),
conn.query(
"INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)",
[uuid, 100]),
conn.query(
"INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)",
[uuid, 101])
]);
})
.then(() => { conn.commit(); })
.catch(err => { conn.rollback(); });
PIPELINING
Local DB:
abuse : socket buffering
Streaming
Streaming
Goal : Avoid loading all in memory
● Streaming resultset -> avoiding loading large resultset totally into memory
○ By event
○ With pipe
● Streaming sent -> sending buffer by chunk
Streaming resultset
BY EVENT :
Limitations :
● Server net_write_timeout.
○ For a command: SET STATEMENT net_write_timeout=10000 FOR XXX
○ For a connection: sessionVariables: { net_write_timeout: 31536000 }
connection
.queryStream("SELECT * FROM mysql.user")
.on("error", err => {})
.on("fields", meta => {})
.on("data", row => {})
.on("end", () => {});
Streaming resultset
USING PIPE : const someWriterStream = fs.createWriteStream("./jsonUsers.txt");
const transformStream = new stream.Transform({
objectMode: true,
transform: function transformer(chunk, encoding, callback) {
callback(null, JSON.stringify(chunk));
}
});
const queryStream = connection.queryStream("SELECT * FROM mysql.user");
stream.pipeline(queryStream, transformStream, someWriterStream);
Streaming - sending
const https = require("https");
https.get(
"https://node.green/#ES2018-features-Promise-prototype-finally-basic-support", //3Mb page
readableStream => {
connection
.query("INSERT INTO StreamingContent (b) VALUE (?)", [readableStream])
.then(res => {})
.catch(err => {});
});
Limitations :
● Server net_read_timeout : SET STATEMENT net_read_timeout=10000 FOR XXX
○ For a connection: sessionVariables: { net_read_timeout: 10000 }
● max_allowed_packet
Batching
BATCH INSERT
conn.batch("INSERT INTO myTable(firstName, lastName) VALUES (?, ?)", [
["john", "smith"],
["jack", "brown"]
]);
})
.then(res => { })
.catch(err => { });
Benchmark : 100 * insert 100 characters on local DB
ORM - sequelize
ORM
$ npm install --save express body-parser sequelize@5.0.0-beta.16 mariadb
const Sequelize = require('sequelize');
const sequelize = new Sequelize('testn', 'root', null, {
host: 'localhost',
dialect: 'mariadb',
pool: {
max: 5,
min: 5,
acquire: 30000,
idle: 10000
}
});
ORM
const User = sequelize.define('myUser', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
firstName: { type: Sequelize.STRING },
lastName: { type: Sequelize.STRING }
});
User.sync({ force: true })
.then(() => {
return User.create({ firstName: 'John', lastName: 'Hancock' });
});
ORM
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const port = 3000;
app.listen(port, () => { console.log(`Running on http://localhost:${port}`) });
// get all users
app.get('/api/users', (req, res) => {
User.findAll().then(users => res.json(users))
});
tricks
DEBUGGING
Connection options ‘trace’ default to false, nice to have in development mode.
{ Error: (conn=149, no: 1146, SQLState: 42S02) Table 'testn.unknownTable' doesn't exist
sql: SELECT * FROM unknownTable - parameters:[]
at Object.module.exports.createError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/misc/errors.js:55:10)
at Packet.readError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet.js:506:19)
at Query.readResponsePacket (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/cmd/resultset.js:47:28)
at PacketInputStream.receivePacket (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet-input-stream.js:73:9)
at PacketInputStream.onData (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet-input-stream.js:129:20)
at Socket.emit (events.js:197:13)
at addChunk (_stream_readable.js:288:12)
at readableAddChunk (_stream_readable.js:269:11)
at Socket.Readable.push (_stream_readable.js:224:10)
at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17)
fatal: false,
errno: 1146,
sqlState: '42S02',
code: 'ER_NO_SUCH_TABLE' }
DEBUGGING
With trace
{ Error: (conn=150, no: 1146, SQLState: 42S02) Table 'testn.unknownTable' doesn't exist
sql: SELECT * FROM unknownTable - parameters:[]
at Object.module.exports.createError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/misc/errors.js:55:10)
...
at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17)
From event:
at /home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/connection.js:166:29
at new Promise (<anonymous>)
at Connection.query (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/connection.js:164:12)
at mariadb.createConnection.then.conn (/home/diego/IdeaProjects/test_node/lib/promise/trace.js:13:8)
at processTicksAndRejections (internal/process/next_tick.js:81:5)
fatal: false,
errno: 1146,
sqlState: '42S02',
code: 'ER_NO_SUCH_TABLE' }
AVOID TCP-IP layer for local connection
Connection options ‘socketPath‘ for local server
- UNIX domain
- Windows named pipe
const mariadb = require('mariadb');
mariadb.createConnection({ socketPath: '/tmp/mysql.sock', user: 'root' })
.then(conn => { ... })
.catch(err => { ... });
METADATA
● Select query = row datas + metadata
Metadata = datatype, format, and lots of additional infos: schema, table name,
table alias, column name, column alias, … Not always needed
conn.query("select * from mysql.user u LIMIT 1")
.then(rows => {
console.log(rows);
// [
// { Host: 'localhost', User: 'root', … }
// ]
});
conn .query({
sql: "select * from mysql.user u LIMIT 1",
rowsAsArray: true
})
.then(rows => {
console.log(rows);
// [
// [ 'localhost', 'root', …]
// ]
});
What next ?
Authentication plugins (Kerberos, ed25519)
Performance improvement
Failover enhancement
Pools improvement
THANK YOU!

More Related Content

What's hot (20)

PPTX
Session: A Reference Architecture for Running Modern APIs with NGINX Unit and...
NGINX, Inc.
 
PDF
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
Amazon Web Services Japan
 
PDF
실전 서버 부하테스트 노하우
YoungSu Son
 
PDF
Meetup #4: AWS ELB Deep dive & Best practices
AWS Vietnam Community
 
PDF
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
Amazon Web Services Korea
 
PDF
효율적인 빅데이터 분석 및 처리를 위한 Glue, EMR 활용 - 김태현 솔루션즈 아키텍트, AWS :: AWS Summit Seoul 2019
Amazon Web Services Korea
 
PPTX
AWS SQS SNS
Durgesh Vaishnav
 
PDF
클라우드 네이티브 데이터베이스 서비스로 Oracle RAC 전환 - 김지훈 :: AWS 클라우드 마이그레이션 온라인
Amazon Web Services Korea
 
PDF
[AWSマイスターシリーズ] Instance Store & Elastic Block Store
Amazon Web Services Japan
 
PPTX
4. 대용량 아키텍쳐 설계 패턴
Terry Cho
 
PDF
누가 내 엔터프라이즈 고객을 클라우드로 옮겼을까?-양승호, Head of Cloud Modernization,AWS::AWS 마이그레이션 ...
Amazon Web Services Korea
 
PPTX
Mongo DB 성능최적화 전략
Jin wook
 
PDF
AWSではじめるDNSSEC
Tomohiro Nakashima
 
PDF
[AWS Builders] AWS상의 보안 위협 탐지 및 대응
Amazon Web Services Korea
 
PDF
AWS DirectConnect 구성 가이드 (김용우) - 파트너 웨비나 시리즈
Amazon Web Services Korea
 
PDF
[AWS Builders] AWS와 함께하는 클라우드 컴퓨팅
Amazon Web Services Korea
 
PDF
Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링
Amazon Web Services Korea
 
PDF
AWS Aurora 운영사례 (by 배은미)
I Goo Lee.
 
PDF
천만 사용자를 위한 AWS 아키텍처 보안 모범 사례 (윤석찬, 테크에반젤리스트)
Amazon Web Services Korea
 
PPTX
Azure AD の新しいデバイス管理パターンを理解しよう
Yusuke Kodama
 
Session: A Reference Architecture for Running Modern APIs with NGINX Unit and...
NGINX, Inc.
 
20180704 AWS Black Belt Online Seminar Amazon Elastic File System (Amazon EFS...
Amazon Web Services Japan
 
실전 서버 부하테스트 노하우
YoungSu Son
 
Meetup #4: AWS ELB Deep dive & Best practices
AWS Vietnam Community
 
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
Amazon Web Services Korea
 
효율적인 빅데이터 분석 및 처리를 위한 Glue, EMR 활용 - 김태현 솔루션즈 아키텍트, AWS :: AWS Summit Seoul 2019
Amazon Web Services Korea
 
AWS SQS SNS
Durgesh Vaishnav
 
클라우드 네이티브 데이터베이스 서비스로 Oracle RAC 전환 - 김지훈 :: AWS 클라우드 마이그레이션 온라인
Amazon Web Services Korea
 
[AWSマイスターシリーズ] Instance Store & Elastic Block Store
Amazon Web Services Japan
 
4. 대용량 아키텍쳐 설계 패턴
Terry Cho
 
누가 내 엔터프라이즈 고객을 클라우드로 옮겼을까?-양승호, Head of Cloud Modernization,AWS::AWS 마이그레이션 ...
Amazon Web Services Korea
 
Mongo DB 성능최적화 전략
Jin wook
 
AWSではじめるDNSSEC
Tomohiro Nakashima
 
[AWS Builders] AWS상의 보안 위협 탐지 및 대응
Amazon Web Services Korea
 
AWS DirectConnect 구성 가이드 (김용우) - 파트너 웨비나 시리즈
Amazon Web Services Korea
 
[AWS Builders] AWS와 함께하는 클라우드 컴퓨팅
Amazon Web Services Korea
 
Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링
Amazon Web Services Korea
 
AWS Aurora 운영사례 (by 배은미)
I Goo Lee.
 
천만 사용자를 위한 AWS 아키텍처 보안 모범 사례 (윤석찬, 테크에반젤리스트)
Amazon Web Services Korea
 
Azure AD の新しいデバイス管理パターンを理解しよう
Yusuke Kodama
 

Similar to Building better Node.js applications on MariaDB (20)

PDF
Node.js with MySQL.pdf
SudhanshiBakre1
 
PDF
Developing for Node.JS with MySQL and NoSQL
John David Duncan
 
PDF
Introduction to Node.js
Somkiat Puisungnoen
 
PPTX
Oracle application container cloud back end integration using node final
Getting value from IoT, Integration and Data Analytics
 
PDF
Building node.js applications with Database Jones
John David Duncan
 
PPTX
Getting Started with MongoDB and NodeJS
MongoDB
 
PDF
Node.js and Oracle Database: New Development Techniques
Christopher Jones
 
PPTX
Intro to node and mongodb 1
Mohammad Qureshi
 
PPTX
PostgreSQL, MongoDb, Express, React, Structured
priya951125
 
PPTX
Html web sql database
AbhishekMondal42
 
PPTX
NodeJS
Alok Guha
 
KEY
Playing With Fire - An Introduction to Node.js
Mike Hagedorn
 
PDF
From Node.js to Design Patterns
Luciano Mammino
 
PPT
Advance MySQL Docstore Features
sankalita chakraborty
 
PPTX
LowlaDB intro March 2015
Teamstudio
 
PPTX
Building a Node.js API backend with LoopBack in 5 Minutes
Raymond Feng
 
PPTX
StrongLoop Overview
Shubhra Kar
 
PDF
Nodejs - A quick tour (v6)
Felix Geisendörfer
 
PDF
MongoDB and Node.js
Norberto Leite
 
KEY
Realtime webapp with node.js
robin_sy
 
Node.js with MySQL.pdf
SudhanshiBakre1
 
Developing for Node.JS with MySQL and NoSQL
John David Duncan
 
Introduction to Node.js
Somkiat Puisungnoen
 
Oracle application container cloud back end integration using node final
Getting value from IoT, Integration and Data Analytics
 
Building node.js applications with Database Jones
John David Duncan
 
Getting Started with MongoDB and NodeJS
MongoDB
 
Node.js and Oracle Database: New Development Techniques
Christopher Jones
 
Intro to node and mongodb 1
Mohammad Qureshi
 
PostgreSQL, MongoDb, Express, React, Structured
priya951125
 
Html web sql database
AbhishekMondal42
 
NodeJS
Alok Guha
 
Playing With Fire - An Introduction to Node.js
Mike Hagedorn
 
From Node.js to Design Patterns
Luciano Mammino
 
Advance MySQL Docstore Features
sankalita chakraborty
 
LowlaDB intro March 2015
Teamstudio
 
Building a Node.js API backend with LoopBack in 5 Minutes
Raymond Feng
 
StrongLoop Overview
Shubhra Kar
 
Nodejs - A quick tour (v6)
Felix Geisendörfer
 
MongoDB and Node.js
Norberto Leite
 
Realtime webapp with node.js
robin_sy
 
Ad

More from MariaDB plc (20)

PDF
MariaDB Berlin Roadshow Slides - 8 April 2025
MariaDB plc
 
PDF
MariaDB München Roadshow - 24 September, 2024
MariaDB plc
 
PDF
MariaDB Paris Roadshow - 19 September 2024
MariaDB plc
 
PDF
MariaDB Amsterdam Roadshow: 19 September, 2024
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - MaxScale 23.02.x
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - Newpharma
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - Cloud
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - MariaDB Enterprise
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - Performance Optimization
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - MaxScale
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - novadys presentation
MariaDB plc
 
PDF
MariaDB Paris Workshop 2023 - DARVA presentation
MariaDB plc
 
PDF
MariaDB Tech und Business Update Hamburg 2023 - MariaDB Enterprise Server
MariaDB plc
 
PDF
MariaDB SkySQL Autonome Skalierung, Observability, Cloud-Backup
MariaDB plc
 
PDF
Einführung : MariaDB Tech und Business Update Hamburg 2023
MariaDB plc
 
PDF
Hochverfügbarkeitslösungen mit MariaDB
MariaDB plc
 
PDF
Die Neuheiten in MariaDB Enterprise Server
MariaDB plc
 
PDF
Global Data Replication with Galera for Ansell Guardian®
MariaDB plc
 
PDF
Introducing workload analysis
MariaDB plc
 
PDF
Under the hood: SkySQL monitoring
MariaDB plc
 
MariaDB Berlin Roadshow Slides - 8 April 2025
MariaDB plc
 
MariaDB München Roadshow - 24 September, 2024
MariaDB plc
 
MariaDB Paris Roadshow - 19 September 2024
MariaDB plc
 
MariaDB Amsterdam Roadshow: 19 September, 2024
MariaDB plc
 
MariaDB Paris Workshop 2023 - MaxScale 23.02.x
MariaDB plc
 
MariaDB Paris Workshop 2023 - Newpharma
MariaDB plc
 
MariaDB Paris Workshop 2023 - Cloud
MariaDB plc
 
MariaDB Paris Workshop 2023 - MariaDB Enterprise
MariaDB plc
 
MariaDB Paris Workshop 2023 - Performance Optimization
MariaDB plc
 
MariaDB Paris Workshop 2023 - MaxScale
MariaDB plc
 
MariaDB Paris Workshop 2023 - novadys presentation
MariaDB plc
 
MariaDB Paris Workshop 2023 - DARVA presentation
MariaDB plc
 
MariaDB Tech und Business Update Hamburg 2023 - MariaDB Enterprise Server
MariaDB plc
 
MariaDB SkySQL Autonome Skalierung, Observability, Cloud-Backup
MariaDB plc
 
Einführung : MariaDB Tech und Business Update Hamburg 2023
MariaDB plc
 
Hochverfügbarkeitslösungen mit MariaDB
MariaDB plc
 
Die Neuheiten in MariaDB Enterprise Server
MariaDB plc
 
Global Data Replication with Galera for Ansell Guardian®
MariaDB plc
 
Introducing workload analysis
MariaDB plc
 
Under the hood: SkySQL monitoring
MariaDB plc
 
Ad

Recently uploaded (20)

PPTX
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
PDF
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
PDF
Executive Business Intelligence Dashboards
vandeslie24
 
PPTX
Engineering the Java Web Application (MVC)
abhishekoza1981
 
PDF
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
PDF
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
PDF
Powering GIS with FME and VertiGIS - Peak of Data & AI 2025
Safe Software
 
PPTX
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
PDF
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
PPTX
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
DOCX
Import Data Form Excel to Tally Services
Tally xperts
 
PPTX
A Complete Guide to Salesforce SMS Integrations Build Scalable Messaging With...
360 SMS APP
 
PPT
MergeSortfbsjbjsfk sdfik k
RafishaikIT02044
 
PPTX
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
PPTX
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
PPTX
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
PPTX
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
PDF
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
PPTX
Fundamentals_of_Microservices_Architecture.pptx
MuhammadUzair504018
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
Equipment Management Software BIS Safety UK.pptx
BIS Safety Software
 
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
Executive Business Intelligence Dashboards
vandeslie24
 
Engineering the Java Web Application (MVC)
abhishekoza1981
 
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
Efficient, Automated Claims Processing Software for Insurers
Insurance Tech Services
 
Powering GIS with FME and VertiGIS - Peak of Data & AI 2025
Safe Software
 
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pdf
Varsha Nayak
 
Migrating Millions of Users with Debezium, Apache Kafka, and an Acyclic Synch...
MD Sayem Ahmed
 
Import Data Form Excel to Tally Services
Tally xperts
 
A Complete Guide to Salesforce SMS Integrations Build Scalable Messaging With...
360 SMS APP
 
MergeSortfbsjbjsfk sdfik k
RafishaikIT02044
 
Tally_Basic_Operations_Presentation.pptx
AditiBansal54083
 
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
Why Businesses Are Switching to Open Source Alternatives to Crystal Reports.pptx
Varsha Nayak
 
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
Fundamentals_of_Microservices_Architecture.pptx
MuhammadUzair504018
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 

Building better Node.js applications on MariaDB

  • 1. Node.js Connector Diego DUPIN MariaDB Connector Engineer MariaDB Corporation
  • 2. PROGRAM - Getting started - Best practice - Advanced topic - ORM
  • 3. NODE.JS CONNECTORS Existing community connectors ● mysqljs/mysql (npm mysql) (+ Promise-mysql to have promise) ● sidorares/node-mysql2 (npm mysql2) ● mscdex/node-mariasql (npm mariasql) New connector ● MariaDB/mariadb-connector-nodejs (npm mariadb)
  • 4. WHY A NEW CONNECTOR ● New functionalities ● Better performance Benchmark : “SELECT * FROM mysql.user LIMIT 1” on local DB
  • 6. QUICK START - INSTALLATION through npm. $ npm install mariadb To use the Connector, you need to import the package into your application code. const mariadb = require('mariadb'); //promise implementation or const mariadb = require('mariadb/callback'); //callback implementation
  • 7. QUICK START - CONNECTION Promise const mariadb = require("mariadb"); mariadb.createConnection({ user: "root", database: "db", host: "localhost", port: 3306 }) .then(conn => { console.log("Connected successful"); conn.end(); }) .catch(err => { console.log("Error: " + err.message); }); const mariadb = require("mariadb/callback"); const conn = mariadb.createConnection({ user: "root", database: "db", host: "localhost", port: 3306 }); conn.connect(err => { if (err) { console.log("Error: " + err.message); } else { console.log("Connected successful"); conn.end(); } }); Callback
  • 8. QUICK START - INSERT conn.query( "CREATE TEMPORARY TABLE myTable " + "(id int NOT NULL AUTO_INCREMENT, firstName varchar(256), lastName varchar(256), " + " PRIMARY KEY (id))" ) .then(() => { return conn.query("INSERT INTO myTable(firstName, lastName) VALUES (?, ?)", [ "john", "smith" ]); }) .then(res => { console.log(res); //{ affectedRows: 1, insertId: 1, warningStatus: 0 } conn.end(); }) .catch(err => { });
  • 9. QUICK START - SELECT conn.query( "SELECT ID,COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS " + "WHERE CHARACTER_SET_NAME = ? LIMIT 2", ["utf8mb4"] ) .then(res => { console.log(res); /* resultset is an array of rows, represented by JSON object [ { ID: 45, COLLATION_NAME: 'utf8mb4_general_ci' }, { ID: 46, COLLATION_NAME: 'utf8mb4_bin' } ] */ conn.end(); }) .catch(err => { … });
  • 10. CONNECTION ESSENTIAL API ● connection.query(sql[, values]) → Promise: Executes a query. ● connection.queryStream(sql[, values]) → Emitter: Executes a query, streaming results. ● connection.batch(sql, values) → Promise: fast batch processing. ● connection.beginTransaction() → Promise: Begins a transaction. ● connection.commit() → Promise: Commits the current transaction, if any. ● connection.rollback() → Promise: Rolls back the current transaction, if any. ● connection.ping() → Promise: Sends a 1 byte packet to the database to validate the connection. ● connection.end() → Promise: Gracefully close the connection.
  • 11. SSL
  • 12. SSL One-Way SSL Authentication or Two-Way SSL Authentication const fs = require("fs"); const mariadb = require('mariadb'); //reading certificates from file const serverCert = [fs.readFileSync("server.pem", "utf8")]; //connecting mariadb .createConnection({ user: "myUser", host: "myHost.com", ssl: { ca: serverCert } }).then(conn => {})
  • 14. POOLING Basically a database connection cache implementation Connections are expensive. On local DB: ● 2.4ms for a basic connection ● 0.05ms for a simple query Problem : correctness and reliability
  • 15. POOLING Event implementation - Pool handle new connection creation one by one - New connection are added to idle connection queue On connection failure: - Pool revalidate all other connections - Continue creating new connections creation one by one Connection request are queued separately Idle Connections
  • 17. POOLING - Implementation to handle query pikes Example with a pool that is configured to have a maximum of 50 connections. actual connection number is 5. With a basis of a connection creation taking 2.4ms, and query taking 0.05ms (example on a local server). Everything is quiet, and then ... Boom! ... 100 queries on the pool at once, wanting a connection.
  • 20. POOLING Connection pools SIZING error Example 10 000 user simultaneously, 20 000 transaction per second. What value to connectionLimit (max connection number in pool) ? 100 ? 500 ? 1000 ?
  • 21. POOLING - CONFIGURATION acquireTimeout t acquireTimeout Timeout to get a new connection from pool. Maximum number of connection in pool. Delay to avoid connection validation Disabling connection control connectionLimit minDelayValidation noControlAfterUse
  • 22. POOL API ● pool.getConnection() → Promise : Creates a new connection. ● pool.query(sql[, values]) → Promise: Executes a query. ● pool.batch(sql, values) → Promise: Executes a batch Stats ● pool.activeConnections() → Number: Gets current active connection number. ● pool.totalConnections() → Number: Gets current total connection number. ● pool.idleConnections() → Number: Gets current idle connection number. ● pool.taskQueueSize() → Number: Gets current stacked request.
  • 23. POOLING - CONFIGURATION const pool = mariadb.createPool({ user: "root", database: "testn", host: "localhost", port: 3306, sessionVariables: { wait_timeout: 31536000 }, acquireTimeout: 5000, connectionLimit: 8, }); pool .getConnection() .then(conn => { console.log("Connected successfully to server"); conn.release(); }) .catch(err => { console.log("Error during connection: " + err.message); }); Server expect connection to be used (@wait_timeout)
  • 25. MULTI-HOST - ESSENTIAL API “Cluster” offer handle multiple pools permitting load balancing and high availability const mariadb = require("mariadb"); const cluster = mariadb.createPoolCluster(); cluster.add("master", { host: "mydb1.com", ... }); cluster.add("slave1", { host: "mydb2.com", ... }); cluster.add("slave2", { host: "mydb3.com", ... }); cluster .getConnection("slave*", "RR") //RR: ROUND-ROBIN .then(conn => { }) .catch(err => { });
  • 26. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 27. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 28. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 29. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 30. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 31. MULTI-HOST - ESSENTIAL API client Master Slave B Slave A Cluster.getConnection("slave*", "RR")
  • 33. MULTI-HOST - ESSENTIAL API ● poolCluster.add(id, config) : add a pool to cluster. ● poolCluster.remove(pattern) : remove and end pool according to pattern. ● poolCluster.end() → Promise : end cluster. ● poolCluster.getConnection(pattern, selector) → Promise : return a connection from cluster. ● poolCluster.of(pattern, selector) → FilteredPoolCluster : return a subset of cluster.
  • 34. MULTI-HOST - ESSENTIAL API poolCluster.of(pattern, selector) → FilteredPoolCluster : return a subset of cluster. const cluster = mariadb.createPoolCluster(); cluster.add("master", { host: "mydb1.com", ... }); cluster.add("slave1", { host: "mydb2.com", ... }); cluster.add("slave2", { host: "mydb3.com", ... }); const slaves = cluster.of(/^slave?/, 'RANDOM'); slaves.getConnection().then( ... ); slaves.query(sql, values).then( ... ); slaves.batch(sql, values).then( ... );
  • 37. PIPELINING Pipelining is a technique in which multiple requests are sent on a single TCP connection without waiting for the corresponding responses. This saves round trip time.
  • 38. PIPELINING - without const uuid = uuidv1(); //generate new UUID conn .beginTransaction() .then(() => { return conn.query("INSERT INTO basket(basketId) values (?)", uuid); }) .then(() => { return conn.query( "INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)", [ uuid, 100 ]); }) .then(() => { return conn.query( "INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)", [ uuid, 101 ]); }) .then(() => { conn.commit(); }) .catch(err => { conn.rollback(); });
  • 39. PIPELINING - with const uuid = uuidv1(); //generate new UUID conn .beginTransaction() .then(() => { return Promise.all([ conn.query("INSERT INTO basket(basketId) values (?)", uuid), conn.query( "INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)", [uuid, 100]), conn.query( "INSERT INTO basket_item(basketId, itemId) VALUES (?, ?)", [uuid, 101]) ]); }) .then(() => { conn.commit(); }) .catch(err => { conn.rollback(); });
  • 40. PIPELINING Local DB: abuse : socket buffering
  • 42. Streaming Goal : Avoid loading all in memory ● Streaming resultset -> avoiding loading large resultset totally into memory ○ By event ○ With pipe ● Streaming sent -> sending buffer by chunk
  • 43. Streaming resultset BY EVENT : Limitations : ● Server net_write_timeout. ○ For a command: SET STATEMENT net_write_timeout=10000 FOR XXX ○ For a connection: sessionVariables: { net_write_timeout: 31536000 } connection .queryStream("SELECT * FROM mysql.user") .on("error", err => {}) .on("fields", meta => {}) .on("data", row => {}) .on("end", () => {});
  • 44. Streaming resultset USING PIPE : const someWriterStream = fs.createWriteStream("./jsonUsers.txt"); const transformStream = new stream.Transform({ objectMode: true, transform: function transformer(chunk, encoding, callback) { callback(null, JSON.stringify(chunk)); } }); const queryStream = connection.queryStream("SELECT * FROM mysql.user"); stream.pipeline(queryStream, transformStream, someWriterStream);
  • 45. Streaming - sending const https = require("https"); https.get( "https://node.green/#ES2018-features-Promise-prototype-finally-basic-support", //3Mb page readableStream => { connection .query("INSERT INTO StreamingContent (b) VALUE (?)", [readableStream]) .then(res => {}) .catch(err => {}); }); Limitations : ● Server net_read_timeout : SET STATEMENT net_read_timeout=10000 FOR XXX ○ For a connection: sessionVariables: { net_read_timeout: 10000 } ● max_allowed_packet
  • 47. BATCH INSERT conn.batch("INSERT INTO myTable(firstName, lastName) VALUES (?, ?)", [ ["john", "smith"], ["jack", "brown"] ]); }) .then(res => { }) .catch(err => { }); Benchmark : 100 * insert 100 characters on local DB
  • 49. ORM $ npm install --save express body-parser [email protected] mariadb const Sequelize = require('sequelize'); const sequelize = new Sequelize('testn', 'root', null, { host: 'localhost', dialect: 'mariadb', pool: { max: 5, min: 5, acquire: 30000, idle: 10000 } });
  • 50. ORM const User = sequelize.define('myUser', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, firstName: { type: Sequelize.STRING }, lastName: { type: Sequelize.STRING } }); User.sync({ force: true }) .then(() => { return User.create({ firstName: 'John', lastName: 'Hancock' }); });
  • 51. ORM const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); const port = 3000; app.listen(port, () => { console.log(`Running on http://localhost:${port}`) }); // get all users app.get('/api/users', (req, res) => { User.findAll().then(users => res.json(users)) });
  • 53. DEBUGGING Connection options ‘trace’ default to false, nice to have in development mode. { Error: (conn=149, no: 1146, SQLState: 42S02) Table 'testn.unknownTable' doesn't exist sql: SELECT * FROM unknownTable - parameters:[] at Object.module.exports.createError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/misc/errors.js:55:10) at Packet.readError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet.js:506:19) at Query.readResponsePacket (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/cmd/resultset.js:47:28) at PacketInputStream.receivePacket (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet-input-stream.js:73:9) at PacketInputStream.onData (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/io/packet-input-stream.js:129:20) at Socket.emit (events.js:197:13) at addChunk (_stream_readable.js:288:12) at readableAddChunk (_stream_readable.js:269:11) at Socket.Readable.push (_stream_readable.js:224:10) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17) fatal: false, errno: 1146, sqlState: '42S02', code: 'ER_NO_SUCH_TABLE' }
  • 54. DEBUGGING With trace { Error: (conn=150, no: 1146, SQLState: 42S02) Table 'testn.unknownTable' doesn't exist sql: SELECT * FROM unknownTable - parameters:[] at Object.module.exports.createError (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/misc/errors.js:55:10) ... at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:145:17) From event: at /home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/connection.js:166:29 at new Promise (<anonymous>) at Connection.query (/home/diego/IdeaProjects/test_node/node_modules/mariadb/lib/connection.js:164:12) at mariadb.createConnection.then.conn (/home/diego/IdeaProjects/test_node/lib/promise/trace.js:13:8) at processTicksAndRejections (internal/process/next_tick.js:81:5) fatal: false, errno: 1146, sqlState: '42S02', code: 'ER_NO_SUCH_TABLE' }
  • 55. AVOID TCP-IP layer for local connection Connection options ‘socketPath‘ for local server - UNIX domain - Windows named pipe const mariadb = require('mariadb'); mariadb.createConnection({ socketPath: '/tmp/mysql.sock', user: 'root' }) .then(conn => { ... }) .catch(err => { ... });
  • 56. METADATA ● Select query = row datas + metadata Metadata = datatype, format, and lots of additional infos: schema, table name, table alias, column name, column alias, … Not always needed conn.query("select * from mysql.user u LIMIT 1") .then(rows => { console.log(rows); // [ // { Host: 'localhost', User: 'root', … } // ] }); conn .query({ sql: "select * from mysql.user u LIMIT 1", rowsAsArray: true }) .then(rows => { console.log(rows); // [ // [ 'localhost', 'root', …] // ] });
  • 57. What next ? Authentication plugins (Kerberos, ed25519) Performance improvement Failover enhancement Pools improvement