diff --git a/benchmark/sinochain/config-local-duration.json b/benchmark/sinochain/config-local-duration.json new file mode 100644 index 000000000..b4776e789 --- /dev/null +++ b/benchmark/sinochain/config-local-duration.json @@ -0,0 +1,45 @@ +{ + "blockchain": { + "type": "sinochain", + "config": "benchmark/sinochain/sinochain.json" + }, + "test": { + "name": "sinochain test", + "description": "Sinochain test!", + "clients": { + "type": "local", + "unfinished": 30000, + "number": 2 + }, + "rounds": [ + { + "label": "open-account", + "txDuration": [ + 180 + ], + "rateControl": [ + { + "type": "fixed-rate", + "opts": { + "tps": 5000 + } + } + ], + "callback": "benchmark/sinochain/open.js" + } + ] + }, + "monitor": { + "type": [ + + ], + "process": [ + { + "command": "node", + "arguments": "local-client.js", + "multiOutput": "avg" + } + ], + "interval": 10 + } + } \ No newline at end of file diff --git a/benchmark/sinochain/config-local.json b/benchmark/sinochain/config-local.json new file mode 100644 index 000000000..71a97a1fe --- /dev/null +++ b/benchmark/sinochain/config-local.json @@ -0,0 +1,45 @@ +{ + "blockchain": { + "type": "sinochain", + "config": "benchmark/sinochain/sinochain.json" + }, + "test": { + "name": "sinochain test", + "description": "Sinochain test!", + "clients": { + "type": "local", + "unfinished": 30000, + "number": 2 + }, + "rounds": [ + { + "label": "open-account", + "txNumber": [ + 100000 + ], + "rateControl": [ + { + "type": "fixed-rate", + "opts": { + "tps": 10000 + } + } + ], + "callback": "benchmark/sinochain/open.js" + } + ] + }, + "monitor": { + "type": [ + + ], + "process": [ + { + "command": "node", + "arguments": "main.js", + "multiOutput": "avg" + } + ], + "interval": 1 + } + } \ No newline at end of file diff --git a/benchmark/sinochain/config-duration.json b/benchmark/sinochain/config-zoo-duration.json similarity index 83% rename from benchmark/sinochain/config-duration.json rename to benchmark/sinochain/config-zoo-duration.json index c4ab0548c..1c9bf2841 100644 --- a/benchmark/sinochain/config-duration.json +++ b/benchmark/sinochain/config-zoo-duration.json @@ -8,6 +8,7 @@ "description": "Sinochain test!", "clients": { "type": "zookeeper", + "unfinished": 30000, "zoo" : { "server": "127.0.0.1:12181", "clientsPerHost": 2 @@ -15,19 +16,19 @@ }, "rounds": [ { - "label": "create-account", + "label": "open-account", "txDuration": [ - 60 + 180 ], "rateControl": [ { "type": "fixed-rate", "opts": { - "tps": 100 + "tps": 5000 } } ], - "callback": "benchmark/sinochain/create.js" + "callback": "benchmark/sinochain/open.js" } ] }, diff --git a/benchmark/sinochain/config.json b/benchmark/sinochain/config-zoo.json similarity index 83% rename from benchmark/sinochain/config.json rename to benchmark/sinochain/config-zoo.json index 6ec3dbd09..080d5a598 100644 --- a/benchmark/sinochain/config.json +++ b/benchmark/sinochain/config-zoo.json @@ -8,6 +8,7 @@ "description": "Sinochain test!", "clients": { "type": "zookeeper", + "unfinished": 30000, "zoo" : { "server": "127.0.0.1:12181", "clientsPerHost": 2 @@ -15,19 +16,19 @@ }, "rounds": [ { - "label": "create-account", + "label": "open-account", "txNumber": [ - 100 + 100000 ], "rateControl": [ { "type": "fixed-rate", "opts": { - "tps": 10 + "tps": 10000 } } ], - "callback": "benchmark/sinochain/create.js" + "callback": "benchmark/sinochain/open.js" } ] }, diff --git a/benchmark/sinochain/create.js b/benchmark/sinochain/create.js deleted file mode 100644 index be4aac490..000000000 --- a/benchmark/sinochain/create.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -module.exports.info = 'create account'; - -let bc, ctx; - -let seqnum; - -module.exports.init = function(blockchain, context, args) { - - bc = blockchain; - ctx = context; - let startIndex = args.txNum * args.clientIndex; - // let endIndex = args.txNum * (args.clientIndex + 1); - seqnum = startIndex; - - return Promise.resolve(); -}; - -module.exports.run = function() { - return bc.bcObj.sinoSendToCloud(ctx, { - Seqnum: (++seqnum), - Payload: new Buffer('{"name": "mycc", "args": ["create", "' + seqnum + '", "10"]}') - }); -}; - -module.exports.end = function() { - return Promise.resolve(); -}; \ No newline at end of file diff --git a/benchmark/sinochain/main.js b/benchmark/sinochain/main.js index ed4858ba7..8ef2e1628 100644 --- a/benchmark/sinochain/main.js +++ b/benchmark/sinochain/main.js @@ -43,7 +43,7 @@ function main() { const fs = require('fs-extra'); let absConfigFile; if(typeof configFile === 'undefined') { - absConfigFile = path.join(__dirname, 'config.json'); + absConfigFile = path.join(__dirname, 'config-local.json'); } else { absConfigFile = path.join(__dirname, configFile); diff --git a/benchmark/sinochain/open.js b/benchmark/sinochain/open.js new file mode 100644 index 000000000..0f15e40ab --- /dev/null +++ b/benchmark/sinochain/open.js @@ -0,0 +1,65 @@ +'use strict'; + +module.exports.info = 'open account'; + +let bc, ctx; + +let seqnum; + +module.exports.init = function(blockchain, context, args) { + + bc = blockchain; + ctx = context; + ctx.txNum = args.txNum; + let startIndex = args.txNum * args.clientIndex; + // let endIndex = args.txNum * (args.clientIndex + 1); + seqnum = startIndex; + + return Promise.resolve(); +}; + +/* module.exports.run = function() { + return bc.bcObj.sinoSendToCloud(ctx, { + Seqnum: (++seqnum), + Payload: new Buffer('{"name": "simple", "args": ["open", "' + seqnum + '", "10"]}') + }); +}; */ + +const dic = 'abcdefghijklmnopqrstuvwxyz'; +/** + * Generate string by picking characters from dic variable + * @param {*} number character to select + * @returns {String} string generated based on @param number + */ +function get26Num(number){ + let result = ''; + while(number > 0) { + result += dic.charAt(number % 26); + number = parseInt(number/26); + } + return result; +} + +let prefix; +/** + * Generate unique account key for the transaction + * @returns {String} account key + */ +function generateAccount() { + // should be [a-z]{1,9} + if(typeof prefix === 'undefined') { + prefix = get26Num(process.pid); + } + return prefix + get26Num(seqnum + 1); +} + +module.exports.run = function() { + return bc.bcObj.sinoSendToCloud(ctx, { + Seqnum: (++seqnum), + Payload: new Buffer('{"name": "simple", "args": ["open", "' + generateAccount() + '", "10"]}') + }); +}; + +module.exports.end = function() { + return Promise.resolve(); +}; \ No newline at end of file diff --git a/benchmark/sinochain/sinochain.json b/benchmark/sinochain/sinochain.json index 363984016..132a79a89 100644 --- a/benchmark/sinochain/sinochain.json +++ b/benchmark/sinochain/sinochain.json @@ -2,7 +2,7 @@ "sinochain": { "cloud": { "servers": [ - "127.0.0.1:8080" + "127.0.0.1:5050" ] }, "channel": "mychannel" diff --git a/src/comm/blockchain-interface.js b/src/comm/blockchain-interface.js index 567cefeaa..9b3633488 100644 --- a/src/comm/blockchain-interface.js +++ b/src/comm/blockchain-interface.js @@ -124,6 +124,13 @@ class BlockchainInterface { sinoIsConnectionOpen() { throw new Error('sinoIsConnectionOpen is not implemented for this blockchain system'); } + + /** + * wait for recived + */ + sinoWaitRecv() { + throw new Error('sinoWaitRecv is not implemented for this blockchain system'); + } } module.exports = BlockchainInterface; \ No newline at end of file diff --git a/src/comm/blockchain.js b/src/comm/blockchain.js index b6019f84b..49c8dab19 100644 --- a/src/comm/blockchain.js +++ b/src/comm/blockchain.js @@ -297,6 +297,7 @@ class Blockchain { * send data to cloud * @param {Object} context context object * @param {Object} payload data send to sinochain cloud + * @returns {Promise} promiseObject */ sinoSendToCloud(context, payload) { return this.bcObj.sinoSendToCloud(context, payload); @@ -311,10 +312,19 @@ class Blockchain { /** * whether the cloud connection is opened + * @returns {boolean} the cloud connection is opened */ sinoIsConnectionOpen() { return this.bcObj.sinoIsConnectionOpen(); } + + /** + * wait for recived + * @returns {Promise} promiseObject + */ + sinoWaitRecv() { + return this.bcObj.sinoWaitRecv(); + } } module.exports = Blockchain; \ No newline at end of file diff --git a/src/comm/client/client.js b/src/comm/client/client.js index e09bb7740..7631ec28d 100644 --- a/src/comm/client/client.js +++ b/src/comm/client/client.js @@ -141,6 +141,11 @@ class Client{ this.results = []; this.updates.data = []; this.updates.id++; + // sinochain: add unfinished control + message.unfinished = -1; + if (this.config.unfinished && this.config.unfinished > 0) { + message.unfinished = this.config.unfinished; + } switch(this.type) { case CLIENT_LOCAL: p = this._startLocalTest(message, clientArgs); diff --git a/src/comm/client/local-client.js b/src/comm/client/local-client.js index f6d54e081..b5e0ebb57 100644 --- a/src/comm/client/local-client.js +++ b/src/comm/client/local-client.js @@ -22,6 +22,7 @@ let txUpdateTime = 1000; let trimType = 0; let trim = 0; let startTime = 0; +let resultNum = 0; /** * Calculate realtime transaction statistics and send the txUpdated message @@ -82,9 +83,11 @@ function addResult(result) { for(let i = 0 ; i < result.length ; i++) { results.push(result[i]); } + resultNum += result.length; } else { results.push(result); + resultNum += 1; } } @@ -146,15 +149,30 @@ async function runFixedNumber(msg, cb, context) { startTime = Date.now(); let promises = []; - while(txNum < msg.numb) { - promises.push(cb.run().then((result) => { - addResult(result); - return Promise.resolve(); - })); - await rateControl.applyRateControl(startTime, txNum, results); + while (txNum < msg.numb) { + if (blockchain.gettype() === 'sinochain') { + cb.run().then((result) => { + return Promise.resolve(); // sinochain: use event add result + }); + } else { + promises.push(cb.run().then((result) => { + addResult(result); + return Promise.resolve(); + })); + } + + if (msg.unfinished > 0 && txNum - resultNum > msg.unfinished) { + await Util.sleep(5000); + } else { + await rateControl.applyRateControl(startTime, txNum, results); + } } - await Promise.all(promises); + if (blockchain.gettype() === 'sinochain') { + await blockchain.sinoWaitRecv(); + } else { + await Promise.all(promises); + } await rateControl.end(); return await blockchain.releaseContext(context); } @@ -187,15 +205,30 @@ async function runDuration(msg, cb, context) { startTime = Date.now(); let promises = []; + // let submitResults = []; while ((Date.now() - startTime)/1000 < duration) { - promises.push(cb.run().then((result) => { - addResult(result); - return Promise.resolve(); - })); - await rateControl.applyRateControl(startTime, txNum, results); - } + if (blockchain.gettype() === 'sinochain') { + cb.run().then((result) => { + return Promise.resolve(); // sinochain: use event add result + }); + } else { + promises.push(cb.run().then((result) => { + addResult(result); + return Promise.resolve(); + })); + } - await Promise.all(promises); + if (msg.unfinished > 0 && txNum - resultNum > msg.unfinished) { + await Util.sleep(5000); + } else { + await rateControl.applyRateControl(startTime, txNum, results); + } + } + if (blockchain.gettype() === 'sinochain') { + await blockchain.sinoWaitRecv(); + } else { + await Promise.all(promises); + } await rateControl.end(); return await blockchain.releaseContext(context); } @@ -229,13 +262,15 @@ function doTest(msg) { if(typeof context === 'undefined') { context = { engine : { - submitCallback : submitCallback + submitCallback : submitCallback, + addResult : addResult } }; } else { context.engine = { - submitCallback : submitCallback + submitCallback : submitCallback, + addResult : addResult }; } if (msg.txDuration) { diff --git a/src/gui/src/demo.js b/src/gui/src/demo.js index 89c1aa7ec..639c22d29 100644 --- a/src/gui/src/demo.js +++ b/src/gui/src/demo.js @@ -141,6 +141,8 @@ function update() { timelength++; if (typeof client === 'undefined') { demoRefreshData([]); + // sinochain: use setTimeout instead + demoInterObj = setTimeout(update, demoInterval * 1000); return; } var updates = client.getUpdates(); @@ -155,6 +157,8 @@ function update() { updateTail = len; } demoRefreshData(data); + // sinochain: use setTimeout instead + demoInterObj = setTimeout(update, demoInterval * 1000); } function demoStartWatch(clientObj) { //demoProcesses = processes.slice(); @@ -165,7 +169,9 @@ function demoStartWatch(clientObj) { updateTail = 0; updateID = 0; // start a interval to query updates - demoInterObj = setInterval(update, demoInterval * 1000); + // demoInterObj = setInterval(update, demoInterval * 1000); + // sinochain: use setTimeout instead + demoInterObj = setTimeout(update, demoInterval * 1000); } } module.exports.startWatch = demoStartWatch; @@ -180,7 +186,9 @@ module.exports.pauseWatch = demoPauseWatch; function demoStopWatch(output) { if(demoInterObj) { - clearInterval(demoInterObj); + // sinochain: use setTimeout instead + // clearInterval(demoInterObj); + clearTimeout(demoInterObj); demoInterObj = null; } demoData.report = output; diff --git a/src/sinochain/sinochain.js b/src/sinochain/sinochain.js index a40887258..2658fd2ac 100644 --- a/src/sinochain/sinochain.js +++ b/src/sinochain/sinochain.js @@ -23,23 +23,45 @@ class Sinochain extends BlockchainInterface { this.channel = this.config.sinochain.channel; this.client = new common_proto.CloudBD(this.getRandomServer(), grpc.credentials.createInsecure()); this.deliver = this.client.deliver(); - + this.context = null; this.connected = true; this.results = {}; - + this.lastTimeout = null; + this.receivedEnd = false; + this.receivedCount = 0; + this.lastDataTime = null; let that = this; this.deliver.on('data', env => { + if (that.lastTimeout !== null) { + clearTimeout(that.lastTimeout); + } + that.receivedCount++; let invokeStatus = that.results[env.seqnum]; - if (invokeStatus !== null) { + if (invokeStatus) { if (env.status === 'SUCCESS') { invokeStatus.SetStatusSuccess(); //invokeStatus.SetFinalTime(new Date()); invokeStatus.SetFinalTime(env.endTime); } else { invokeStatus.SetStatusFail(); + invokeStatus.SetFinalTime(env.endTime); + } + // that.results[env.seqnum] = invokeStatus; + if (that.context && that.context.engine) { + that.context.engine.addResult(invokeStatus); + } + that.lastDataTime = env.endTime; + delete this.results[env.seqnum]; + } + if (that.context && that.context.txNum) { + if (that.receivedCount === that.context.txNum) { + that.setToFail(that); + return; } - that.results[env.seqnum] = invokeStatus; } + that.lastTimeout = setTimeout(function() { + that.setToFail(that); + }, 10000); }).on('end', function() { that.sinoDisconnectFromCloud(); }).on('error', err => { @@ -47,6 +69,24 @@ class Sinochain extends BlockchainInterface { }); } + /** + * set all not back to fail + * @param {Object} that this object + */ + setToFail(that) { + for (let seqnum in that.results) { + let invokeStatus = that.results[seqnum]; + if (invokeStatus) { + invokeStatus.SetStatusFail(); + invokeStatus.SetFinalTime(that.lastDataTime); + if (that.context && that.context.engine) { + that.context.engine.addResult(invokeStatus); + } + } + } + that.receivedEnd = true; + } + /** * sinochain no need init * @returns {Promise} The return promise @@ -90,7 +130,7 @@ class Sinochain extends BlockchainInterface { * @param {integer} checkTimes the result check times * @returns {Object} the result wapper */ - waitForInvokeBack(seqnum, checkTimes) { + /* waitForInvokeBack(seqnum, checkTimes) { return commUtil.sleep(1000).then(() => { if (this.results[seqnum]) { if (this.results[seqnum].Get('status') !== 'submitted') { @@ -111,7 +151,7 @@ class Sinochain extends BlockchainInterface { return invokeStatus; } }); - } + } */ /** * send data to cloud @@ -120,8 +160,8 @@ class Sinochain extends BlockchainInterface { * @returns {Object} the invoke result */ sinoSendToCloud(context, payload) { - if (context.engine) { - context.engine.submitCallback(1); + if (this.context === null) { + this.context = context; } let env = new common_proto.Envelope(); env.seqnum = payload.Seqnum; @@ -131,14 +171,20 @@ class Sinochain extends BlockchainInterface { invokeStatus.Set('status', 'submitted'); if (this.sinoIsConnectionOpen()) { this.deliver.write(env); + if (context.engine) { + context.engine.submitCallback(1); + } invokeStatus.Set('status', 'submitted'); this.results[env.seqnum] = invokeStatus; - invokeStatus = this.waitForInvokeBack(env.seqnum, 1); + // invokeStatus = this.waitForInvokeBack(env.seqnum, 1); return Promise.resolve(invokeStatus); } else { //invokeStatus.SetFlag(); invokeStatus.SetErrMsg('Connection is closed.'); invokeStatus.SetStatusFail(); + if (context.engine) { + context.engine.addResult(invokeStatus); + } return Promise.resolve(invokeStatus); } } @@ -160,6 +206,24 @@ class Sinochain extends BlockchainInterface { } } + /** + * wait for sinochain cloud data + * @returns {Promise} the Promise + */ + async sinoWaitRecv() { + while(!this.receivedEnd) { + await commUtil.sleep(1000); + } + return Promise.resolve(); + /* if (this.receivedEnd) { + return Promise.resolve(); + } + return commUtil.sleep(1000).then(() => { + // commUtil.log('wait for received end...'); + return this.sinoWaitRecv(); + }); */ + } + /** * return a random cloud server address, for request load balance * @returns {string} sinochain cloud server address