Commit Graph

24704 Commits

Author SHA1 Message Date
Matthew Chen
fffca57867 "Feature flags for .beta." 2021-08-30 17:45:59 -03:00
Matthew Chen
5f35380d83 Update l10n strings. 2021-08-30 17:45:39 -03:00
Matthew Chen
6627945156 Merge branch 'charlesmchen/conversationPickerSearchCrash' into release/5.19.0 2021-08-30 17:10:11 -03:00
Matthew Chen
c680a4b36b Fix crash in conversation picker. 2021-08-30 17:09:50 -03:00
Matthew Chen
e3c8af828e Merge branch 'charlesmchen/minorFixes2021.08.30' into release/5.19.0 2021-08-30 17:06:41 -03:00
Matthew Chen
016e814884 Restore conversation picker selection state after navigation bar changes. 2021-08-30 17:06:27 -03:00
Matthew Chen
82a4dfa955 Ensure conversation picker selection UI reflects theme. 2021-08-30 17:06:27 -03:00
Matthew Chen
2bf974bc58 Don't restore keyboard after CVC message context menu if the menu item presented a VC. 2021-08-30 17:06:27 -03:00
Matthew Chen
c474d3f07c Ensure message actions footer updates for theme changes; fixed styling of its selection label. 2021-08-30 17:06:27 -03:00
Matthew Chen
e6676db82a Revert "Merge remote-tracking branch 'private/mlin/PR/PashtoLanguageSupport' into release/5.19.0"
This reverts commit 5c23dfa341, reversing
changes made to 31cf855901.
2021-08-30 15:24:34 -03:00
Matthew Chen
345c46dba4 Bump CFBundleVersion to 5030. 2021-08-30 09:32: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
Michelle Linington
e383e1459c Add Pashto localization files 2021-08-29 15:36:21 -07: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
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
Michelle Linington
98adf7ae52 Merge branch 'mlin/PR/PlaceholderPerf' into release/5.19.0 2021-08-26 20:57:20 -07:00
Michelle Linington
f7a5aafc63 Update Pods 2021-08-26 20:57:07 -07:00
Michelle Linington
6b5e7fb7b7 PR Feedback from earlier PR:
- Don't open up read transactions on the main thread if it can be
  avoided
- Use readiness async
- Remove spurious [weak self]
2021-08-26 20:57:06 -07:00
Michelle Linington
8b283bf82e Lint + Update Pods 2021-08-26 20:57:06 -07:00
Michelle Linington
c37312478d More index tuning 2021-08-26 20:57:06 -07:00
Michelle Linington
d69483e489 Update index to use expression instead of column 2021-08-26 20:57:06 -07:00
Michelle Linington
c7444cb01d Remove isHidden column, use recordType instead 2021-08-26 20:57:06 -07:00
Michelle Linington
5b1b0d3bd0 Make hiddenUntilTimestamp a boolean column 2021-08-26 20:57:06 -07:00
Michelle Linington
4eeb4e34be Tests should be runnable in Profiling scheme 2021-08-26 20:57:06 -07:00
Michelle Linington
914b909e93 Testing 2021-08-26 20:57:06 -07:00
Michelle Linington
eec4d68ee1 Merge branch 'mlin/PR/InvisiblePlaceholders' into release/5.19.0 2021-08-26 20:56:36 -07:00
Michelle Linington
1407a36370 Lint 2021-08-26 20:29:22 -07:00