Commit Graph

24753 Commits

Author SHA1 Message Date
Matthew Chen
9f7b49385f "Bump build to 5.20.0.17." (Internal) 2021-08-30 13:46:44 -03:00
Matthew Chen
4ec677e7d4 "Bump build to 5.20.0.16." (Internal) 2021-08-30 10:41:06 -03:00
Matthew Chen
54f61af48a "Bump build to 5.20.0.15." (Internal) 2021-08-30 09:19:18 -03:00
Matthew Chen
c22341b630 Merge branch 'release/5.19.0' 2021-08-30 09:19:04 -03:00
Matthew Chen
c10e851a32 "Bump build to 5.19.0.56." (Internal) 2021-08-30 09:17:42 -03:00
Matthew Chen
236a4c78fb Update l10n strings. 2021-08-30 09:17:32 -03:00
Matthew Chen
e89f25088c Merge branch 'mlin/PR/BetterLoadQueryIndexing' into release/5.19.0 2021-08-30 08:56:58 -03:00
Michelle Linington
1012d7b42d Tune indices for more performant conversation loads
- Replaces the existing placeholder excluding index for two new indices,
  optimized to reduce disk I/O. These are partial indices, only
  applicable to the placeholder excluding queries performed during
  conversation load. They're also covering indices. Most queries will be
  resolved by only consulting the index. There's no need to consult the
  backing rows in the model_TSInteraction table.
- Adds a helper SQL bytecode explainer for debugging

Tracking down these optimizations took a great deal of time. I promised
in a comment that this commit message will have a thorough explanation
of why these indices are constructed the way they are. The TL;DR:
- There are a bunch of limitations on index column ordering that you
  should be aware of.
- Partial indices can help you avoid some of those limitations
- SQLite can be finnicky when trying to decide if a partial index is
  applicable to a query.
- When an index covers a given query, it can avoid a whole bunch of
  disk I/O.
- Index row size matters, and SQLite may choose a less efficient index
  if it looks like it might be smaller.

The long explanation of lessons learned along the way. All of this is
based on observation, documentation, and trial-and-error:

1. SQLite can be *very* strict when deciding whether or not to use a
   partial index. Compare these two index declarations:
  > CREATE INDEX index1 ON table(uniqueThreadId, id) WHERE 'recordType IS NOT 70';
  > CREATE INDEX index2 ON table(uniqueThreadId, id) WHERE recordType IS NOT 70;

  Note the only difference between the two partial index declarations is
  wrapping the WHERE clause in quotes. The GRDB index builder we were
  using before would do this on our behalf.

  Now let's run this query against both indices. The indices we declared
  *should* be a perfect choice for this query:
  > SELECT COUNT(*) FROM table WHERE uniqueThreadId = 'abc123'
  > AND id > 100 AND recordType IS NOT 70;

  With the first index:
  > `--SEARCH TABLE model_TSInteraction USING INDEX index_interactions_on_threadUniqueId_and_id (uniqueThreadId=? AND id>?)
  With the second index:
  > `--SEARCH TABLE model_TSInteraction USING INDEX index2 (uniqueThreadId=? AND id>?)

  By dropping the quotes around the WHERE clause, SQLite's query planner
  is now convinced that the partial index is applicable to the query
  we're running. This is good, since as SQLite iterates over the index,
  it can skip any recordType comparisons. It also reduces the search
  space, since it doesn't even need to read rows it should exclude.

2. Covering indices can be a huge performance win. An index is covering
   when every column in the index matches some element. Let's again
   compare some indices:
  > CREATE INDEX index1 ON table(uniqueThreadId, id) WHERE recordType IS NOT 70;
  > CREATE INDEX index2 ON table(uniqueThreadId, id, uniqueId) WHERE recordType IS NOT 70;
  > CREATE INDEX index3 ON table(uniqueThreadId, id, recordType, uniqueId) WHERE recordType IS NOT 70;
  (Why a difference between index2 and index3? See the next section)

  When run on this query:
  > SELECT uniqueId FROM table WHERE uniqueThreadId = 'abc123'
  > AND id > 100 AND recordType IS NOT 70;

  The first index:
  > --SEARCH TABLE model_TSInteraction USING INDEX index1 (uniqueThreadId=? AND id>?)
  The second index:
  > --SEARCH TABLE model_TSInteraction USING INDEX index2 (uniqueThreadId=? AND id>?)
  The third index:
  > --SEARCH TABLE model_TSInteraction USING COVERING INDEX index3 (uniqueThreadId=? AND id>?)

  You can see the difference in the actual query bytecode by running:
  "EXPLAIN [query]" (not "EXPLAIN QUERY PLAN"). Notice that when run
  against the first index, it needs to perform two reads. One against
  the table, and another against the index. The only reason it's
  fetching the model is to grab the uniqueId in operation 9.
  > 0     Init           0     12    0                    0   Start at 12
  > 1     OpenRead       0     10    0     67             0   root=10 iDb=0; model_TSInteraction
  > 2     OpenRead       1     162160  0     k(3,,,)        0   root=162160 iDb=0; index1
  > 3     String8        0     1     0     abc123         0   r[1]='abc123'
  > 4     Integer        100   2     0                    0   r[2]=100
  > 5     SeekGT         1     11    1     2              0   key=r[1..2]
  > 6       IdxGT          1     11    1     1              0   key=r[1]
  > 7       DeferredSeek   1     0     0                    0   Move 0 to 1.rowid if needed
  > 8       Column         0     2     3                    0   r[3]=model_TSInteraction.uniqueId
  > 9       ResultRow      3     1     0                    0   output=r[3]
  > 10    Next           1     6     0                    0
  > 11    Halt           0     0     0                    0
  > 12    Transaction    0     0     198   0              1   usesStmtJournal=0
  > 13    Goto           0     1     0                    0

