Some information on performance of each of the back ends in Database abstraction with google apps script

The library reference is 

Mj61W-201_t_zC9fJg1IzYiz3TLx7pV4j

 

Update – I’ve added Driver MongoLab since these original tests were made. For a more detailed analysis than here, see Back end performance linearity

Comparing performance

In Relative performance I covered performance of scriptDB versus Parse versus DataStore.  I have since improved the delete batching performance of Parse.com, so now parse comes out best on all counts between the 3. Since that time I’ve added DriverMemory as a backend – naturally this is the fastest, but it’s interesting to look at them all side by side. Note that this performance test is fairly naive. It simply deletes everything, recreates it and queries it. This is good for those with good batching capabilities (parse) and bad for those without (orchestrate). It also shows very poor performance for writing to Fusion Tables. The batching capabilities of Fusion are seriously hampered by SQL string length constraints as described in Multiple inserts in Fusion Tables – which might be something that can be resolved, but in general Fusion is fine for querying and deleting, but seriously sucks for inserting ( i had to split into two to avoid execution time exceeded for 1000 records). I was also surprised by how good both drive and sheets are as a datastore, but they will degenerate with querying of big datasets, and in the case of sheets,  deletion of interspersed rows. I was completely frustrated with Datastore and quota problems on the free tier, so it all depends on what you are going to be using this for, and whether you are sharing the underlying database with other apps that access them in different ways. I am also sure that I can and will continue optimizing and adding new drivers as we go along. I’ve spent most time tweaking Parse.com, so bear that in mind when looking at these. 

The results

In this example I’m copying 1000 records to each of the backends. Where possible,  each of these drivers using batching. For the purposes of the test, I’ve switched caching off in all cases. There are 3 operations performed – deleting 1000 records, adding 1000 records and querying them. Some of them I ran a few times and averaged, where the run times were very short. It doesnt include Driver MongoLab which I’ve added since, and which outperforms all of these (except memory).Now when I limit to the top 5, and add mongolab, and throw in lots of complex queries, deletions and other playing around as described in Some test cases for various backends, here’s the performance, mongolab comes out the clear winner after doing it all in memory. Note that we are measuring my implementation of the drivers here – some may be better than others.

Original summary data

  remove save query
PARSE 12.1 12.4 2.0
DRIVE 8.0 2.8 1.0
MEMORY 0.5 0.6 0.5
SCRIPTDB 112.8 52.0 24.0
FUSION 3.5 387.1 13.5
ORCHESTRATE 103.0 95.0 4.6
SHEET 1.2 2.8 1.3
DATASTORE 17.6 20.7 4.1

Raw Data

at event what where code elapsed
1405947425068 start remove MEMORY    
1405947425530 end remove MEMORY 0 462
1405947426017 start save MEMORY    
1405947426695 end save MEMORY 0 678
1405947427272 start query MEMORY    
1405947427813 end query MEMORY 0 541
1405947446504 start remove MEMORY    
1405947446991 end remove MEMORY 0 487
1405947447444 start save MEMORY    
1405947448040 end save MEMORY 0 596
1405947448454 start query MEMORY    
1405947448987 end query MEMORY 0 533
1405947456497 start remove SHEET    
1405947457697 end remove SHEET 0 1200
1405947458107 start save SHEET    
1405947461129 end save SHEET 0 3022
1405947461549 start query SHEET    
1405947462873 end query SHEET 0 1324
1405947648192 start remove SHEET    
1405947649409 end remove SHEET 0 1217
1405947649982 start save SHEET    
1405947652565 end save SHEET 0 2583
1405947652978 start query SHEET    
1405947654347 end query SHEET 0 1369
1405947698376 start remove DRIVE    
1405947706875 end remove DRIVE 0 8499
1405947707343 start save DRIVE    
1405947709896 end save DRIVE 0 2553
1405947710341 start query DRIVE    
1405947711321 end query DRIVE 0 980
1405947726205 start remove DRIVE    
1405947733721 end remove DRIVE 0 7516
1405947734189 start save DRIVE    
1405947737152 end save DRIVE 0 2963
1405947737625 start query DRIVE    
1405947738576 end query DRIVE 0 951
1405948513593 start remove ORCHESTRATE    
1405948615322 end remove ORCHESTRATE 0 101729
1405948615989 start save ORCHESTRATE    
1405948710888 end save ORCHESTRATE 0 94899
1405948711421 start query ORCHESTRATE    
1405948716975 end query ORCHESTRATE 0 5554
1405948729670 start remove ORCHESTRATE    
1405948833999 end remove ORCHESTRATE 0 104329
1405948838583 start save ORCHESTRATE    
1405948933679 end save ORCHESTRATE 0 95096
1405948937852 start query ORCHESTRATE    
1405948941578 end query ORCHESTRATE 0 3726
1405948985877 start remove PARSE    
1405948997960 end remove PARSE 0 12083
1405949003186 start save PARSE    
1405949016045 end save PARSE 0 12859
1405949016604 start query PARSE    
1405949018543 end query PARSE 0 1939
1405949051646 start remove PARSE    
1405949063703 end remove PARSE 0 12057
1405949064567 start save PARSE    
1405949076489 end save PARSE 0 11922
1405949076980 start query PARSE    
1405949079096 end query PARSE 0 2116
1405949094111 start remove SCRIPTDB    
1405949206937 end remove SCRIPTDB 0 112826
1405949207460 start save SCRIPTDB    
1405949259461 end save SCRIPTDB 0 52001
1405949260008 start query SCRIPTDB    
1405949283965 end query SCRIPTDB 0 23957
1405949911928 start save FUSION    
1405950299013 end save FUSION 0 387085
1405950767326 start query FUSION    
1405950780836 end query FUSION 0 13510
1405951052239 start remove FUSION    
1405951055754 end remove FUSION 0 3515
1405953241366 start save DATASTORE    
1405953262652 end save DATASTORE 0 21286
1405953263926 start query DATASTORE    
1405953268121 end query DATASTORE 0 4195
1405953343972 start remove DATASTORE    
1405953361598 end remove DATASTORE 0 17626
1405953362236 start save DATASTORE    
1405953382381 end save DATASTORE 0 20145
1405953383608 start query DATASTORE    
1405953387704 end query DATASTORE 0 4096

