pinafore/routes/_database/cleanup.js
Nolan Lawson 270df188cb
Store IDB data for 1 week rather than 2 weeks (#498)
Based on issues I'm seeing on iOS Safari, I'm guessing that my IDB caching is too aggressive and is hitting origin storage limits, which causes IDB transactions in safari to fail silently and never call any transaction callbacks (`onblocked`, `onerror`, `oncomplete`).

This is pretty bad and causes Pinafore to basically just not work unless you clear site data, which we should avoid. 2 weeks was probably unnecessary anyway; in a social network, who cares about content that's 2 weeks old?
2018-08-26 15:38:54 -07:00

150 lines
4.1 KiB
JavaScript

import { dbPromise, getDatabase } from './databaseLifecycle'
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
import {
ACCOUNTS_STORE,
NOTIFICATION_TIMELINES_STORE,
NOTIFICATIONS_STORE,
PINNED_STATUSES_STORE,
RELATIONSHIPS_STORE,
STATUS_TIMELINES_STORE,
STATUSES_STORE,
THREADS_STORE,
TIMESTAMP
} from './constants'
import debounce from 'lodash-es/debounce'
import { store } from '../_store/store'
import { mark, stop } from '../_utils/marks'
import { deleteAll } from './utils'
import { createPinnedStatusKeyRange, createThreadKeyRange } from './keys'
const BATCH_SIZE = 20
const TIME_AGO = 7 * 24 * 60 * 60 * 1000 // one week ago
const DELAY = 5 * 60 * 1000 // five minutes
function batchedGetAll (callGetAll, callback) {
function nextBatch () {
callGetAll().onsuccess = function (e) {
let results = e.target.result
callback(results)
if (results.length) {
nextBatch()
}
}
}
nextBatch()
}
function cleanupStatuses (statusesStore, statusTimelinesStore, threadsStore, cutoff) {
batchedGetAll(
() => statusesStore.index(TIMESTAMP).getAllKeys(IDBKeyRange.upperBound(cutoff), BATCH_SIZE),
results => {
results.forEach(statusId => {
statusesStore.delete(statusId)
deleteAll(
statusTimelinesStore,
statusTimelinesStore.index('statusId'),
IDBKeyRange.only(statusId)
)
deleteAll(
threadsStore,
threadsStore,
createThreadKeyRange(statusId)
)
})
}
)
}
function cleanupNotifications (notificationsStore, notificationTimelinesStore, cutoff) {
batchedGetAll(
() => notificationsStore.index(TIMESTAMP).getAllKeys(IDBKeyRange.upperBound(cutoff), BATCH_SIZE),
results => {
results.forEach(notificationId => {
notificationsStore.delete(notificationId)
deleteAll(
notificationTimelinesStore,
notificationTimelinesStore.index('notificationId'),
IDBKeyRange.only(notificationId)
)
})
}
)
}
function cleanupAccounts (accountsStore, pinnedStatusesStore, cutoff) {
batchedGetAll(
() => accountsStore.index(TIMESTAMP).getAllKeys(IDBKeyRange.upperBound(cutoff), BATCH_SIZE),
results => {
results.forEach(accountId => {
accountsStore.delete(accountId)
deleteAll(
pinnedStatusesStore,
pinnedStatusesStore,
createPinnedStatusKeyRange(accountId)
)
})
}
)
}
function cleanupRelationships (relationshipsStore, cutoff) {
batchedGetAll(
() => relationshipsStore.index(TIMESTAMP).getAllKeys(IDBKeyRange.upperBound(cutoff), BATCH_SIZE),
results => {
results.forEach(relationshipId => {
relationshipsStore.delete(relationshipId)
})
}
)
}
async function cleanup (instanceName) {
console.log('cleanup', instanceName)
mark(`cleanup:${instanceName}`)
let db = await getDatabase(instanceName)
let storeNames = [
STATUSES_STORE,
STATUS_TIMELINES_STORE,
NOTIFICATIONS_STORE,
NOTIFICATION_TIMELINES_STORE,
ACCOUNTS_STORE,
RELATIONSHIPS_STORE,
THREADS_STORE,
PINNED_STATUSES_STORE
]
await dbPromise(db, storeNames, 'readwrite', (stores) => {
let [
statusesStore,
statusTimelinesStore,
notificationsStore,
notificationTimelinesStore,
accountsStore,
relationshipsStore,
threadsStore,
pinnedStatusesStore
] = stores
let cutoff = Date.now() - TIME_AGO
cleanupStatuses(statusesStore, statusTimelinesStore, threadsStore, cutoff)
cleanupNotifications(notificationsStore, notificationTimelinesStore, cutoff)
cleanupAccounts(accountsStore, pinnedStatusesStore, cutoff)
cleanupRelationships(relationshipsStore, cutoff)
})
stop(`cleanup:${instanceName}`)
}
function doCleanup (instanceName) {
scheduleIdleTask(() => cleanup(instanceName))
}
function scheduledCleanup () {
console.log('scheduledCleanup')
let { loggedInInstancesInOrder } = store.get()
for (let instance of loggedInInstancesInOrder) {
doCleanup(instance)
}
}
export const scheduleCleanup = debounce(() => scheduleIdleTask(scheduledCleanup), DELAY)