mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-11-06 22:46:07 +08:00
Summary:
Until now, we've been hiding transactions beneath the surface. When you call persistModel, you're implicitly creating a transaction.
You could explicitly create them with `atomically`..., but there were several critical problems that are fixed in this diff:
- Calling persistModel / unpersistModel within a transaction could cause the DatabaseStore to trigger. This could result in other parts of the app making queries /during/
the transaction, potentially before the COMMIT occurred and saved the changes. The new, explicit inTransaction syntax holds all changes until after COMMIT and then triggers.
- Calling atomically and then calling persistModel inside that resulted in us having to check whether a transaction was present and was gross.
- Many parts of the code ran extensive logic inside a promise chained within `atomically`:
BAD:
```
DatabaseStore.atomically =>
DatabaseStore.persistModel(draft) =>
GoMakeANetworkRequestThatReturnsAPromise
```
OVERWHELMINGLY BETTER:
```
DatabaseStore.inTransaction (t) =>
t.persistModel(draft)
.then =>
GoMakeANetworkRequestThatReturnsAPromise
```
Having explicit transactions also puts us on equal footing with Sequelize and other ORMs. Note that you /have/ to call DatabaseStore.inTransaction (t) =>. There is no other way to access the methods that let you alter the database. :-)
Other changes:
- This diff removes Message.labels and the Message-Labels table. We weren't using Message-level labels anywhere, and the table could grow very large.
- This diff changes the page size during initial sync from 250 => 200 in an effort to make transactions a bit faster.
Test Plan: Run tests!
Reviewers: juan, evan
Reviewed By: juan, evan
Differential Revision: https://phab.nylas.com/D2353
54 lines
1.4 KiB
CoffeeScript
54 lines
1.4 KiB
CoffeeScript
_ = require 'underscore'
|
|
{NylasStore, DatabaseStore} = require 'nylas-exports'
|
|
|
|
|
|
class RefreshingJSONCache
|
|
|
|
constructor: ({@key, @version, @refreshInterval}) ->
|
|
@_timeoutId = null
|
|
|
|
start: ->
|
|
# Clear any scheduled actions
|
|
@end()
|
|
|
|
# Look up existing data from db
|
|
DatabaseStore.findJSONBlob(@key).then (json) =>
|
|
|
|
# Refresh immediately if json is missing or version is outdated. Otherwise,
|
|
# compute next refresh time and schedule
|
|
timeUntilRefresh = 0
|
|
if json? and json.version is @version
|
|
timeUntilRefresh = Math.max(0, @refreshInterval - (Date.now() - json.time))
|
|
|
|
@_timeoutId = setTimeout(@refresh, timeUntilRefresh)
|
|
|
|
reset: ->
|
|
# Clear db value, turn off any scheduled actions
|
|
DatabaseStore.inTransaction (t) => t.persistJSONBlob(@key, {})
|
|
@end()
|
|
|
|
end: ->
|
|
# Turn off any scheduled actions
|
|
clearInterval(@_timeoutId) if @_timeoutId
|
|
@_timeoutId = null
|
|
|
|
refresh: =>
|
|
# Set up next refresh call
|
|
clearTimeout(@_timeoutId) if @_timeoutId
|
|
@_timeoutId = setTimeout(@refresh, @refreshInterval)
|
|
|
|
# Call fetch data function, save it to the database
|
|
@fetchData (newValue) =>
|
|
DatabaseStore.inTransaction (t) =>
|
|
t.persistJSONBlob(@key, {
|
|
version: @version
|
|
time: Date.now()
|
|
value: newValue
|
|
})
|
|
|
|
fetchData: (callback) =>
|
|
throw new Error("Subclasses should override this method.")
|
|
|
|
|
|
|
|
module.exports = RefreshingJSONCache
|