Mailspring/internal_packages/worker-sync/lib/refreshing-json-cache.coffee
Ben Gotow ea76b7c442 feat(transactions): Explicit (and faster) database transactions
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
2015-12-17 11:46:05 -08:00

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