3. SQLite will not consider a partial index covering unless the columns
   defining the index condition are also included in the index content.
   No idea why, but this is why index2 above isn't considered covering
   while index3 is.

4. Index column ordering matters. Briefly, if a WHERE clause in a query
   has any sort of inequality comparisons (everything but IS, IN and =),
   then every subsequent column in an index cannot be used. (mostly)

  For example:
  > CREATE INDEX ON Table(a,b,c);
  > SELECT * WHERE AND b > 100 AND c = "hi";

  Column C isn't going to be usable on the index since we're performing
  an inequality comparison on B. There are loads of exceptions SQLite
  can try to work around this. You can learn more here:
  https://www.sqlite.org/optoverview.html

  How this applies to this change. Consider these two indices:
  > CREATE INDEX index1 ON table(uniqueThreadId, id, recordType, uniqueId) WHERE recordType IS NOT 70;
  > CREATE INDEX index2 ON table(uniqueThreadId, recordType, id, uniqueId) WHERE recordType IS NOT 70;
  > SELECT uniqueId FROM table WHERE uniqueThreadId = 'abc123'
  > AND id > 100 AND recordType IS NOT 70;

  index1 is absolutely going to be the better choice here. Since index1
  is sorted by id first, it's easy for SQLite to binary search to the
  correct offset in the index and then just scan down the index.
  (verifying that each recordType IS NOT 70 along the way, which will
   always hold true because of the partial index condition).

5. Index size matters. At this point, we've constructed this index:
   > CREATE INDEX index1 ON table(uniqueThreadId, id, recordType, uniqueId) WHERE recordType IS NOT 70;

  And SQLite is using it for queries like this. Great!
  > SELECT uniqueId FROM model_TSInteraction
  > WHERE uniqueThreadId = 'whatever' AND recordType IS NOT 70
  > ORDER BY id LIMIT 2 OFFSET 0
  > `--SEARCH TABLE model_TSInteraction USING COVERING INDEX index1 (uniqueThreadId=?)

  Here's a simpler query that we need to run to. It is even less
  specific than the one above so we should expect it to use our
  index.
  > SELECT COUNT(*) FROM model_TSInteraction
  > WHERE uniqueThreadId = 'blah' AND recordType IS NOT 70;
  > `--SEARCH TABLE model_TSInteraction USING COVERING INDEX index_model_TSInteraction_on_uniqueThreadId_and_hasEnded_and_recordType (uniqueThreadId=?)

  As best I can tell, the reason SQLite is picking this existing index
  is because each row in the index is smaller.
  Each row in our index: [String, Int, Int, String]
  Each row in the chosen index: [String, Bool, Int]

  The query planner had to choose, was it better off picking the index
  that's more specific but larger, or less specific but smaller. It
  picked the latter.

  To work around this we can add a *second* index, that's even smaller
  that the index it's choosing.
  > CREATE INDEX index2 ON table(uniqueThreadId, recordType) WHERE recordType IS NOT 70;

  Now the query picks our new index to run the count.
  --SEARCH TABLE model_TSInteraction USING COVERING INDEX index2 (uniqueThreadId=?)
