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