mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-10 03:29:36 +01:00
[Refactor]: Optimize cache
Implement own caching system, since mem doesnt allow for fine enough control. I think im done for the year.
This commit is contained in:
parent
30ec16f610
commit
2f2af239fa
4 changed files with 91 additions and 14 deletions
|
|
@ -299,10 +299,9 @@ class ImapService extends EventEmitter {
|
||||||
* @param uid delete specific mail per UID
|
* @param uid delete specific mail per UID
|
||||||
*/
|
*/
|
||||||
async deleteSpecificEmail(uid) {
|
async deleteSpecificEmail(uid) {
|
||||||
debug(`Deleting mails ${uid}`)
|
|
||||||
if (!this.config.email.examples.uids.includes(parseInt(uid))) {
|
if (!this.config.email.examples.uids.includes(parseInt(uid))) {
|
||||||
await this.connection.deleteMessage(uid)
|
await this.connection.deleteMessage(uid)
|
||||||
debug(`Deleted mail with UID: ${uid}.`)
|
debug(`Deleted UID ${uid}`)
|
||||||
this.emit(ImapService.EVENT_DELETED_MAIL, uid)
|
this.emit(ImapService.EVENT_DELETED_MAIL, uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
const EventEmitter = require('events')
|
const EventEmitter = require('events')
|
||||||
const debug = require('debug')('48hr-email:imap-processor')
|
const debug = require('debug')('48hr-email:imap-processor')
|
||||||
const mem = require('mem')
|
|
||||||
const ImapService = require('./imap-service')
|
const ImapService = require('./imap-service')
|
||||||
const Helper = require('./helper')
|
const Helper = require('./helper')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
|
|
@ -16,9 +15,7 @@ class MailProcessingService extends EventEmitter {
|
||||||
this.config = config
|
this.config = config
|
||||||
|
|
||||||
// Cached methods:
|
// Cached methods:
|
||||||
this.cachedFetchFullMail = mem(
|
this._initCache()
|
||||||
this.imapService.fetchOneFullMail.bind(this.imapService), { maxAge: 10 * 60 * 1000 }
|
|
||||||
)
|
|
||||||
|
|
||||||
this.initialLoadDone = false
|
this.initialLoadDone = false
|
||||||
|
|
||||||
|
|
@ -32,21 +29,97 @@ class MailProcessingService extends EventEmitter {
|
||||||
}, this.config.imap.refreshIntervalSeconds * 1000)
|
}, this.config.imap.refreshIntervalSeconds * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_initCache() {
|
||||||
|
// Create a cache storage to track entries by UID
|
||||||
|
this.cacheStorage = new Map() // Map of "address:uid:raw" -> cached result
|
||||||
|
|
||||||
|
// Wrapper that maintains our own cache with selective deletion
|
||||||
|
this.cachedFetchFullMail = async(address, uid, raw) => {
|
||||||
|
const cacheKey = `${address}:${uid}:${raw}`
|
||||||
|
|
||||||
|
// Check our cache first
|
||||||
|
if (this.cacheStorage.has(cacheKey)) {
|
||||||
|
const entry = this.cacheStorage.get(cacheKey)
|
||||||
|
if (Date.now() - entry.timestamp < 10 * 60 * 1000) {
|
||||||
|
return entry.value
|
||||||
|
} else {
|
||||||
|
this.cacheStorage.delete(cacheKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and cache
|
||||||
|
const result = await this.imapService.fetchOneFullMail(address, uid, raw)
|
||||||
|
this.cacheStorage.set(cacheKey, {
|
||||||
|
value: result,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
uid: uid
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap it to use in sync context
|
||||||
|
this._wrappedCachedFetch = (address, uid, raw) => {
|
||||||
|
return this.cachedFetchFullMail(address, uid, raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearCache() {
|
||||||
|
// Clear entire cache
|
||||||
|
debug('Clearing entire email cache')
|
||||||
|
this.cacheStorage.clear()
|
||||||
|
this._initCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
_clearCacheForUid(uid) {
|
||||||
|
// Selectively clear cache entries for a specific UID
|
||||||
|
// Normalize UID to integer for comparison
|
||||||
|
const normalizedUid = parseInt(uid)
|
||||||
|
let cleared = 0
|
||||||
|
|
||||||
|
for (const [key, entry] of this.cacheStorage.entries()) {
|
||||||
|
if (parseInt(entry.uid) === normalizedUid) {
|
||||||
|
this.cacheStorage.delete(key)
|
||||||
|
cleared++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleared > 0) {
|
||||||
|
debug(`Cleared ${cleared} cache entries for UID ${uid}`)
|
||||||
|
} else {
|
||||||
|
debug(`No cache entries found for UID ${uid}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getMailSummaries(address) {
|
getMailSummaries(address) {
|
||||||
debug('Getting mail summaries for', address)
|
debug('Getting mail summaries for', address)
|
||||||
return this.mailRepository.getForRecipient(address)
|
return this.mailRepository.getForRecipient(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSpecificEmail(adress, uid) {
|
deleteSpecificEmail(adress, uid) {
|
||||||
debug('Deleting specific email', adress, uid)
|
|
||||||
if (this.mailRepository.removeUid(uid, adress) == true) {
|
if (this.mailRepository.removeUid(uid, adress) == true) {
|
||||||
|
// Clear cache immediately for this UID
|
||||||
|
debug('Clearing cache for uid', uid)
|
||||||
|
this._clearCacheForUid(uid)
|
||||||
this.imapService.deleteSpecificEmail(uid)
|
this.imapService.deleteSpecificEmail(uid)
|
||||||
|
} else {
|
||||||
|
debug('Repository removeUid returned false for', uid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOneFullMail(address, uid, raw = false) {
|
getOneFullMail(address, uid, raw = false) {
|
||||||
debug('Cache lookup for', address + ':' + uid, raw ? '(raw)' : '(parsed)')
|
debug('Cache lookup for', address + ':' + uid, raw ? '(raw)' : '(parsed)')
|
||||||
return this.cachedFetchFullMail(address, uid, raw)
|
|
||||||
|
// Check if this UID exists in repository before fetching
|
||||||
|
const summaries = this.mailRepository.getForRecipient(address)
|
||||||
|
const exists = summaries.some(mail => mail.uid === parseInt(uid))
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
debug(`UID ${uid} not found in repository for ${address}, returning null`)
|
||||||
|
return Promise.resolve(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._wrappedCachedFetch(address, uid, raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllMailSummaries() {
|
getAllMailSummaries() {
|
||||||
|
|
@ -87,7 +160,15 @@ class MailProcessingService extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMailDeleted(uid) {
|
onMailDeleted(uid) {
|
||||||
debug('Mail deleted with uid', uid)
|
debug('Mail deleted:', uid)
|
||||||
|
|
||||||
|
// Clear cache for this specific UID
|
||||||
|
try {
|
||||||
|
this._clearCacheForUid(uid)
|
||||||
|
} catch (err) {
|
||||||
|
debug('Failed to clear email cache:', err.message)
|
||||||
|
}
|
||||||
|
|
||||||
this.mailRepository.removeUid(uid)
|
this.mailRepository.removeUid(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ class MailRepository {
|
||||||
const mailToDelete = mails.find(mail => mail.uid === parseInt(uid))
|
const mailToDelete = mails.find(mail => mail.uid === parseInt(uid))
|
||||||
if (mailToDelete) {
|
if (mailToDelete) {
|
||||||
this.mailSummaries.remove(address, mailToDelete)
|
this.mailSummaries.remove(address, mailToDelete)
|
||||||
debug('Removed ', mailToDelete.date, address, mailToDelete.subject)
|
|
||||||
deleted = true
|
deleted = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,7 @@ router.get(
|
||||||
async(req, res, next) => {
|
async(req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const mailProcessingService = req.app.get('mailProcessingService')
|
const mailProcessingService = req.app.get('mailProcessingService')
|
||||||
debug(`Deleting email ${req.params.uid} for ${req.params.address}`)
|
mailProcessingService.deleteSpecificEmail(req.params.address, req.params.uid)
|
||||||
await mailProcessingService.deleteSpecificEmail(req.params.address, req.params.uid)
|
|
||||||
debug(`Successfully deleted email ${req.params.uid} for ${req.params.address}`)
|
|
||||||
res.redirect(`/inbox/${req.params.address}`)
|
res.redirect(`/inbox/${req.params.address}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debug(`Error deleting email ${req.params.uid} for ${req.params.address}:`, error.message)
|
debug(`Error deleting email ${req.params.uid} for ${req.params.address}:`, error.message)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue