UNPKG

5.28 kBJavaScriptView Raw
1'use strict';
2// This script can be used to stress the ability of loki to save large databases.
3// I have found that within most javascript engines there seems to be memory
4// contraints and inefficiencies involved with using JSON.stringify.
5//
6// One way to limit memory overhead is to serialize smaller objects rather than
7// one large (single) JSON.stringify of the whole database.
8//
9// The LokiFsStructuredAdapter streams the database in an out as a series
10// of smaller, individual serializations. It also partitions database into
11//
12// The native node fs adapter is hands down the most memory efficient and
13// fastest adapter if you are using node.js. It accomplishes this by
14// using io streams to save and load the database to disk rather than saving
15// the whole database as a single string.
16//
17// This stress can be used to analyse memory overhead for saving a loki database.
18// Both stress.js and destress.js need to be configured to use the same serialization
19// method and adapter. By default, this is configured to use the
20// loki-fs-structured-adapter which will stream output and input.
21
22// On my first review i saw no significant benefit to "destructured" format if you
23// are going to save in single file, but subsequent benchmarks show that saves
24// are actually faster. I wasn't expecting this and if i had to guess at why I would
25// guess that by not doing one huge JSON.stringify, but instead doing many smaller
26// ones, that this is faster than whatever string manipulation they do in a single
27// deep object scan. Since this serialization is done within db.saveDatabase()
28// it showed up on my disk io benchmark portion of this stress test.
29
30// The closer you get to running out of heap space, the less memory is left over
31// for io bufferring so saves are slower. A few hundred megs of free heap space
32// will keep db save io speeds from exponentially rising.
33
34var crypto = require("crypto"); // for random string generation
35var loki = require('../src/lokijs.js');
36var lfsa = require('../src/loki-fs-structured-adapter.js');
37
38// number of collections to create and populate
39var numCollections = 2;
40
41// number of documents to populate -each- collection with
42// if using 2 collections, will probably max @ 75000, structured adapter @ 310000
43var numObjects = 150000;
44
45// #
46// # Choose -one- method of serialization and make sure to match in destress.js
47// #
48
49//var mode = "fs-normal";
50//var mode = "fs-structured";
51//var mode = "fs-partitioned";
52var mode = "fs-structured-partitioned";
53
54var adapter;
55
56switch (mode) {
57 case "fs-normal":
58 case "fs-structured": adapter = new loki.LokiFsAdapter(); break;
59 case "fs-partitioned": adapter = new loki.LokiPartitioningAdapter(new loki.LokiFsAdapter()); break;
60 case "fs-structured-partitioned" : adapter = new lfsa(); break;
61 default:adapter = new loki.LokiFsAdapter(); break;
62};
63
64console.log(mode);
65
66var db = new loki('sandbox.db', {
67 adapter : adapter,
68 serializationMethod: (mode === "fs-structured")?"destructured":"normal"
69});
70
71// using less 'leaky' way to generate random strings
72// node specific
73function genRandomVal() {
74 return crypto.randomBytes(50).toString('hex');
75}
76
77function stepInsertObjects() {
78 var cidx, idx;
79
80 for (cidx=0; cidx < numCollections; cidx++) {
81 var items = db.addCollection('items' + cidx);
82
83 for(idx=0; idx<numObjects; idx++) {
84 items.insert({
85 start : (new Date()).getTime(),
86 first : genRandomVal(),
87 owner: genRandomVal(),
88 maker: genRandomVal(),
89 orders: [
90 genRandomVal(),
91 genRandomVal(),
92 genRandomVal(),
93 genRandomVal(),
94 genRandomVal()
95 ],
96 attribs: {
97 a: genRandomVal(),
98 b: genRandomVal(),
99 c: genRandomVal(),
100 d: {
101 d1: genRandomVal(),
102 d2: genRandomVal(),
103 d3: genRandomVal()
104 }
105 }
106 });
107 }
108
109 console.log('inserted ' + numObjects + ' documents');
110 }
111}
112
113function stepSaveDatabase() {
114 var start, end;
115
116 start = process.hrtime();
117
118 db.saveDatabase(function(err) {
119 if (err === null) {
120 console.log('finished saving database');
121 logMemoryUsage("after database save : ");
122 end = process.hrtime(start);
123 console.info("database save time : %ds %dms", end[0], end[1]/1000000);
124 }
125 else {
126 console.log('error encountered saving database : ' + err);
127 }
128 });
129}
130
131function step4ReloadDatabase() {
132 db = new loki('sandbox.db', {
133 verbose: true,
134 autoload: true,
135 autoloadCallback: dbLoaded
136 });
137}
138
139function formatBytes(bytes,decimals) {
140 if(bytes == 0) return '0 Byte';
141 var k = 1000; // or 1024 for binary
142 var dm = decimals + 1 || 3;
143 var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
144 var i = Math.floor(Math.log(bytes) / Math.log(k));
145 return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
146}
147
148function logMemoryUsage(msg) {
149 var pmu = process.memoryUsage();
150 console.log(msg + " > rss : " + formatBytes(pmu.rss) + " heapTotal : " + formatBytes(pmu.heapTotal) + " heapUsed : " + formatBytes(pmu.heapUsed));
151}
152
153logMemoryUsage("before document inserts : ");
154stepInsertObjects();
155
156logMemoryUsage("after document inserts : ");
157stepSaveDatabase();
158