Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change LRUDiskCache to allow subclasses to override functionality #554

Open
wants to merge 5 commits into
base: feat/cacheOptions
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]

### Added
- Change LRUDiskCache to allow subclasses to override functionality
- Create new optional request option for customizing local cache behavior
- Allow passing request config to VBase's getJSON method

Expand Down
51 changes: 32 additions & 19 deletions src/caches/LRUDiskCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import { outputJSON, readJSON, remove } from 'fs-extra'
import LRU from 'lru-cache'
import { join } from 'path'
import ReadWriteLock from 'rwlock'
import { LocalCacheOptions } from '../HttpClient'

export type LRUData = Record<string, unknown> & { timeOfDeath: number }

export class LRUDiskCache<V> implements CacheLayer<string, V>{

protected disposed: number
protected hits = 0
protected total = 0
private lock: ReadWriteLock
private disposed: number
private hits = 0
private total = 0
private lruStorage: LRU<string, number>
private lruStorage: LRU<string, LRUData>
private keyToBeDeleted: string

constructor(private cachePath: string, options: LRUDiskCacheOptions, private readFile=readJSON, private writeFile=outputJSON) {
Expand All @@ -33,11 +36,13 @@ export class LRUDiskCache<V> implements CacheLayer<string, V>{
noDisposeOnSet: true,
}

this.lruStorage = new LRU<string, number>(lruOptions)
this.lruStorage = new LRU<string, LRUData>(lruOptions)

}

public has = (key: string): boolean => this.lruStorage.has(key)
public has (key: string): boolean {
Copy link
Contributor Author

@mairatma mairatma Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the has/get/set methods syntax because the previous syntax doesn't work properly when a subclass overrides, losing the access to super.

No changes were made in this one besides the syntax though.

return this.lruStorage.has(key)
}

public getStats = (name='disk-lru-cache'): LRUStats => {
const stats = {
Expand All @@ -56,10 +61,10 @@ export class LRUDiskCache<V> implements CacheLayer<string, V>{
return stats
}

public get = async (key: string): Promise<V | void> => {
const timeOfDeath = this.lruStorage.get(key)
public async get (key: string): Promise<V | void> {
const lruData = this.lruStorage.get(key)
this.total += 1
if (timeOfDeath === undefined) {
if (lruData === undefined) {

// if it is an outdated file when stale=false
if (this.keyToBeDeleted) {
Expand All @@ -85,23 +90,18 @@ export class LRUDiskCache<V> implements CacheLayer<string, V>{
})

// if it is an outdated file when stale=true
if (timeOfDeath < Date.now()) {
if (lruData.timeOfDeath < Date.now()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeOfDeath is now part of the data retrieved from the memomery cache (lruStorage), instead of being the value itself.

this.lruStorage.del(key)
await this.deleteFile(key)
}

return data
}

public set = async (key: string, value: V, maxAge?: number): Promise<boolean> => {
let timeOfDeath = NaN
if (maxAge) {
timeOfDeath = maxAge + Date.now()
this.lruStorage.set(key, timeOfDeath, maxAge)
}
else {
this.lruStorage.set(key, NaN)
}
public async set (key: string, value: V, maxAge?: number, localCacheOptions?: LocalCacheOptions): Promise<boolean> {
const timeOfDeath = maxAge ? maxAge + Date.now() : NaN
const lruData = this.buildLRUData(timeOfDeath, localCacheOptions)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just changed the way we build the data to be stored in the memory cache to use the new buildLRUData function.

this.lruStorage.set(key, lruData, maxAge ? maxAge : undefined)

if (this.keyToBeDeleted && this.keyToBeDeleted !== key) {
await this.deleteFile(this.keyToBeDeleted)
Expand All @@ -124,6 +124,19 @@ export class LRUDiskCache<V> implements CacheLayer<string, V>{
return !failure
}

/**
* Builds the data object that will be stored at the LRU memory storage.
* Subclasses that need to store more than just the time of death should
* override this.
*/
protected buildLRUData(timeOfDeath: number, localCacheOptions?: LocalCacheOptions): LRUData {
return { timeOfDeath }
}
Comment on lines +127 to +134
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data stored in the memory part of the cache was only the timeOfDeath. This new method will allow storing more data there, for any logic that needs to run before deciding to fetch the file from disk.

In our use case this extra data will be the list of fields that we have stored from the original object.


protected getLRU() {
return this.lruStorage
}

private getPathKey = (key: string): string => {
return join(this.cachePath, key)
}
Expand Down
Loading