perf.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2015 The MemDB Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. // implied. See the License for the specific language governing
  13. // permissions and limitations under the License. See the AUTHORS file
  14. // for names of contributors.
  15. 'use strict';
  16. var P = require('bluebird');
  17. var _ = require('lodash');
  18. var util = require('util');
  19. var utils = require('../app/utils');
  20. var should = require('should');
  21. var env = require('./env');
  22. var memdb = require('../lib');
  23. var Database = require('../app/database');
  24. var AutoConnection = require('../lib/autoconnection');
  25. var logger = require('memdb-logger').getLogger('test', __filename);
  26. describe.skip('performance test', function(){
  27. beforeEach(env.flushdb);
  28. after(env.flushdb);
  29. it('standard', function(cb){
  30. this.timeout(3600 * 1000);
  31. var runTest = function(opts){
  32. opts = opts || {};
  33. var shardCount = opts.shardCount || 1;
  34. var playerCount = opts.playerCount || 100;
  35. var areaCount = Math.floor(opts.areaCount || (playerCount / 10));
  36. var transOps = opts.transOps || 5;
  37. var useIndex = opts.useIndex || false;
  38. var randomRoute = opts.randomRoute || false;
  39. var transCount = Math.floor(opts.transCount || (50000 / playerCount / transOps));
  40. if(transCount < 1){
  41. transCount = 1;
  42. }
  43. var shardIds = Object.keys(env.config.shards).slice(0, shardCount);
  44. var autoconn = null;
  45. var queryThread = function(threadId){
  46. var Dummy = useIndex ? autoconn.collection('player') : autoconn.collection('dummy');
  47. return P.each(_.range(transCount), function(){
  48. var shardId = randomRoute ? _.sample(shardIds) : shardIds[threadId % shardIds.length];
  49. //Make sure same areaId route to same shard
  50. var areaId = randomRoute ? _.random(areaCount) : Math.floor(_.random(areaCount) / shardIds.length) * shardIds.length + threadId % shardIds.length;
  51. var makeQuery = function(id){
  52. var modifier = useIndex ? {$set : {areaId : areaId}} : {$inc : {exp : 1}};
  53. return Dummy.update(id, modifier, {upsert : true});
  54. };
  55. return autoconn.transaction(function(){
  56. if(transOps === 1){
  57. return makeQuery(threadId);
  58. }
  59. else{
  60. return P.each(_.range(transOps), function(id){
  61. return makeQuery(threadId);
  62. //return makeQuery((threadId % 1000) * 2 + id % 2);
  63. //.delay(5);
  64. });
  65. }
  66. }, shardId)
  67. //.delay(_.random(6000))
  68. .catch(function(e){
  69. logger.error(e.stack);
  70. });
  71. });
  72. };
  73. return P.try(function(){
  74. env.startCluster(shardIds);
  75. })
  76. .then(function(){
  77. var config = {
  78. shards : env.config.shards,
  79. maxConnection : 64,
  80. maxPendingTask : 4096,
  81. };
  82. return memdb.autoConnect(config)
  83. .then(function(ret){
  84. autoconn = ret;
  85. });
  86. })
  87. .then(function(){
  88. var startTick = Date.now();
  89. return P.map(_.range(playerCount), queryThread)
  90. .then(function(){
  91. var baseRate = playerCount * 1000 / (Date.now() - startTick);
  92. logger.warn(opts.description);
  93. logger.warn('ShardCount: %s, PlayerCount: %s, ops/trans: %s', shardCount, playerCount, transOps);
  94. logger.warn('ops: %s', baseRate * transOps * transCount);
  95. logger.warn('tps: %s', baseRate * transCount);
  96. });
  97. })
  98. .then(function(){
  99. return autoconn.info('s1')
  100. .then(function(info){
  101. logger.warn(util.inspect(info));
  102. });
  103. })
  104. .then(function(){
  105. return autoconn.close();
  106. })
  107. .delay(1000)
  108. .finally(function(){
  109. return env.stopCluster();
  110. });
  111. };
  112. var testOpts = [
  113. {
  114. description : 'operations (1 shard)',
  115. shardCount : 1,
  116. playerCount : 100,
  117. transOps : 1000,
  118. transCount : 1,
  119. },
  120. // {
  121. // description : 'transactions(1op) (1 shard)',
  122. // shardCount : 1,
  123. // playerCount : 100,
  124. // transOps : 1,
  125. // transCount : 1000,
  126. // },
  127. // {
  128. // description : 'transactions(10ops) (1 shard)',
  129. // shardCount : 1,
  130. // playerCount : 100,
  131. // transOps : 10,
  132. // transCount : 100,
  133. // },
  134. // {
  135. // description : 'operations with index (1 shard)',
  136. // shardCount : 1,
  137. // playerCount : 100,
  138. // transOps : 1000,
  139. // useIndex : true,
  140. // transCount : 1,
  141. // },
  142. // {
  143. // description : 'transactions(1op) with index (1 shard)',
  144. // shardCount : 1,
  145. // playerCount : 100,
  146. // transOps : 1,
  147. // transCount : 1000,
  148. // useIndex : true,
  149. // },
  150. // {
  151. // //bottle neck is client side
  152. // description : 'transactions(1op) (2 shards)',
  153. // shardCount : 2,
  154. // playerCount : 100,
  155. // transOps : 1,
  156. // transCount : 1000,
  157. // },
  158. // {
  159. // description : 'transactions(1op) (2 shards random route)',
  160. // shardCount : 2,
  161. // playerCount : 100,
  162. // transOps : 1,
  163. // transCount : 100,
  164. // randomRoute : true,
  165. // },
  166. // {
  167. // //bottle neck is client side
  168. // description : 'operations (2 shards)',
  169. // shardCount : 2,
  170. // playerCount : 100,
  171. // transOps : 1000,
  172. // transCount : 1,
  173. // },
  174. // {
  175. // description : 'transaction(1op) with index (2 shards)',
  176. // shardCount : 2,
  177. // playerCount : 100,
  178. // transOps : 1,
  179. // transCount : 1000,
  180. // useIndex : true,
  181. // },
  182. ];
  183. return P.each(testOpts, function(testOpt){
  184. return P.try(function(){
  185. return P.promisify(env.flushdb);
  186. })
  187. .then(function(){
  188. return runTest(testOpt);
  189. })
  190. .then(function(){
  191. return P.promisify(env.flushdb);
  192. });
  193. })
  194. .nodeify(cb);
  195. });
  196. it('load/unload', function(cb){
  197. this.timeout(300 * 1000);
  198. var count = 20000;
  199. var concurrency = 100;
  200. var autoconn = null;
  201. return P.try(function(){
  202. return env.startCluster('s1', function(config){
  203. config.persistentDelay = 3600 * 1000;
  204. config.idleTimeout = 3600 * 1000;
  205. });
  206. })
  207. .then(function(){
  208. return memdb.autoConnect(env.config)
  209. .then(function(ret){
  210. autoconn = ret;
  211. });
  212. })
  213. .then(function(){
  214. var startTick = Date.now();
  215. var Player = autoconn.collection('player');
  216. return P.map(_.range(concurrency), function(groupId){
  217. var groupCount = Math.floor(count/concurrency);
  218. return autoconn.transaction(function(){
  219. return P.each(_.range(groupCount), function(index){
  220. var doc = {_id : groupCount * groupId + index, name : 'rain'};
  221. return Player.insert(doc);
  222. });
  223. }, 's1');
  224. })
  225. .then(function(){
  226. var rate = count * 1000 / (Date.now() - startTick);
  227. logger.warn('Load Rate: %s', rate);
  228. });
  229. })
  230. .then(function(){
  231. var startTick = Date.now();
  232. var Player = autoconn.collection('player');
  233. return P.map(_.range(concurrency), function(groupId){
  234. var groupCount = Math.floor(count/concurrency);
  235. return autoconn.transaction(function(){
  236. return P.each(_.range(groupCount), function(index){
  237. return Player.update({_id : groupCount * groupId + index}, {$set : {name : 'snow'}}, {upsert : true});
  238. });
  239. }, 's1');
  240. })
  241. .then(function(){
  242. var rate = count * 1000 / (Date.now() - startTick);
  243. logger.warn('Update Rate: %s', rate);
  244. });
  245. })
  246. .finally(function(){
  247. var startTick = Date.now();
  248. return env.stopCluster()
  249. .then(function(){
  250. var rate = count * 1000 / (Date.now() - startTick);
  251. logger.warn('Unload Rate: %s', rate);
  252. });
  253. })
  254. .nodeify(cb);
  255. });
  256. it('mdbgoose', function(cb){
  257. this.timeout(300 * 1000);
  258. var queryCount = 200, concurrency = 100;
  259. var mdbgoose = memdb.goose;
  260. delete mdbgoose.connection.models.player;
  261. var Player = mdbgoose.model('player', new mdbgoose.Schema({
  262. _id : String,
  263. exp : Number,
  264. }, {collection : 'player'}));
  265. return P.try(function(){
  266. return env.startCluster('s1');
  267. })
  268. .then(function(){
  269. return mdbgoose.connectAsync(env.config);
  270. })
  271. .then(function(){
  272. var startTick = Date.now();
  273. return P.map(_.range(concurrency), function(playerId){
  274. return mdbgoose.transaction(function(){
  275. var player = new Player({_id : playerId, exp : 0});
  276. return P.each(_.range(queryCount), function(){
  277. player.exp++;
  278. return player.saveAsync();
  279. });
  280. }, 's1');
  281. })
  282. .then(function(){
  283. var rate = queryCount * concurrency * 1000 / (Date.now() - startTick);
  284. logger.warn('Rate: %s', rate);
  285. });
  286. })
  287. .then(function(){
  288. return mdbgoose.disconnectAsync();
  289. })
  290. .finally(function(){
  291. return env.stopCluster();
  292. })
  293. .nodeify(cb);
  294. });
  295. it.skip('gc & idle', function(cb){
  296. this.timeout(10000 * 1000);
  297. return P.try(function(){
  298. return env.startCluster('s1', function(config){
  299. config.memoryLimit = 1024; // 1G
  300. // Set large value to trigger gc, small value to not trigger gc
  301. config.idleTimeout = 10000 * 1000;
  302. config.persistentDelay = 10000 * 1000;
  303. });
  304. })
  305. .then(function(){
  306. return memdb.autoConnect(env.config);
  307. })
  308. .then(function(autoconn){
  309. var concurrency = 100;
  310. var count = 1000000;
  311. return P.map(_.range(concurrency), function(thread){
  312. return P.each(_.range(Math.floor(count / concurrency)), function(i){
  313. i = i * concurrency + thread;
  314. var doc = {_id : i};
  315. for(var j=0; j<10; j++){
  316. doc['key' + j] = 'value' + j;
  317. }
  318. if(i % 100 === 0) {
  319. logger.warn(i);
  320. }
  321. return autoconn.transaction(function(){
  322. return autoconn.collection('player').insert(doc);
  323. }, 's1');
  324. });
  325. });
  326. })
  327. .then(function(){
  328. logger.warn('%j', process.memoryUsage());
  329. })
  330. .finally(function(){
  331. return env.stopCluster();
  332. })
  333. .nodeify(cb);
  334. });
  335. it('logger', function(){
  336. var startTick = Date.now();
  337. var count = 100000;
  338. for(var i=0; i<count; i++){
  339. logger.warn('1');
  340. }
  341. var rate = count * 1000 / (Date.now() - startTick);
  342. console.log('rate %s', rate);
  343. });
  344. });