Mailspring/spec/stores/database-setup-query-builder-spec.coffee
Ben Gotow b75ed6f920 perf(db): Use subselect to improve thread list query perf (53%!)
Summary:
- Use a sub-select query with much better performance to display the thread list
- Perform analyze on tables after launch

The new query is:

```
SELECT `Thread`.`data` FROM `Thread` WHERE `Thread`.`id` IN (SELECT `id` FROM `ThreadCategory` AS `M26` WHERE `M26`.`value` IN ('9m9ks71k06n5rmx82kgues09p','9s9k25q6j1krjgpkovbcjm7d','13b7ufruoymvih07ki0uahlto','dtmhlzz6phr47zp512knhjgf8','16dvjb84bszfh15kgfrjj37i3','aclwmgncdqjfibp51bvgbeik','17qad7jhbp6tozog3klm5zagt','4x4bkbawiq825u4eu3aus8tll','7axr9f5f1lzpwm2rw2ghkirhq','dsnn660af0pmou2gg3nstga8a','361qr5rva1ieby2r0ec3sn0bm','10fyvba7pjyjgeyr5i65i1zri') AND `M26`.`in_all_mail` = 1  ORDER BY `M26`.`last_message_received_timestamp` DESC LIMIT 200 OFFSET 0) ORDER BY `Thread`.`last_message_received_timestamp` DESC;
`
0|0|0|SEARCH TABLE Thread USING INDEX Thread_id (id=?)
0|0|0|EXECUTE LIST SUBQUERY 1
1|0|0|SCAN TABLE Thread-Category AS M26 USING COVERING INDEX ThreadFancyIndex
1|0|0|EXECUTE LIST SUBQUERY 2
0|0|0|USE TEMP B-TREE FOR (only on 200 result items)
```

Which is twice as performant as:
```
SELECT `Thread`.`data` FROM `Thread` INNER JOIN `ThreadCategory` AS `M26` ON `M26`.`id` = `Thread`.`id` WHERE `M26`.`value` IN ('9m9ks71k06n5rmx82kgues09p','9s9k25q6j1krjgpkovbcjm7d','13b7ufruoymvih07ki0uahlto','dtmhlzz6phr47zp512knhjgf8','16dvjb84bszfh15kgfrjj37i3','aclwmgncdqjfibp51bvgbeik','17qad7jhbp6tozog3klm5zagt','4x4bkbawiq825u4eu3aus8tll','7axr9f5f1lzpwm2rw2ghkirhq','361qr5rva1ieby2r0ec3sn0bm','10fyvba7pjyjgeyr5i65i1zri') AND `M26`.`in_all_mail` = 1  ORDER BY `M26`.`last_message_received_timestamp` DESC LIMIT 200 OFFSET 0;

0|0|1|SCAN TABLE Thread-Category AS M26 USING COVERING INDEX ThreadFancyIndex
0|0|0|EXECUTE LIST SUBQUERY 1
0|1|0|SEARCH TABLE Thread USING INDEX Thread_id (id=?)
```

Test Plan: Broken!

Reviewers: evan, juan

Reviewed By: juan

Differential Revision: https://phab.nylas.com/D2869
2016-04-11 13:29:05 -07:00

58 lines
3 KiB
CoffeeScript

TestModel = require '../fixtures/db-test-model'
Attributes = require '../../src/flux/attributes'
DatabaseSetupQueryBuilder = require '../../src/flux/stores/database-setup-query-builder'
describe "DatabaseSetupQueryBuilder", ->
beforeEach ->
@builder = new DatabaseSetupQueryBuilder()
describe "setupQueriesForTable", ->
it "should return the queries for creating the table and the primary unique index", ->
TestModel.attributes =
'attrQueryable': Attributes.DateTime
queryable: true
modelKey: 'attrQueryable'
jsonKey: 'attr_queryable'
'attrNonQueryable': Attributes.Collection
modelKey: 'attrNonQueryable'
jsonKey: 'attr_non_queryable'
queries = @builder.setupQueriesForTable(TestModel)
expected = [
'CREATE TABLE IF NOT EXISTS `TestModel` (id TEXT PRIMARY KEY,data BLOB,attr_queryable INTEGER)',
'CREATE UNIQUE INDEX IF NOT EXISTS `TestModel_id` ON `TestModel` (`id`)'
]
for query,i in queries
expect(query).toBe(expected[i])
it "should correctly create join tables for models that have queryable collections", ->
TestModel.configureWithCollectionAttribute()
queries = @builder.setupQueriesForTable(TestModel)
expected = [
'CREATE TABLE IF NOT EXISTS `TestModel` (id TEXT PRIMARY KEY,data BLOB,client_id TEXT,server_id TEXT,other TEXT)',
'CREATE UNIQUE INDEX IF NOT EXISTS `TestModel_id` ON `TestModel` (`id`)',
'CREATE TABLE IF NOT EXISTS `TestModelCategory` (id TEXT KEY,`value` TEXT,other TEXT)'
'CREATE INDEX IF NOT EXISTS `TestModelCategory_id` ON `TestModelCategory` (`id` ASC)'
'CREATE UNIQUE INDEX IF NOT EXISTS `TestModelCategory_val_id` ON `TestModelCategory` (`value` ASC, `id` ASC)',
]
for query,i in queries
expect(query).toBe(expected[i])
it "should use the correct column type for each attribute", ->
TestModel.configureWithAllAttributes()
queries = @builder.setupQueriesForTable(TestModel)
expect(queries[0]).toBe('CREATE TABLE IF NOT EXISTS `TestModel` (id TEXT PRIMARY KEY,data BLOB,datetime INTEGER,string-json-key TEXT,boolean INTEGER,number INTEGER)')
describe "when the model provides additional sqlite config", ->
it "the setup method should return these queries", ->
TestModel.configureWithAdditionalSQLiteConfig()
spyOn(TestModel.additionalSQLiteConfig, 'setup').andCallThrough()
queries = @builder.setupQueriesForTable(TestModel)
expect(TestModel.additionalSQLiteConfig.setup).toHaveBeenCalledWith()
expect(queries.pop()).toBe('CREATE INDEX IF NOT EXISTS ThreadListIndex ON Thread(last_message_received_timestamp DESC, account_id, id)')
it "should not fail if additional config is present, but setup is undefined", ->
delete TestModel.additionalSQLiteConfig['setup']
@m = new TestModel(id: 'local-6806434c-b0cd', body: 'hello world')
expect( => @builder.setupQueriesForTable(TestModel)).not.toThrow()