work on etags

This commit is contained in:
Nolan Lawson 2020-05-13 08:12:56 -07:00
parent 961140c167
commit e55f2ecec0
4 changed files with 59 additions and 27 deletions

View File

@ -13,20 +13,15 @@ export class Database {
}
async _init () {
const emojiBaseData = await (await fetch(this._dataSource)).json()
const response = await fetch(this._dataSource)
const emojiBaseData = await response.json()
if (!emojiBaseData || !Array.isArray(emojiBaseData)) {
throw new Error('Expected emojibase data, but data was in wrong format: ' + emojiBaseData)
}
this._idbEngine = new IndexedDBEngine(`lite-emoji-picker-${this._locale}`)
await this._idbEngine.open()
}
async loadData (emojiBaseData) {
if (!emojiBaseData || !Array.isArray(emojiBaseData)) {
throw new Error('Expected emojibase data, got: ' + emojiBaseData)
}
await this._readyPromise
await this._idbEngine.loadData(emojiBaseData)
const etag = response.headers.get('etag') // TODO: use cache-control as well
await this._idbEngine.loadData(emojiBaseData, etag)
}
async getEmojiByGroup (group) {

View File

@ -1,7 +1,7 @@
import { closeDatabase, dbPromise, deleteDatabase, openDatabase } from './databaseLifecycle'
import { closeDatabase, dbPromise, deleteDatabase, get, openDatabase } from './databaseLifecycle'
import {
DATA_VERSION_CURRENT,
INDEX_GROUP_AND_ORDER, INDEX_TOKENS,
INDEX_GROUP_AND_ORDER, INDEX_TOKENS, KEY_ETAG,
KEY_VERSION, MODE_READONLY, MODE_READWRITE,
STORE_EMOJI,
STORE_META
@ -18,24 +18,55 @@ export class IndexedDBEngine {
this._db = await openDatabase(this._dbName)
}
async loadData (emojiBaseData) {
async loadData (emojiBaseData, etag) {
const transformedData = transformEmojiBaseData(emojiBaseData)
const dataVersion = await dbPromise(this._db, STORE_META, MODE_READONLY, (metaStore, cb) => {
metaStore.get(KEY_VERSION).onsuccess = e => cb(e.target.result)
})
if (dataVersion < DATA_VERSION_CURRENT) {
await dbPromise(this._db, [STORE_EMOJI, STORE_META], MODE_READWRITE, ([emojiStore, metaStore]) => {
metaStore.get(KEY_VERSION).onsuccess = e => {
const dataVersion = e.target.result
const existingEtag = await get(this._db, STORE_META, KEY_ETAG)
if (existingEtag === etag) {
return
}
await dbPromise(this._db, [STORE_EMOJI, STORE_META], MODE_READWRITE, ([emojiStore, metaStore]) => {
let existingEtag
let gotEtag = false
let existingKeys
function checkHasEtagAndKeys () {
if (gotEtag && existingKeys) {
onGetEtagAndKeys()
}
}
function onGetEtagAndKeys () {
if (existingEtag === etag) {
// check again within the transaction to guard against concurrency, e.g. multiple browser tabs
if (dataVersion < DATA_VERSION_CURRENT) {
for (const data of transformedData) {
emojiStore.put(data)
}
return
}
if (existingKeys.length) {
for (const key of existingKeys) {
emojiStore.delete(key)
}
}
})
}
insertData()
}
function insertData () {
for (const data of transformedData) {
emojiStore.put(data)
}
}
metaStore.get(KEY_ETAG).onsuccess = e => {
existingEtag = e.target.result
gotEtag = true
checkHasEtagAndKeys()
}
emojiStore.getAllKeys().onsuccess = e => {
existingKeys = e.target.result
checkHasEtagAndKeys()
}
})
}
getEmojiByGroup (group) {

View File

@ -10,6 +10,6 @@ export const FIELD_UNICODE = 'unicode'
export const FIELD_GROUP = 'group'
export const FIELD_ORDER = 'order'
export const INDEX_GROUP_AND_ORDER = 'group-order'
export const KEY_VERSION = 'version'
export const KEY_ETAG = 'etag'
export const MODE_READONLY = 'readonly'
export const MODE_READWRITE = 'readwrite'

View File

@ -1,5 +1,5 @@
import { migrations } from './migrations'
import { DB_VERSION_CURRENT } from './constants'
import { DB_VERSION_CURRENT, KEY_VERSION, MODE_READONLY, STORE_META } from './constants'
const openReqs = {}
const databaseCache = {}
@ -57,6 +57,12 @@ export async function dbPromise (db, storeName, readOnlyOrReadWrite, cb) {
})
}
export function get (db, storeName, key) {
return dbPromise(db, storeName, MODE_READONLY, (store, cb) => {
store.get(key).onsuccess = e => cb(e.target.result)
})
}
export function closeDatabase (dbName) {
if (!dbName) {
throw new Error('dbName is required')