Here’s the code

function getTheData() {    // get seed data from Drive  var driveHandler = new cDataHandler.DataHandler (     ‘seed.json’,                                  cDataHandler.dhConstants.DB.DRIVE,         undefined,      ‘/datahandler/driverdrive’);     assert(driveHandler.isHappy(), ‘unable to get driver handler’,’handler’);   var result = driveHandler.query ();  assert (result.handleCode >=0, result, ‘ getting seed data from drive ‘ + result.handleError);   return result.data; } function compareScriptDb() {  compareMethod (‘customers’,cDataHandler.dhConstants.DB.SCRIPTDB, ‘myddb’,ScriptDb.getMyDb()); } function compareDataStore() {  doGetPattern({} , constructConsentScreen, function (accessToken) {      compareMethod (‘customers’,cDataHandler.dhConstants.DB.DATASTORE, ‘xliberationdatastore’,undefined,accessToken);    } ,’googleDatastore’);  }function compareDrive() {  compareMethod (‘customers.json’,cDataHandler.dhConstants.DB.DRIVE, ‘/datahandler/driverdrive’);}function compareSheet() {  compareMethod (‘customers’,cDataHandler.dhConstants.DB.SHEET, ’13ccFPXI0L8-ZViHlv8qoVspotUcnX8v0ZFeY4nUP574′);}function compareFusion() {  compareMethod (‘1lOvQDzjosOzHnKlZP0xnwbTndbgxDEbSwUS5tseb’,cDataHandler.dhConstants.DB.FUSION, ‘customers’);}function compareMemory () {  compareMethod (‘customers’,cDataHandler.dhConstants.DB.MEMORY);} function compareParse () { var userStore = PropertiesService.getScriptProperties(); compareMethod (‘customers’,cDataHandler.dhConstants.DB.PARSE, ‘myparse’,JSON.parse(userStore.getProperty(“parseKeys”)));} function compareOrchestrate () { var userStore = PropertiesService.getScriptProperties(); compareMethod (‘customers’,cDataHandler.dhConstants.DB.ORCHESTRATE, ‘myorch’,JSON.parse(userStore.getProperty(“orchestrateKeys”)));} function getLogHandle() {  var logHandle =  new cDataHandler.DataHandler(‘comparison’,cDataHandler.dhConstants.DB.SHEET,undefined,’13ccFPXI0L8-ZViHlv8qoVspotUcnX8v0ZFeY4nUP574′);  assert(logHandle.isHappy(), ‘unable to get log handler’,’handler’);    return logHandle;}   function compareMethod ( silo , type , dbid, dbprop, accessToken ) {    var theData = getTheData();    // get a datastore handler  var theHandler = new cDataHandler.DataHandler (     silo,                              // Kind     type,    // Datastore     undefined,      dbid,                   // project id     dbprop,      undefined,     false,                                     // analytics opt out     ‘comparing’,                              // analytics debugging tracking      accessToken,                              // the access token     true);                                    // disable caching for testing    assert(theHandler.isHappy(), ‘unable to get compare handler’,’handler’);     // log it here  var logHandle = getLogHandle();        // copy it to   deleteAndCopy_ (theHandler, theData);    // delete whats there, insert and query – can be shared across handlers  function deleteAndCopy_ () {    // delete all current   record_ (‘remove’);        // save new   record_ (‘save’,theData);        // check   var result = record_ (‘query’);   assert (result.data.length === theData.length,result,’checked length’);   }    function record_ (what,ar) {    var then = new Date().getTime();    logHandle.save({at:then,event:’start’,what:what,where:theHandler.getDBName()});    var result = theHandler[what](ar);    var now = new Date().getTime();    logHandle.save({at:now,event:’end’,what:what,where:theHandler.getDBName(),code:result.handleCode,elapsed:now-then});    assert (result.handleCode >=0, result, what + ‘ data from ‘ +  theHandler.getDBName() );    return result;  }}

Summary

All round, I’d go for Driver MongoLab as a standalone GAS replacement for ScriptDB. See more like this in Database abstraction with google apps script and Back end performance linearity