2021-08-30 08:55:53 -03:00
Matthew Chen
5c23dfa341 Merge remote-tracking branch 'private/mlin/PR/PashtoLanguageSupport' into release/5.19.0 2021-08-30 08:29:12 -03:00
Nora Trapp
4a80b2ca42 "Bump build to 5.20.0.14." (nightly-08-30-2021) 2021-08-30 04:00:15 -07:00
Michelle Linington
e383e1459c Add Pashto localization files 2021-08-29 15:36:21 -07:00
Nora Trapp
6db1a5ad9e "Bump build to 5.20.0.13." (nightly-08-29-2021) 2021-08-29 04:00:12 -07:00
Nora Trapp
67784a7a8b "Bump build to 5.20.0.12." (nightly-08-28-2021) 2021-08-28 04:00:11 -07:00
Matthew Chen
80283f1d39 "Bump build to 5.20.0.11." (Internal) 2021-08-27 22:16:42 -03:00
Matthew Chen
0957a5df49 Merge branch 'release/5.19.0' 2021-08-27 22:15:56 -03:00
Matthew Chen
31cf855901 "Feature flags for .qa." 2021-08-27 22:14:28 -03:00
Matthew Chen
4d146c5a7e "Bump build to 5.19.0.55." (Beta) 2021-08-27 22:14:23 -03:00
Matthew Chen
e2ed0e8a88 "Feature flags for .beta." 2021-08-27 22:14:20 -03:00
Matthew Chen
fa0ef14c0b Merge branch 'charlesmchen/resumeAttachmentUploadCrash' into release/5.19.0 2021-08-27 22:12:38 -03:00
Matthew Chen
8908233e72 Fix 'resume attachment upload' crash. 2021-08-27 22:12:16 -03:00
Matthew Chen
8c56bcfc57 Update l10n strings. 2021-08-27 22:11:30 -03:00
Michelle Linington
13c950bd60 "Bump build to 5.19.0.54." (Internal) 2021-08-27 16:33:32 -07:00
Michelle Linington
671c3db656 Fix inadvertent compilation error 2021-08-27 16:28:53 -07:00
Michelle Linington
d8880306a9 "Bump build to 5.19.0.53." (Internal) 2021-08-27 16:23:24 -07:00
Michelle Linington
6af6272cea Merge branch 'mlin/PR/EnableSenderKey' into release/5.19.0 2021-08-27 16:14:00 -07:00
Michelle Linington
94726b3dad Enable sender key 2021-08-27 16:13:48 -07:00
Michelle Linington
67f15704eb Merge branch 'mlin/PR/SenderKeyFinalFixes' into release/5.19.0 2021-08-27 16:13:13 -07:00
Michelle Linington
aad8af1ef4 Lint 2021-08-27 16:12:24 -07:00
Michelle Linington
94f3d297a4 Fixes a few final sender key issues:
- The excludePlaceholders parameter was inverted, but the DebugFlag
  wasn't
- Fix an issue with how we detect content-less sync messages
2021-08-27 16:12:24 -07:00
Michelle Linington
f617d5d589 Merge branch 'mlin/PR/SmallSenderKeyFixes' into release/5.19.0 2021-08-27 16:10:59 -07:00
Michelle Linington
d3ff346af9 PR Feedback 2021-08-27 16:10:36 -07:00
Michelle Linington
aff4be51d2 Some small sender key changes
- Annotate groupIds fetched from the UD messgae content as untrusted
- Double check the sender of a failed decryption is a member of the
  group before inserting a placeholder/error message
- Double check group membership hasn't changed before deciding not to
  expire a sender key.
2021-08-27 16:10:36 -07:00
Michelle Linington
d76292b5ec Merge branch 'mlin/PR/OrphanDataSpuriousAssertion' 2021-08-27 14:26:24 -07:00
Michelle Linington
8de3b76ee9 There are two types of File Not Found errors we can ignore 2021-08-27 14:26:10 -07:00
Eugene Bistolas
82c19348d2 Merge branch 'eb/nseAvatarDonationUpdate' 2021-08-27 10:03:58 -10:00
Nora Trapp
17dad45ef1 Merge branch 'nt/message-send-perf' into release/5.19.0 2021-08-27 12:58:31 -07:00
Nora Trapp
64ca34f485 Eliminate wait on main thread for every network request 2021-08-27 12:55:57 -07:00
Nora Trapp
ee298b4688 Start next job in the same transaction it's created in 2021-08-27 12:55:57 -07:00
Nora Trapp
15cb0a5730 Add benchmarks around various message sending milestones 2021-08-27 12:55:57 -07:00
Eugene Bistolas
aa810205cf Defer NSE contentUpdating until donation completes, enable caching for avatars 2021-08-27 09:25:56 -10:00
Matthew Chen
6f21ce52ce Tweak sender name size. 2021-08-27 14:22:01 -03:00
Matthew Chen
4c003d6f5a "Bump build to 5.20.0.10." (Internal) 2021-08-27 13:37:16 -03:00
Matthew Chen
6362ee7952 "Bump build to 5.20.0.9." (Internal) 2021-08-27 08:39:50 -03:00
Nora Trapp
ee883eb3db "Bump build to 5.20.0.8." (nightly-08-27-2021) 2021-08-27 04:01:08 -07:00
Michelle Linington
aa9f18e572 Merge branch 'mlin/PR/FixTests' 2021-08-26 23:10:50 -07:00
Michelle Linington
744c527233 Fix tests 2021-08-26 22:47:07 -07:00
Michelle Linington
b84bba3dc0 Merge branch 'mlin/PR/FixQuotedReplies' 2021-08-26 22:32:51 -07:00
Michelle Linington
19e5506dde Lint 2021-08-26 22:32:30 -07:00
Michelle Linington
436acf2694 Various bug fixes and improvements
Most significant here is better handling of quoted reply content without
an attachment. Or with an attachment without a thumbnail
2021-08-26 22:32:30 -07:00
Michelle Linington
9a49c3b64c Bug fixes 2021-08-26 22:32:30 -07:00