Skip to content

Commit

Permalink
Merge pull request #47 from akashchouhan16/dev
Browse files Browse the repository at this point in the history
🍁 Cache update for private data members + New Unit Test suite
  • Loading branch information
akashchouhan16 authored Jan 24, 2024
2 parents e0e12aa + 0b2d81c commit 2c3f3f6
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 98 deletions.
104 changes: 61 additions & 43 deletions src/nodecache.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,85 +11,105 @@ let cacheConfig = require("./config/cacheConfig")


class NodeCache {
#logger
#cache
#config
constructor(options = {}) {
this.cache = {}
this.logger = new Logger({ ...options })

let { forceString, maxKeys, stdTTL, valueOnly } = options
cacheConfig.valueOnly = valueOnly !== undefined && typeof valueOnly === "boolean" ? valueOnly : cacheConfig.valueOnly
cacheConfig.forceString = forceString !== undefined && typeof forceString === "boolean" ? forceString : cacheConfig.forceString
cacheConfig.maxKeys = maxKeys !== undefined && typeof maxKeys === "number" && maxKeys > 0 ? maxKeys : cacheConfig.maxKeys
cacheConfig.stdTTL = (stdTTL && typeof stdTTL === "number" && stdTTL >= 0) ? stdTTL : cacheConfig.stdTTL

this.config = { ...cacheConfig }
this.#cache = {}
this.#config = Object.assign({}, cacheConfig);
this.#logger = new Logger({ ...options })
this.#setConfigurations(options);

if (isMainThread) {
this.worker = new Worker(path.join(__dirname, "/worker/worker.js"))
this.worker.on("message", this._onWorkerMessage.bind(this))
this.worker.on("error", (err) => {
this.logger.log(`${err.message}`, { type: "Worker Error" })
this.#logger.log(`${err.message}`, { type: "Worker Error" })
})

this.worker.postMessage({ cache: this.cache })
this.worker.postMessage({ cache: this.#cache })
}

this.#logger.log(`nodeCache.js initialized`)
}

#setConfigurations(options) {
if (options) {
let { forceString, maxKeys, stdTTL, valueOnly } = options;

if (valueOnly !== undefined && typeof valueOnly === "boolean")
this.#config.valueOnly = valueOnly
if (forceString !== undefined && typeof forceString === "boolean")
this.#config.forceString = forceString
if (maxKeys && typeof maxKeys === "number" && maxKeys > 0)
this.#config.maxKeys = maxKeys
if (stdTTL && typeof stdTTL === "number" && stdTTL >= 0)
this.#config.stdTTL = stdTTL
}
}

getLogConfig() {
return this.#logger
}

this.logger.log(`nodeCache.js initialized`)
getCacheConfig() {
return this.#config
}

get(key) {
const cacheItem = this.cache[key]
const cacheItem = this.#cache[key]

if (!cacheItem) {
cacheConfig.cacheMiss += 1
this.logger.log(`${CONSTANTS.ITEM_NOTFOUND} : ${key}`)
this.#config.cacheMiss += 1
this.#logger.log(`${CONSTANTS.ITEM_NOTFOUND} : ${key}`)
return undefined
}
if (cacheItem.ttl && cacheItem.ttl < Date.now()) {
cacheConfig.cacheMiss += 1
if (this.#config.stdTTL != 0 && cacheItem.ttl && cacheItem.ttl < Date.now()) {
this.#config.cacheMiss += 1
// update the context of cache in the worker thread.
this.worker.postMessage({ cache: this.cache })
this.worker.postMessage({ cache: this.#cache })
//passive ttl expire - fallback for env not supporting worker threads
this.delete(key)
this.logger.log(`${CONSTANTS.ITEM_NOTFOUND} : ${key}`)
this.#logger.log(`${CONSTANTS.ITEM_NOTFOUND} : ${key}`)
return undefined
}

cacheConfig.cacheHit += 1
if (cacheConfig.valueOnly) {
this.#config.cacheHit += 1
if (this.#config.valueOnly) {
return cacheItem.value
}
return cacheItem
}

set(key, value, ttl) {
if (!key && !value) {
this.logger.log(CONSTANTS.INVALID_KEY_VALUE)
this.#logger.log(CONSTANTS.INVALID_KEY_VALUE)
return false
} else if (!key || !["string", "number"].includes(typeof key)) {
this.logger.log(CONSTANTS.INVALID_KEY_TYPE)
this.#logger.log(CONSTANTS.INVALID_KEY_TYPE)
return false
}
else if (!value || !["string", "number", "object"].includes(typeof value)) {
this.logger.log(CONSTANTS.INVALID_VALUE_TYPE)
this.#logger.log(CONSTANTS.INVALID_VALUE_TYPE)
return false
}

if (cacheConfig.maxKeys > 0 && cacheConfig.maxKeys <= Object.keys(this.cache).length) {
if (this.#config.maxKeys > 0 && this.#config.maxKeys <= Object.keys(this.#cache).length) {
throw new Error(CONSTANTS.MAX_CACHE_LIMIT)
}

if (cacheConfig.forceString && typeof value !== "string") {
if (this.#config.forceString && typeof value !== "string") {
value = JSON.stringify(value)
}

if (ttl && (typeof ttl !== "number")) {
throw new Error(CONSTANTS.INVALID_TTL_TYPE)
}

this.cache[key] = { value, ttl: ttl ? Date.now() + Math.abs(ttl) : Date.now() + cacheConfig.stdTTL }
cacheConfig.keyCount += 1
this.#cache[key] = { value, ttl: ttl ? Date.now() + Math.abs(ttl) : Date.now() + this.#config.stdTTL }
this.#config.keyCount += 1
// update the context of cache in the worker thread.
// this.worker.postMessage({ cache: this.cache, logger: this.logger, action: "set" })
// this.worker.postMessage({ cache: this.#cache, logger: this.#logger, action: "set" })
return true
}

Expand All @@ -113,7 +133,7 @@ class NodeCache {
return [false]
}

if (cacheConfig.maxKeys > 0 && cacheConfig.maxKeys <= values.length) {
if (this.#config.maxKeys > 0 && this.#config.maxKeys <= values.length) {
throw new Error(CONSTANTS.MAX_CACHE_LIMIT)
}

Expand All @@ -130,7 +150,7 @@ class NodeCache {
if (!key || (!["string", "number"].includes(typeof key))) {
throw new Error(CONSTANTS.INVALID_KEY_TYPE)
}
let item = this.cache[key]
let item = this.#cache[key]
return item ? item.ttl : undefined
}

Expand All @@ -143,24 +163,24 @@ class NodeCache {
throw new Error(CONSTANTS.INVALID_TTL_TYPE)
}

if (!this.cache[key])
if (!this.#cache[key])
return false

const newTTL = Date.now() + Math.abs(ttl)
this.cache[key].ttl = newTTL
this.#cache[key].ttl = newTTL
return true
}

async refresh() {
return new Promise((resolve, reject) => {
try {
const now = Date.now()
for (const [key, cacheItem] of Object.entries(this.cache)) {
for (const [key, cacheItem] of Object.entries(this.#cache)) {
if (cacheItem.ttl && Number(cacheItem.ttl) < Number(now)) {
this.delete(key)
}
}
resolve(this.cache)
resolve(this.#cache)
} catch (error) {
reject(new Error(`Refresh failed to update cache: ${error.message}`))
}
Expand All @@ -169,7 +189,7 @@ class NodeCache {
}

global() {
const { cacheHit, cacheMiss, keyCount } = cacheConfig
const { cacheHit, cacheMiss, keyCount } = this.#config
return {
cacheHit,
cacheMiss,
Expand All @@ -178,15 +198,13 @@ class NodeCache {
}

flush() {
cacheConfig.cacheHit = 0
cacheConfig.cacheMiss = 0
cacheConfig.keyCount = 0
this.cache = {}
this.#config = cacheConfig;
this.#cache = {}
}

delete(key) {
delete this.cache[key]
cacheConfig.keyCount -= 1
delete this.#cache[key]
this.#config.keyCount -= 1
}

_onWorkerMessage({ key }) {
Expand Down
Loading

0 comments on commit 2c3f3f6

Please sign in to comment.