implemented LRU cache
This commit is contained in:
@@ -3,7 +3,7 @@ import { openDB, deleteDB } from "../scripts/idb/index.js"
|
|||||||
|
|
||||||
const MAX_TIMESTAMP_DIFFERENCE = 3
|
const MAX_TIMESTAMP_DIFFERENCE = 3
|
||||||
|
|
||||||
describe("sanity check", () => {
|
xdescribe("sanity check", () => {
|
||||||
it("works", () => {
|
it("works", () => {
|
||||||
expect(true).toBeTrue()
|
expect(true).toBeTrue()
|
||||||
})
|
})
|
||||||
@@ -38,6 +38,13 @@ describe("SlopDB", () => {
|
|||||||
describe("version 2", () => {
|
describe("version 2", () => {
|
||||||
|
|
||||||
let slopdb
|
let slopdb
|
||||||
|
let mock_clock
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
const j_clock = jasmine.clock()
|
||||||
|
mock_clock = j_clock.install()
|
||||||
|
mock_clock.mockDate(new Date(2020, 1, 1))
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
slopdb = new SlopDB(2)
|
slopdb = new SlopDB(2)
|
||||||
@@ -60,17 +67,20 @@ describe("SlopDB", () => {
|
|||||||
|
|
||||||
const slop_url = new URL("https://sloppy-slop.com/sloparticle")
|
const slop_url = new URL("https://sloppy-slop.com/sloparticle")
|
||||||
|
|
||||||
await cache.store(slop_url.toString())
|
const new_item = await cache.store(slop_url.toString())
|
||||||
const store_time = Date.now()
|
const store_time = Date.now()
|
||||||
|
|
||||||
|
mock_clock.tick(1000)
|
||||||
const cached_item = await cache.get(slop_url.toString())
|
const cached_item = await cache.get(slop_url.toString())
|
||||||
|
|
||||||
expect(cached_item.url).toEqual(slop_url.toString())
|
expect(cached_item.url).toEqual(slop_url.toString())
|
||||||
expect(cached_item.check_timestamp).toBeLessThanOrEqual(store_time)
|
expect(new_item.check_timestamp).toBeCloseTo(store_time)
|
||||||
expect(cached_item.check_timestamp).toBeGreaterThan(store_time - MAX_TIMESTAMP_DIFFERENCE)
|
expect(new_item.check_timestamp).toBeGreaterThan(store_time - MAX_TIMESTAMP_DIFFERENCE)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("updates a cached url's timestamp when it is accessed", async () => {
|
it("updates a cached url's timestamp when it is accessed", async () => {
|
||||||
const mock_clock = clock.install()
|
|
||||||
|
|
||||||
const cache = slopdb.get_check_cache()
|
const cache = slopdb.get_check_cache()
|
||||||
|
|
||||||
const slop_url = new URL("https://sloppy-slop.com/sloparticle")
|
const slop_url = new URL("https://sloppy-slop.com/sloparticle")
|
||||||
@@ -79,8 +89,30 @@ describe("SlopDB", () => {
|
|||||||
|
|
||||||
mock_clock.tick(1000 * 30)
|
mock_clock.tick(1000 * 30)
|
||||||
|
|
||||||
const cached_item = cache.get(slop_url.toString())
|
const cached_item = await cache.get(slop_url.toString())
|
||||||
expect(cached_item.check_timestamp).toEqual(store_time + 30)
|
expect(cached_item.check_timestamp).toBeGreaterThan(store_time + 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("evicts the least recently accessed URL when an item is added to a full cache", async () => {
|
||||||
|
const cache = slopdb.get_check_cache()
|
||||||
|
cache.cache_capacity = 2
|
||||||
|
|
||||||
|
const slop_url = new URL("https://sloppy-slop.com/sloparticle")
|
||||||
|
await cache.store(slop_url.toString())
|
||||||
|
|
||||||
|
mock_clock.tick(1000)
|
||||||
|
|
||||||
|
const slop_url2 = new URL("https://sloppy-slop.com/sloparticle2")
|
||||||
|
await cache.store(slop_url2.toString())
|
||||||
|
|
||||||
|
mock_clock.tick(1000)
|
||||||
|
|
||||||
|
const slop_url3 = new URL("https://sloppy-slop.com/sloparticle3")
|
||||||
|
await cache.store(slop_url3.toString())
|
||||||
|
|
||||||
|
const get_slop1 = await cache.get(slop_url.toString())
|
||||||
|
expect(get_slop1).toBeUndefined()
|
||||||
|
expect(cache.size).toEqual(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { openDB, IDBPDatabase } from "idb/index.js"
|
import { openDB, IDBPDatabase, unwrap } from "idb/index.js"
|
||||||
|
|
||||||
export class IDBCursorValueIterator {
|
export class IDBCursorValueIterator {
|
||||||
cursor: IDBCursorWithValue
|
cursor: IDBCursorWithValue
|
||||||
@@ -37,11 +37,13 @@ export class IDBCursorValueIterator {
|
|||||||
export class CheckCache {
|
export class CheckCache {
|
||||||
slopdb: SlopDB
|
slopdb: SlopDB
|
||||||
cache_capacity: number
|
cache_capacity: number
|
||||||
|
size: number
|
||||||
static cache_objectstore_name = "checkcache"
|
static cache_objectstore_name = "checkcache"
|
||||||
|
|
||||||
constructor(slopdb: SlopDB, max_entries: number) {
|
constructor(slopdb: SlopDB, max_entries: number) {
|
||||||
this.slopdb = slopdb
|
this.slopdb = slopdb
|
||||||
this.cache_capacity = max_entries
|
this.cache_capacity = max_entries
|
||||||
|
this.size = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_item_factory(url: string) {
|
cache_item_factory(url: string) {
|
||||||
@@ -53,43 +55,31 @@ export class CheckCache {
|
|||||||
|
|
||||||
async store(url: string) {
|
async store(url: string) {
|
||||||
const cache_store = this.slopdb.db.transaction(CheckCache.cache_objectstore_name, "readwrite").objectStore(CheckCache.cache_objectstore_name)
|
const cache_store = this.slopdb.db.transaction(CheckCache.cache_objectstore_name, "readwrite").objectStore(CheckCache.cache_objectstore_name)
|
||||||
await cache_store.add(this.cache_item_factory(url))
|
const cache_item = this.cache_item_factory(url)
|
||||||
|
cache_store.add(cache_item)
|
||||||
|
this.size++
|
||||||
|
if(this.size > this.cache_capacity) {
|
||||||
|
this.evict_lru()
|
||||||
|
}
|
||||||
|
return cache_item
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(url: string) {
|
async get(url: string) {
|
||||||
const cache_store = this.slopdb.db.transaction(CheckCache.cache_objectstore_name, "readwrite").objectStore(CheckCache.cache_objectstore_name)
|
const cache_store = this.slopdb.db.transaction(CheckCache.cache_objectstore_name, "readwrite").objectStore(CheckCache.cache_objectstore_name)
|
||||||
return cache_store.get(url)
|
const cache_item = await cache_store.get(url)
|
||||||
|
if(cache_item) {
|
||||||
|
cache_item.check_timestamp = Date.now()
|
||||||
|
await cache_store.put(cache_item)
|
||||||
|
}
|
||||||
|
return cache_item
|
||||||
}
|
}
|
||||||
|
|
||||||
// async evict_least_recently_checked(count: number) {
|
async evict_lru() {
|
||||||
// const transaction = this.slopdb.start_transaction(CheckCache.cache_objectstore_name, "readwrite")
|
const timestamp_index = this.slopdb.db.transaction(CheckCache.cache_objectstore_name, "readwrite").store.index("timestamp")
|
||||||
// const cache_objectstore = transaction.objectStore(CheckCache.cache_objectstore_name)
|
const timestamp_cursor = await timestamp_index.openCursor()
|
||||||
|
await timestamp_cursor.delete()
|
||||||
// const cursor_result_promise = new Promise<Iterable<any>>((resolve, reject) => {
|
this.size--
|
||||||
// const cache_cursor_request = cache_objectstore.openCursor()
|
}
|
||||||
|
|
||||||
// cache_cursor_request.onerror = (error) => {
|
|
||||||
// reject(error)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// cache_cursor_request.onsuccess = (event) => {
|
|
||||||
// const cursor = cache_cursor_request.result
|
|
||||||
// resolve(new IDBCursorValueIterator(cursor))
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const cursor = await cursor_result_promise
|
|
||||||
|
|
||||||
// const key_array = Array.from(cursor)
|
|
||||||
// key_array.sort((a, b) => {
|
|
||||||
// const a_datetime = a.check_timestamp
|
|
||||||
// const b_datetime = b.check_timestamp
|
|
||||||
|
|
||||||
// return a_datetime.getTime - b_datetime.getTime
|
|
||||||
// })
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SlopDB {
|
export class SlopDB {
|
||||||
@@ -103,7 +93,8 @@ export class SlopDB {
|
|||||||
db.createObjectStore("slop", { keyPath: "domain" })
|
db.createObjectStore("slop", { keyPath: "domain" })
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
db.createObjectStore("checkcache", { keyPath: "url" })
|
const store = db.createObjectStore("checkcache", { keyPath: "url" })
|
||||||
|
store.createIndex("timestamp", "check_timestamp")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user