From 96943f51a322abc306ed9db1b9bb792179881335 Mon Sep 17 00:00:00 2001 From: nhan Date: Wed, 10 Feb 2021 19:20:09 -0800 Subject: [PATCH 1/6] small cleanup on destructure file --- src/destructure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/destructure.js b/src/destructure.js index 9242124..3ad4582 100644 --- a/src/destructure.js +++ b/src/destructure.js @@ -4,7 +4,7 @@ * 2. We won't worry about arguments on fields for now * 3. We won't worry about aliases for now * 4. We won't worry about handling directives for now - * 5. We wont' worry about fragments for now + * 5. We won't worry about fragments for now * 6. This function will assume that everything passed in can be a query or a mutation (not both). * 7. We won't handle variables for now, but we may very well find we need to * 8. We will handle only the meta field "__typename" for now From 6342d7636f2f640e0b74cbc0f79c48e7e3312599 Mon Sep 17 00:00:00 2001 From: gealarcon Date: Mon, 29 Mar 2021 19:30:25 -0700 Subject: [PATCH 2/6] configured redis garbage collect --- src/CacheClassServer.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/CacheClassServer.js b/src/CacheClassServer.js index bcc03c4..abcdefb 100644 --- a/src/CacheClassServer.js +++ b/src/CacheClassServer.js @@ -112,7 +112,7 @@ export class Cache { this.storage[hash] = value; } else { value = JSON.stringify(value); - await redis.setex(hash, 30, value); + await redis.setex(hash, 6000, value); let hashedQuery = await redis.get(hash); } } @@ -164,6 +164,8 @@ export class Cache { return allHashesFromQuery.reduce(async (acc, hash) => { // for each hash from the input query, build the response object const readVal = await this.cacheRead(hash); + // return undefine if hash has been garbage collected + if (readVal === undefined) return undefined if (readVal === 'DELETED') return acc; const dataObj = {}; for (const field in fields) { @@ -185,11 +187,18 @@ export class Cache { if (dataObj[field] === undefined) return undefined; } } - // acc is an array of response object for each hash - const resolvedProm = await Promise.resolve(acc); - resolvedProm.push(dataObj); - return resolvedProm; + // acc is an array within a Response object for each hash + try{ + const resolvedProm = await Promise.resolve(acc) + resolvedProm.push(dataObj); + return resolvedProm; + } catch (error){ + return undefined + } + }, []); + + } // Case where allHashesFromQuery has only one hash and is not an array but a single string const hash = allHashesFromQuery; From 7cafb6e40e2511d943910e79954745edf0b4b300 Mon Sep 17 00:00:00 2001 From: nhan Date: Tue, 30 Mar 2021 18:22:04 -0700 Subject: [PATCH 3/6] begin work on lfu cache --- documentation/garbage-collection-doc.js | 5 +- src/CacheClassBrowser.js | 84 ++- src/CacheClassServer.js | 129 ++++- src/obsidian.ts | 3 +- src/possible-gc-algo.js | 496 ++++++++++++++++++ src/possible-lfu-cache-algo.js | 0 src/possible-lru-cache-algo.js | 43 ++ .../garbage_collection_test.ts | 61 +++ test_files/rhum_test_files/readCache_test.ts | 2 +- .../garbage_collection_variables.ts | 95 ++++ 10 files changed, 907 insertions(+), 11 deletions(-) create mode 100644 src/possible-gc-algo.js create mode 100644 src/possible-lfu-cache-algo.js create mode 100644 src/possible-lru-cache-algo.js create mode 100644 test_files/rhum_test_files/garbage_collection_test.ts create mode 100644 test_files/test_variables/garbage_collection_variables.ts diff --git a/documentation/garbage-collection-doc.js b/documentation/garbage-collection-doc.js index 35f8a24..b3dad76 100644 --- a/documentation/garbage-collection-doc.js +++ b/documentation/garbage-collection-doc.js @@ -22,7 +22,7 @@ class Cache { * - remove any hash reference that is a member of the deleted hash Set * - for any hash reference that has not been deleted * - add that hash to a Set of accessible hashes - * - recrusively trace that hash and continue removing any deleted hash references and updating the Set of accesible hashes + * - recursively trace that hash and continue removing any deleted hash references and updating the Set of accesible hashes * 4. remove any hashes that are not a member of the accessible hash Set */ @@ -85,7 +85,8 @@ const cacheAfterGC = { 'favoriteMovie(id:2)': 'Movie~2', "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', - 'deleteMovie(id:4)': 'Movie~4', + // 'deleteMovie(id:4)': 'Movie~4', // mistake? + 'deleteMovie(id:3)': 'Movie~3', }, 'Movie~1': { diff --git a/src/CacheClassBrowser.js b/src/CacheClassBrowser.js index 10ef4c1..c73f01f 100644 --- a/src/CacheClassBrowser.js +++ b/src/CacheClassBrowser.js @@ -44,7 +44,9 @@ export default class Cache { return undefined; } } - return { data: responseObject }; + return { + data: responseObject + }; } async write(queryStr, respObj, deleteFlag) { @@ -66,8 +68,77 @@ export default class Cache { gc() { // garbageCollection; garbage collection: removes any inaccessible hashes from the cache + const badHashes = getBadHashes(); + const goodHashes = rootQueryCleaner(badHashes); + const goodHashes2 = getGoodHashes(badHashes, goodHashes); + removeInaccessibleHashes(badHashes, goodHashes2); } + // remove hashes that are flagged for deletion and store records of them in a set badHashes for removal inside root queries + getBadHashes() { + const badHashes = new Set(); + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (this.storage[key] === 'DELETED') { + badHashes.add(key); + delete this.storage[key]; + } + } + return badHashes; + } + + // go through root queries, remove all instances of bad hashes, add remaining hashes into goodHashes set + rootQueryCleaner(badHashes) { + const goodHashes = new Set(); + const rootQuery = this.storage['ROOT_QUERY']; + for (let key in rootQuery) { + if (Array.isArray(rootQuery[key])) { + rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); + if (rootQuery[key].length === 0) delete rootQuery[key]; + for (let el of rootQuery[key]) goodHashes.add(el); + } else (badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); + } + return goodHashes; + } + + // Go through the cache, check good hashes for any nested hashes and add them to goodHashes set + getGoodHashes(badHashes, goodHashes) { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + for (let el of this.storage[key][i]) { + if (el.includes('~') && !badHashes.has(el)) { + goodHashes.add(el); + } + } + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && !badHashes.has(this.storage[key][i])) { + goodHashes.add(this.storage[key][i]); + } + } + } + } + return goodHashes; + } + + // Remove inaccessible hashes by checking if they are in goodhashes set or not + removeInaccessibleHashes(badHashes, goodHashes) { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (!goodHashes.has(key)) delete this.storage[key]; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + this.storage[key][i] = this.storage[key][i].filter(x => !badHashes.has(x)); + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && badHashes.has(this.storage[key][i])) { + delete this.storage[key][i]; + } + } + } + } + } + // cache read/write helper methods async cacheRead(hash) { return this.storage[hash]; @@ -82,7 +153,10 @@ export default class Cache { } async cacheClear() { - this.storage = { ROOT_QUERY: {}, ROOT_MUTATION: {} }; + this.storage = { + ROOT_QUERY: {}, + ROOT_MUTATION: {} + }; } // functionality to stop polling @@ -99,7 +173,9 @@ export default class Cache { readWholeQuery(queryStr) { const hash = queryStr.replace(/\s/g, ''); const root = this.cacheRead('ROOT_QUERY'); - if (root[hash]) return { data: root[hash] }; + if (root[hash]) return { + data: root[hash] + }; return undefined; } @@ -170,4 +246,4 @@ export default class Cache { return dataObj; } } -} +} \ No newline at end of file diff --git a/src/CacheClassServer.js b/src/CacheClassServer.js index bcc03c4..1dead89 100644 --- a/src/CacheClassServer.js +++ b/src/CacheClassServer.js @@ -17,15 +17,69 @@ console.log(await redis.ping()); export class Cache { constructor( + // initialCache = { + // ROOT_QUERY: {}, + // ROOT_MUTATION: {}, + // } initialCache = { - ROOT_QUERY: {}, + ROOT_QUERY: { + 'actor(id:1)': 'Actor~1', + movies: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], + actors: ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4', 'Actor~6'], + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~4', 'Movie~5'], + }, ROOT_MUTATION: {}, + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: ['Actor~1', 'Actor~2', 'Actor~6', 'Actor~7'], + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + }, + 'Movie~3': { + id: '3', + title: 'Witness', + actors: ['Actor~1', 'Actor~4'], + releaseYear: 1985, + }, + 'Movie~4': { + id: '4', + title: 'Air Force One', + actors: ['Actor~1', 'Actor~5'], + genre: 'ACTION', + releaseYear: 1997, + }, + 'Movie~5': 'DELETED', + 'Actor~1': { id: '1', firstName: 'Harrison' }, + 'Actor~2': { id: '2', firstName: 'Sean' }, + 'Actor~3': { id: '3', firstName: 'Mark' }, + 'Actor~4': { id: '4', firstName: 'Patti' }, + 'Actor~5': { id: '5', firstName: 'Gary' }, + 'Actor~6': 'DELETED', + 'Actor~7': { id: '7', firstName: 'Christy' } } ) { this.storage = initialCache; this.context = window.Deno ? 'server' : 'client'; } + insertIntoRedis() { + // console.log('i am called'); + for (let key in this.storage) { + redis.set(key, JSON.stringify(this.storage[key])); + } + } + + pullOutCache() { + + } + // Main functionality methods async read(queryStr) { if (typeof queryStr !== 'string') @@ -81,8 +135,77 @@ export class Cache { gc() { // garbageCollection; garbage collection: removes any inaccessible hashes from the cache + const badHashes = getBadHashes(); + const goodHashes = rootQueryCleaner(badHashes); + const goodHashes2 = getGoodHashes(badHashes, goodHashes); + removeInaccessibleHashes(badHashes, goodHashes2); + } + + // remove hashes that are flagged for deletion and store records of them in a set badHashes for removal inside root queries + getBadHashes() { + const badHashes = new Set(); + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (this.storage[key] === 'DELETED') { + badHashes.add(key); + delete this.storage[key]; + } + } + return badHashes; } + // go through root queries, remove all instances of bad hashes, add remaining hashes into goodHashes set + rootQueryCleaner(badHashes) { + const goodHashes = new Set(); + const rootQuery = this.storage['ROOT_QUERY']; + for (let key in rootQuery) { + if (Array.isArray(rootQuery[key])) { + rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); + if (rootQuery[key].length === 0) delete rootQuery[key]; + for (let el of rootQuery[key]) goodHashes.add(el); + } else (badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); + } + return goodHashes; + } + + // Go through the cache, check good hashes for any nested hashes and add them to goodHashes set + getGoodHashes(badHashes, goodHashes) { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + for (let el of this.storage[key][i]) { + if (el.includes('~') && !badHashes.has(el)) { + goodHashes.add(el); + } + } + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && !badHashes.has(this.storage[key][i])) { + goodHashes.add(this.storage[key][i]); + } + } + } + } + return goodHashes; + } + + // Remove inaccessible hashes by checking if they are in goodhashes set or not + removeInaccessibleHashes(badHashes, goodHashes) { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (!goodHashes.has(key)) delete this.storage[key]; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + this.storage[key][i] = this.storage[key][i].filter(x => !badHashes.has(x)); + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && badHashes.has(this.storage[key][i])) { + delete this.storage[key][i]; + } + } + } + } + } + // cache read/write helper methods async cacheRead(hash) { // returns value from either object cache or cache || 'DELETED' || undefined @@ -112,7 +235,7 @@ export class Cache { this.storage[hash] = value; } else { value = JSON.stringify(value); - await redis.setex(hash, 30, value); + await redis.set(hash, value); let hashedQuery = await redis.get(hash); } } @@ -219,4 +342,4 @@ export class Cache { return dataObj; } } -} +} \ No newline at end of file diff --git a/src/obsidian.ts b/src/obsidian.ts index 080fadc..70cad3d 100644 --- a/src/obsidian.ts +++ b/src/obsidian.ts @@ -47,9 +47,10 @@ export async function ObsidianRouter({ const schema = makeExecutableSchema({ typeDefs, resolvers }); const cache = new Cache(); + cache.insertIntoRedis(); // clear redis cache when restarting the server - cache.cacheClear(); + // cache.cacheClear(); await router.post(path, async (ctx: any) => { const { response, request } = ctx; diff --git a/src/possible-gc-algo.js b/src/possible-gc-algo.js new file mode 100644 index 0000000..979b221 --- /dev/null +++ b/src/possible-gc-algo.js @@ -0,0 +1,496 @@ +const { + performance +} = require('perf_hooks'); + +const gc1 = (object) => { + this.storage = object; + const badSet = new Set(); + // const goodSet = new Set(); + + for (let key in this.storage) { + if (this.storage[key] === 'DELETED') { + badSet.add(key); + delete this.storage[key]; + } + // else goodSet.add(key); + } + console.log(badSet); + // console.log(goodSet); + + // helper function to remove bad hashes + const helper = () => { + // examine the root query first + const rootQuery = this.storage['ROOT_QUERY']; + // iterate through each key in root query + for (let query in rootQuery) { + let hash = rootQuery[query]; + // console.log(hash); + // if the hash is an array + if (Array.isArray(hash)) { + // filter the hash array, removing any items that are in badSet + hash = hash.filter(item => !badSet.has(item)); + rootQuery[query] = hash; + // console.log(hash); + // if the hash array is empty, delete query + if (hash.length === 0) { + delete rootQuery[query]; + } + } // if the hash is a string + else { + if (badSet.has(hash)) { + delete rootQuery[query]; + } + } + } + + // examine the rest of the cache except for root mutation? + for (let hash in this.storage) { + if (hash === 'ROOT_MUTATION') continue; + + for (let subHash in hash) { + if (Array.isArray(hash[subHash])) { + hash[subHash] = hash[subHash].filter(item => !badSet.has(item)); + console.log(hash[subHash]); + if (hash[subHash].length === 0) { + delete hash[subHash]; + } + } else { + if (badSet.has(hash[subHash])) { + delete subHash; + } + } + } + } + } + + helper(); + // return this.storage; +} // don't use + +const gc2 = (object) => { + this.storage = object; + const badHashes = new Set(); + const goodHashes = new Set(); + + // go through cache, excluding root query and mutation + // add hashes to either bad or good sets + // a hash is "bad" if it's marked for deletion + const helper1 = () => { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (this.storage[key] === 'DELETED') { + badHashes.add(key); + delete this.storage[key]; + } + } + } + + // go through root query, remove any instances of bad hashes + const helper2 = () => { + const rootQuery = this.storage['ROOT_QUERY']; + for (let key in rootQuery) { + if (Array.isArray(rootQuery[key])) { + rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); + if (rootQuery[key].length === 0) delete rootQuery[key]; + for (let el of rootQuery[key]) goodHashes.add(el); + } else(badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); + } + } + + // helper function to go through the good hashes, see if they have sub hashes + // and add those to good hashes set + const helper3 = () => { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + for (let el of this.storage[key][i]) { + if (el.includes('~') && !badHashes.has(el)) { + goodHashes.add(el); + } + } + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && !badHashes.has(this.storage[key][i])) { + goodHashes.add(this.storage[key][i]); + } + } + } + } + } + + // helper function to remove inaccessible hashes if they are not in goodhashes set + const helper4 = () => { + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (!goodHashes.has(key)) delete this.storage[key]; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + this.storage[key][i] = this.storage[key][i].filter(x => !badHashes.has(x)); + } else if (typeof this.storage[key][i] === 'string') { + if (this.storage[key][i].includes('~') && badHashes.has(this.storage[key][i])) { + delete this.storage[key][i]; + } + } + } + } + } + + let t0_h1 = performance.now(); + helper1(); + let t1_h1 = performance.now(); + + let t0_h2 = performance.now(); + helper2(); + let t1_h2 = performance.now(); + + let t0_h3 = performance.now(); + helper3(); + let t1_h3 = performance.now(); + + let t0_h4 = performance.now(); + helper4(); + let t1_h4 = performance.now(); + + // console.log('Time for helper1: ', t1_h1 - t0_h1); + // console.log('Time for helper2: ', t1_h2 - t0_h2); + // console.log('Time for helper3: ', t1_h3 - t0_h3); + // console.log('Time for helper4: ', t1_h4 - t0_h4); + // console.log('Total time: ', t1_h4 - t0_h1); + + console.log(badHashes); + console.log(goodHashes); + return this.storage; +} + +const test_1 = { + ROOT_QUERY: { + 'actor(id:1)': 'Actor~1', + favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + ROOT_MUTATION: { + 'favoriteMovie(id:2)': 'Movie~2', + "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', + 'deleteMovie(id:3)': 'Movie~3', + }, + + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: 'Actor~2', + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + isFavorite: true, + }, + // DELETED + 'Movie~3': 'DELETED', + 'Movie~5': { + id: '5', + title: 'The Fugitive', + genre: 'ACTION', + releaseYear: 1993, + isFavorite: false, + }, + 'Actor~1': { + id: '1', + firstName: 'Harrison', + lastName: 'Ford', + films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + 'Actor~2': { + id: '2', + firstName: 'Sean', + lastName: 'Connery' + }, + 'Actor~3': { + id: '3', + firstName: 'Mark', + lastName: 'Hamill' + }, + 'Actor~4': { + id: '4', + firstName: 'Patti', + lastName: 'LuPone' + }, // INACCESSIBLE + 'Actor~5': { + id: '5', + firstName: 'Gary', + lastName: 'Oldman' + }, // INACCESSIBLE +}; + +const test_2 = { + ROOT_QUERY: { + 'actor(id:4)': 'Actor~4', + 'actor(id:1)': 'Actor~1', + favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + ROOT_MUTATION: { + 'favoriteMovie(id:2)': 'Movie~2', + "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', + 'deleteMovie(id:3)': 'Movie~3', + }, + + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: 'Actor~2', + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + isFavorite: true, + }, + // DELETED + 'Movie~3': 'DELETED', + 'Movie~5': 'DELETED', + 'Actor~1': { + id: '1', + firstName: 'Harrison', + lastName: 'Ford', + films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + 'Actor~2': { + id: '2', + firstName: 'Sean', + lastName: 'Connery' + }, + 'Actor~3': { + id: '3', + firstName: 'Mark', + lastName: 'Hamill' + }, + 'Actor~4': { + id: '4', + firstName: 'Patti', + lastName: 'LuPone', + }, + 'Actor~5': 'DELETED', // DELETED +}; + +const test_3 = { + ROOT_QUERY: { + // 'actor(id:4)': 'Actor~4', + 'actor(id:1)': 'Actor~1', + favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + ROOT_MUTATION: { + 'favoriteMovie(id:2)': 'Movie~2', + "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', + 'deleteMovie(id:3)': 'Movie~3', + }, + + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: 'Actor~2', + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + isFavorite: true, + }, + // DELETED + 'Movie~3': 'DELETED', + 'Movie~5': 'DELETED', + 'Movie~6': { + id: '6', + title: 'Whatever' + }, // INACCESSIBLE IF ACTOR 4 IS DELETED + 'Actor~1': { + id: '1', + firstName: 'Harrison', + lastName: 'Ford', + films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + 'Actor~2': { + id: '2', + firstName: 'Sean', + lastName: 'Connery' + }, + 'Actor~3': { + id: '3', + firstName: 'Mark', + lastName: 'Hamill' + }, + 'Actor~4': { + id: '4', + firstName: 'Patti', + lastName: 'LuPone', + films: 'Movie~6' + }, // INACCESSIBLE + 'Actor~5': 'DELETED', // DELETED +}; + +// let test1 = gc2(test_1); +// console.log(test1); +// let test2 = gc2(test_2); +// console.log(test2); +// let test3 = gc2(test_3); +// console.log(test3); +// let test4 = gc2(test_3); +// console.log(test4); + +/*--------------------------------------------------------------*/ + +function gc() { + // garbageCollection; garbage collection: removes any inaccessible hashes from the cache + const badHashes = getBadHashes(); + const goodHashes = rootQueryCleaner(badHashes); + const goodHashes2 = getGoodHashes(badHashes, goodHashes); + removeInaccessibleHashes(badHashes, goodHashes2); +} + +// remove hashes that are flagged for deletion and store records of them in a set badHashes for removal inside root queries +function getBadHashes() { + const badHashes = new Set(); + for (let key in storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (storage[key] === 'DELETED') { + badHashes.add(key); + delete storage[key]; + } + } + return badHashes; +} + +// go through root queries, remove all instances of bad hashes, add remaining hashes into goodHashes set +function rootQueryCleaner(badHashes) { + const goodHashes = new Set(); + const rootQuery = storage['ROOT_QUERY']; + for (let key in rootQuery) { + if (Array.isArray(rootQuery[key])) { + rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); + if (rootQuery[key].length === 0) delete rootQuery[key]; + for (let el of rootQuery[key]) goodHashes.add(el); + } else(badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); + } + return goodHashes; +} + +// Go through the cache, check good hashes for any nested hashes and add them to goodHashes set +function getGoodHashes(badHashes, goodHashes) { + for (let key in storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + for (let i in storage[key]) { + if (Array.isArray(storage[key][i])) { + for (let el of storage[key][i]) { + if (el.includes('~') && !badHashes.has(el)) { + goodHashes.add(el); + } + } + } else if (typeof storage[key][i] === 'string') { + if (storage[key][i].includes('~') && !badHashes.has(storage[key][i])) { + goodHashes.add(storage[key][i]); + } + } + } + } + return goodHashes; +} + +// Remove inaccessible hashes by checking if they are in goodhashes set or not +function removeInaccessibleHashes(badHashes, goodHashes) { + for (let key in storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (!goodHashes.has(key)) delete storage[key]; + for (let i in storage[key]) { + if (Array.isArray(storage[key][i])) { + storage[key][i] = storage[key][i].filter(x => !badHashes.has(x)); + } else if (typeof storage[key][i] === 'string') { + if (storage[key][i].includes('~') && badHashes.has(storage[key][i])) { + delete storage[key][i]; + } + } + } + } + return storage; +} + +const storage = { + ROOT_QUERY: { + 'actor(id:1)': 'Actor~1', + favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + ROOT_MUTATION: { + 'favoriteMovie(id:2)': 'Movie~2', + "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', + 'deleteMovie(id:3)': 'Movie~3', + }, + + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: 'Actor~2', + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + isFavorite: true, + }, + // DELETED + 'Movie~3': 'DELETED', + 'Movie~5': { + id: '5', + title: 'The Fugitive', + genre: 'ACTION', + releaseYear: 1993, + isFavorite: false, + }, + 'Actor~1': { + id: '1', + firstName: 'Harrison', + lastName: 'Ford', + films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash + }, + 'Actor~2': { + id: '2', + firstName: 'Sean', + lastName: 'Connery' + }, + 'Actor~3': { + id: '3', + firstName: 'Mark', + lastName: 'Hamill' + }, + 'Actor~4': { + id: '4', + firstName: 'Patti', + lastName: 'LuPone' + }, // INACCESSIBLE + 'Actor~5': { + id: '5', + firstName: 'Gary', + lastName: 'Oldman' + }, // INACCESSIBLE +}; + +let getBadHashesTest = getBadHashes(test_1); +// console.log(getBadHashesTest); +let rootQueryCleanerTest = rootQueryCleaner(getBadHashesTest); +// console.log(rootQueryCleanerTest); +let getGoodHashesTest= getGoodHashes(getBadHashesTest,rootQueryCleanerTest); +// console.log(getGoodHashesTest); +let removeInaccessibleHashesTest = removeInaccessibleHashes(getBadHashesTest, getGoodHashesTest); +// console.log(removeInaccessibleHashesTest); \ No newline at end of file diff --git a/src/possible-lfu-cache-algo.js b/src/possible-lfu-cache-algo.js new file mode 100644 index 0000000..e69de29 diff --git a/src/possible-lru-cache-algo.js b/src/possible-lru-cache-algo.js new file mode 100644 index 0000000..a5b7849 --- /dev/null +++ b/src/possible-lru-cache-algo.js @@ -0,0 +1,43 @@ +class LRUCache { + constructor(capacity) { + this.cache = new Map(); + this.capacity = capacity; + } + + get(key) { + if (this.cache.has(key)) { + let value = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, value); + return value; + } else { + console.log('key not found'); + return null; + } + } + + put(key, value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } + this.cache.set(key, value); + if (this.cache.size >= this.capacity) { + let LRUKey = this.cache.keys().next().value; + this.cache.delete(LRUKey); + } + } +} + +let testCache = new LRUCache(7); +console.log(testCache); + +testCache.put('Item~1', 'Value~1'); +testCache.put('Item~2', 'Value~2'); +testCache.put('Item~3', 'Value~3'); +testCache.put('Item~4', 'Value~4'); +testCache.put('Item~5', 'Value~5'); +testCache.put('Item~6', 'Value~6'); +console.log(testCache); +testCache.put('Item~7', 'Value~7'); +//item 1 is evicted +console.log(testCache); diff --git a/test_files/rhum_test_files/garbage_collection_test.ts b/test_files/rhum_test_files/garbage_collection_test.ts new file mode 100644 index 0000000..7ebf0a1 --- /dev/null +++ b/test_files/rhum_test_files/garbage_collection_test.ts @@ -0,0 +1,61 @@ +import { Rhum } from 'https://deno.land/x/rhum@v1.1.4/mod.ts'; +import Cache from '../../src/CacheClassBrowser.js'; +import { test } from '../test_variables/garbage_collection_variables.ts'; + +Rhum.testPlan('CacheClassBrowser garbage collection', () => { + Rhum.testSuite('getBadHashes()', () => { + Rhum.testCase( + 'should return a badHashes set that contains all the hashes that are flagged for deletion', + async () => { + const cache = new Cache(test.cache); + const result = Array.from(cache.getBadHashes()); + Rhum.asserts.assertEquals(result, test.badHashesSet); + } + ); + }); + Rhum.testSuite('rootQueryCleaner()', () => { + Rhum.testCase( + 'should return (partial) goodHashes set from the root queries after removing bad hashes', + async () => { + const cache = new Cache(test.cache); + const badHashes = new Set(test.badHashesSet); + const result = Array.from(cache.rootQueryCleaner(badHashes)).sort(); + Rhum.asserts.assertEquals(result, test.goodHashesSet.sort()); + } + ); + Rhum.testCase( + 'should clean up the root queries by removing bad hashes', + async () => { + const cache = new Cache(test.cache); + const badHashes = new Set(test.badHashesSet); + cache.rootQueryCleaner(badHashes); + Rhum.asserts.assertEquals(cache.storage.ROOT_QUERY, test.cleanedRootQuery); + } + ) + }); + Rhum.testSuite('getGoodHashes()', () => { + Rhum.testCase( + 'should return (complete) goodHashes set after checking goodHashes for nested hashes and adding them to the goodHashes set', + async () => { + const cache = new Cache(test.cache); + const badHashes = new Set(test.badHashesSet); + const goodHashes = new Set(test.goodHashesSet); + const result = Array.from(cache.getGoodHashes(badHashes, goodHashes)).sort(); + Rhum.asserts.assertEquals(result, test.getGoodHashes.sort()); + } + ); + }); + Rhum.testSuite('removeInaccessibleHashes()', () => { + Rhum.testCase( + 'should remove inaccessible hashes from cache', + async () => { + const cache = new Cache(test.cache); + const badHashes = new Set(test.badHashesSet); + const goodHashes = new Set(test.getGoodHashes); + cache.removeInaccessibleHashes(badHashes, goodHashes); + Rhum.asserts.assertEquals(cache.storage, test.removeInaccessibleHashes); + } + ); + }); + }); + Rhum.run(); \ No newline at end of file diff --git a/test_files/rhum_test_files/readCache_test.ts b/test_files/rhum_test_files/readCache_test.ts index 6d84431..aca925f 100644 --- a/test_files/rhum_test_files/readCache_test.ts +++ b/test_files/rhum_test_files/readCache_test.ts @@ -2,7 +2,7 @@ * NOTES: * 1.This file will test the read method on the Cache class functionalities: * Should return a graphql response object if all required values are found in the cache. - * Should return undefined if any field is missing value in the cache. + * Should return u;ndefined if any field is missing value in the cache. * Should accept multiple queries in one query operation. * Should ignore the elements with a 'DELETE' value and not throw a cache miss if asked for in the query string * 2. This file will test populateAllHashes functionalities: diff --git a/test_files/test_variables/garbage_collection_variables.ts b/test_files/test_variables/garbage_collection_variables.ts new file mode 100644 index 0000000..68dd375 --- /dev/null +++ b/test_files/test_variables/garbage_collection_variables.ts @@ -0,0 +1,95 @@ +export const test = { + cache: { + ROOT_QUERY: { + 'actor(id:1)': 'Actor~1', + movies: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], + actors: ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4', 'Actor~6'], + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~4', 'Movie~5'], + }, + ROOT_MUTATION: {}, + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: ['Actor~1', 'Actor~2', 'Actor~6', 'Actor~7'], + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + }, + 'Movie~3': { + id: '3', + title: 'Witness', + actors: ['Actor~1', 'Actor~4'], + releaseYear: 1985, + }, + 'Movie~4': { + id: '4', + title: 'Air Force One', + actors: ['Actor~1', 'Actor~5'], + genre: 'ACTION', + releaseYear: 1997, + }, + 'Movie~5': 'DELETED', + 'Actor~1': { id: '1', firstName: 'Harrison' }, + 'Actor~2': { id: '2', firstName: 'Sean' }, + 'Actor~3': { id: '3', firstName: 'Mark' }, + 'Actor~4': { id: '4', firstName: 'Patti' }, + 'Actor~5': { id: '5', firstName: 'Gary' }, + 'Actor~6': 'DELETED', + 'Actor~7': { id: '7', firstName: 'Christy' } + }, + 'badHashesSet': ['Movie~5', 'Actor~6'], + 'goodHashesSet': ['Actor~1', 'Movie~1', 'Movie~2', 'Movie~3', 'Movie~4', 'Actor~2', 'Actor~3', 'Actor~4'], + 'cleanedRootQuery': { + 'actor(id:1)': 'Actor~1', + movies: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], + actors: ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4'], + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~4'], + }, + 'getGoodHashes': ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4', 'Actor~5', 'Actor~7', 'Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], + 'removeInaccessibleHashes': { + ROOT_QUERY: { + 'actor(id:1)': 'Actor~1', + movies: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], + actors: ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4'], + 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~4'], + }, + ROOT_MUTATION: {}, + 'Movie~1': { + id: '1', + title: 'Indiana Jones and the Last Crusade', + actors: ['Actor~1', 'Actor~2', 'Actor~7'], + genre: 'ACTION', + releaseYear: 1989, + }, + 'Movie~2': { + id: '2', + title: 'Empire Strikes Back', + actors: ['Actor~1', 'Actor~3'], + releaseYear: 1980, + }, + 'Movie~3': { + id: '3', + title: 'Witness', + actors: ['Actor~1', 'Actor~4'], + releaseYear: 1985, + }, + 'Movie~4': { + id: '4', + title: 'Air Force One', + actors: ['Actor~1', 'Actor~5'], + genre: 'ACTION', + releaseYear: 1997, + }, + 'Actor~1': { id: '1', firstName: 'Harrison' }, + 'Actor~2': { id: '2', firstName: 'Sean' }, + 'Actor~3': { id: '3', firstName: 'Mark' }, + 'Actor~4': { id: '4', firstName: 'Patti' }, + 'Actor~5': { id: '5', firstName: 'Gary' }, + 'Actor~7': { id: '7', firstName: 'Christy' } + } +}; From fce9395f1ca19a9ae20d5bf733edb0e84e2ff5fa Mon Sep 17 00:00:00 2001 From: nhan Date: Wed, 31 Mar 2021 17:53:45 -0700 Subject: [PATCH 4/6] initial garbage collection ready for PR --- src/possible-gc-algo.js | 496 --------------------------------- src/possible-lfu-cache-algo.js | 0 src/possible-lru-cache-algo.js | 43 --- 3 files changed, 539 deletions(-) delete mode 100644 src/possible-gc-algo.js delete mode 100644 src/possible-lfu-cache-algo.js delete mode 100644 src/possible-lru-cache-algo.js diff --git a/src/possible-gc-algo.js b/src/possible-gc-algo.js deleted file mode 100644 index 979b221..0000000 --- a/src/possible-gc-algo.js +++ /dev/null @@ -1,496 +0,0 @@ -const { - performance -} = require('perf_hooks'); - -const gc1 = (object) => { - this.storage = object; - const badSet = new Set(); - // const goodSet = new Set(); - - for (let key in this.storage) { - if (this.storage[key] === 'DELETED') { - badSet.add(key); - delete this.storage[key]; - } - // else goodSet.add(key); - } - console.log(badSet); - // console.log(goodSet); - - // helper function to remove bad hashes - const helper = () => { - // examine the root query first - const rootQuery = this.storage['ROOT_QUERY']; - // iterate through each key in root query - for (let query in rootQuery) { - let hash = rootQuery[query]; - // console.log(hash); - // if the hash is an array - if (Array.isArray(hash)) { - // filter the hash array, removing any items that are in badSet - hash = hash.filter(item => !badSet.has(item)); - rootQuery[query] = hash; - // console.log(hash); - // if the hash array is empty, delete query - if (hash.length === 0) { - delete rootQuery[query]; - } - } // if the hash is a string - else { - if (badSet.has(hash)) { - delete rootQuery[query]; - } - } - } - - // examine the rest of the cache except for root mutation? - for (let hash in this.storage) { - if (hash === 'ROOT_MUTATION') continue; - - for (let subHash in hash) { - if (Array.isArray(hash[subHash])) { - hash[subHash] = hash[subHash].filter(item => !badSet.has(item)); - console.log(hash[subHash]); - if (hash[subHash].length === 0) { - delete hash[subHash]; - } - } else { - if (badSet.has(hash[subHash])) { - delete subHash; - } - } - } - } - } - - helper(); - // return this.storage; -} // don't use - -const gc2 = (object) => { - this.storage = object; - const badHashes = new Set(); - const goodHashes = new Set(); - - // go through cache, excluding root query and mutation - // add hashes to either bad or good sets - // a hash is "bad" if it's marked for deletion - const helper1 = () => { - for (let key in this.storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - if (this.storage[key] === 'DELETED') { - badHashes.add(key); - delete this.storage[key]; - } - } - } - - // go through root query, remove any instances of bad hashes - const helper2 = () => { - const rootQuery = this.storage['ROOT_QUERY']; - for (let key in rootQuery) { - if (Array.isArray(rootQuery[key])) { - rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); - if (rootQuery[key].length === 0) delete rootQuery[key]; - for (let el of rootQuery[key]) goodHashes.add(el); - } else(badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); - } - } - - // helper function to go through the good hashes, see if they have sub hashes - // and add those to good hashes set - const helper3 = () => { - for (let key in this.storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - for (let i in this.storage[key]) { - if (Array.isArray(this.storage[key][i])) { - for (let el of this.storage[key][i]) { - if (el.includes('~') && !badHashes.has(el)) { - goodHashes.add(el); - } - } - } else if (typeof this.storage[key][i] === 'string') { - if (this.storage[key][i].includes('~') && !badHashes.has(this.storage[key][i])) { - goodHashes.add(this.storage[key][i]); - } - } - } - } - } - - // helper function to remove inaccessible hashes if they are not in goodhashes set - const helper4 = () => { - for (let key in this.storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - if (!goodHashes.has(key)) delete this.storage[key]; - for (let i in this.storage[key]) { - if (Array.isArray(this.storage[key][i])) { - this.storage[key][i] = this.storage[key][i].filter(x => !badHashes.has(x)); - } else if (typeof this.storage[key][i] === 'string') { - if (this.storage[key][i].includes('~') && badHashes.has(this.storage[key][i])) { - delete this.storage[key][i]; - } - } - } - } - } - - let t0_h1 = performance.now(); - helper1(); - let t1_h1 = performance.now(); - - let t0_h2 = performance.now(); - helper2(); - let t1_h2 = performance.now(); - - let t0_h3 = performance.now(); - helper3(); - let t1_h3 = performance.now(); - - let t0_h4 = performance.now(); - helper4(); - let t1_h4 = performance.now(); - - // console.log('Time for helper1: ', t1_h1 - t0_h1); - // console.log('Time for helper2: ', t1_h2 - t0_h2); - // console.log('Time for helper3: ', t1_h3 - t0_h3); - // console.log('Time for helper4: ', t1_h4 - t0_h4); - // console.log('Total time: ', t1_h4 - t0_h1); - - console.log(badHashes); - console.log(goodHashes); - return this.storage; -} - -const test_1 = { - ROOT_QUERY: { - 'actor(id:1)': 'Actor~1', - favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash - 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - ROOT_MUTATION: { - 'favoriteMovie(id:2)': 'Movie~2', - "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', - 'deleteMovie(id:3)': 'Movie~3', - }, - - 'Movie~1': { - id: '1', - title: 'Indiana Jones and the Last Crusade', - actors: 'Actor~2', - genre: 'ACTION', - releaseYear: 1989, - }, - 'Movie~2': { - id: '2', - title: 'Empire Strikes Back', - actors: ['Actor~1', 'Actor~3'], - releaseYear: 1980, - isFavorite: true, - }, - // DELETED - 'Movie~3': 'DELETED', - 'Movie~5': { - id: '5', - title: 'The Fugitive', - genre: 'ACTION', - releaseYear: 1993, - isFavorite: false, - }, - 'Actor~1': { - id: '1', - firstName: 'Harrison', - lastName: 'Ford', - films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - 'Actor~2': { - id: '2', - firstName: 'Sean', - lastName: 'Connery' - }, - 'Actor~3': { - id: '3', - firstName: 'Mark', - lastName: 'Hamill' - }, - 'Actor~4': { - id: '4', - firstName: 'Patti', - lastName: 'LuPone' - }, // INACCESSIBLE - 'Actor~5': { - id: '5', - firstName: 'Gary', - lastName: 'Oldman' - }, // INACCESSIBLE -}; - -const test_2 = { - ROOT_QUERY: { - 'actor(id:4)': 'Actor~4', - 'actor(id:1)': 'Actor~1', - favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash - 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - ROOT_MUTATION: { - 'favoriteMovie(id:2)': 'Movie~2', - "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', - 'deleteMovie(id:3)': 'Movie~3', - }, - - 'Movie~1': { - id: '1', - title: 'Indiana Jones and the Last Crusade', - actors: 'Actor~2', - genre: 'ACTION', - releaseYear: 1989, - }, - 'Movie~2': { - id: '2', - title: 'Empire Strikes Back', - actors: ['Actor~1', 'Actor~3'], - releaseYear: 1980, - isFavorite: true, - }, - // DELETED - 'Movie~3': 'DELETED', - 'Movie~5': 'DELETED', - 'Actor~1': { - id: '1', - firstName: 'Harrison', - lastName: 'Ford', - films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - 'Actor~2': { - id: '2', - firstName: 'Sean', - lastName: 'Connery' - }, - 'Actor~3': { - id: '3', - firstName: 'Mark', - lastName: 'Hamill' - }, - 'Actor~4': { - id: '4', - firstName: 'Patti', - lastName: 'LuPone', - }, - 'Actor~5': 'DELETED', // DELETED -}; - -const test_3 = { - ROOT_QUERY: { - // 'actor(id:4)': 'Actor~4', - 'actor(id:1)': 'Actor~1', - favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash - 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - ROOT_MUTATION: { - 'favoriteMovie(id:2)': 'Movie~2', - "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', - 'deleteMovie(id:3)': 'Movie~3', - }, - - 'Movie~1': { - id: '1', - title: 'Indiana Jones and the Last Crusade', - actors: 'Actor~2', - genre: 'ACTION', - releaseYear: 1989, - }, - 'Movie~2': { - id: '2', - title: 'Empire Strikes Back', - actors: ['Actor~1', 'Actor~3'], - releaseYear: 1980, - isFavorite: true, - }, - // DELETED - 'Movie~3': 'DELETED', - 'Movie~5': 'DELETED', - 'Movie~6': { - id: '6', - title: 'Whatever' - }, // INACCESSIBLE IF ACTOR 4 IS DELETED - 'Actor~1': { - id: '1', - firstName: 'Harrison', - lastName: 'Ford', - films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - 'Actor~2': { - id: '2', - firstName: 'Sean', - lastName: 'Connery' - }, - 'Actor~3': { - id: '3', - firstName: 'Mark', - lastName: 'Hamill' - }, - 'Actor~4': { - id: '4', - firstName: 'Patti', - lastName: 'LuPone', - films: 'Movie~6' - }, // INACCESSIBLE - 'Actor~5': 'DELETED', // DELETED -}; - -// let test1 = gc2(test_1); -// console.log(test1); -// let test2 = gc2(test_2); -// console.log(test2); -// let test3 = gc2(test_3); -// console.log(test3); -// let test4 = gc2(test_3); -// console.log(test4); - -/*--------------------------------------------------------------*/ - -function gc() { - // garbageCollection; garbage collection: removes any inaccessible hashes from the cache - const badHashes = getBadHashes(); - const goodHashes = rootQueryCleaner(badHashes); - const goodHashes2 = getGoodHashes(badHashes, goodHashes); - removeInaccessibleHashes(badHashes, goodHashes2); -} - -// remove hashes that are flagged for deletion and store records of them in a set badHashes for removal inside root queries -function getBadHashes() { - const badHashes = new Set(); - for (let key in storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - if (storage[key] === 'DELETED') { - badHashes.add(key); - delete storage[key]; - } - } - return badHashes; -} - -// go through root queries, remove all instances of bad hashes, add remaining hashes into goodHashes set -function rootQueryCleaner(badHashes) { - const goodHashes = new Set(); - const rootQuery = storage['ROOT_QUERY']; - for (let key in rootQuery) { - if (Array.isArray(rootQuery[key])) { - rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); - if (rootQuery[key].length === 0) delete rootQuery[key]; - for (let el of rootQuery[key]) goodHashes.add(el); - } else(badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); - } - return goodHashes; -} - -// Go through the cache, check good hashes for any nested hashes and add them to goodHashes set -function getGoodHashes(badHashes, goodHashes) { - for (let key in storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - for (let i in storage[key]) { - if (Array.isArray(storage[key][i])) { - for (let el of storage[key][i]) { - if (el.includes('~') && !badHashes.has(el)) { - goodHashes.add(el); - } - } - } else if (typeof storage[key][i] === 'string') { - if (storage[key][i].includes('~') && !badHashes.has(storage[key][i])) { - goodHashes.add(storage[key][i]); - } - } - } - } - return goodHashes; -} - -// Remove inaccessible hashes by checking if they are in goodhashes set or not -function removeInaccessibleHashes(badHashes, goodHashes) { - for (let key in storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - if (!goodHashes.has(key)) delete storage[key]; - for (let i in storage[key]) { - if (Array.isArray(storage[key][i])) { - storage[key][i] = storage[key][i].filter(x => !badHashes.has(x)); - } else if (typeof storage[key][i] === 'string') { - if (storage[key][i].includes('~') && badHashes.has(storage[key][i])) { - delete storage[key][i]; - } - } - } - } - return storage; -} - -const storage = { - ROOT_QUERY: { - 'actor(id:1)': 'Actor~1', - favoriteMovies: ['Movie~1', 'Movie~2', 'Movie~3'], // includes reference to deleted hash - 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - ROOT_MUTATION: { - 'favoriteMovie(id:2)': 'Movie~2', - "addMovie(input: {title: 'The Fugitive', releaseYear: 1993, genre: ACTION })": 'Movie~5', - 'deleteMovie(id:3)': 'Movie~3', - }, - - 'Movie~1': { - id: '1', - title: 'Indiana Jones and the Last Crusade', - actors: 'Actor~2', - genre: 'ACTION', - releaseYear: 1989, - }, - 'Movie~2': { - id: '2', - title: 'Empire Strikes Back', - actors: ['Actor~1', 'Actor~3'], - releaseYear: 1980, - isFavorite: true, - }, - // DELETED - 'Movie~3': 'DELETED', - 'Movie~5': { - id: '5', - title: 'The Fugitive', - genre: 'ACTION', - releaseYear: 1993, - isFavorite: false, - }, - 'Actor~1': { - id: '1', - firstName: 'Harrison', - lastName: 'Ford', - films: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~5'], // includes reference to deleted hash - }, - 'Actor~2': { - id: '2', - firstName: 'Sean', - lastName: 'Connery' - }, - 'Actor~3': { - id: '3', - firstName: 'Mark', - lastName: 'Hamill' - }, - 'Actor~4': { - id: '4', - firstName: 'Patti', - lastName: 'LuPone' - }, // INACCESSIBLE - 'Actor~5': { - id: '5', - firstName: 'Gary', - lastName: 'Oldman' - }, // INACCESSIBLE -}; - -let getBadHashesTest = getBadHashes(test_1); -// console.log(getBadHashesTest); -let rootQueryCleanerTest = rootQueryCleaner(getBadHashesTest); -// console.log(rootQueryCleanerTest); -let getGoodHashesTest= getGoodHashes(getBadHashesTest,rootQueryCleanerTest); -// console.log(getGoodHashesTest); -let removeInaccessibleHashesTest = removeInaccessibleHashes(getBadHashesTest, getGoodHashesTest); -// console.log(removeInaccessibleHashesTest); \ No newline at end of file diff --git a/src/possible-lfu-cache-algo.js b/src/possible-lfu-cache-algo.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/possible-lru-cache-algo.js b/src/possible-lru-cache-algo.js deleted file mode 100644 index a5b7849..0000000 --- a/src/possible-lru-cache-algo.js +++ /dev/null @@ -1,43 +0,0 @@ -class LRUCache { - constructor(capacity) { - this.cache = new Map(); - this.capacity = capacity; - } - - get(key) { - if (this.cache.has(key)) { - let value = this.cache.get(key); - this.cache.delete(key); - this.cache.set(key, value); - return value; - } else { - console.log('key not found'); - return null; - } - } - - put(key, value) { - if (this.cache.has(key)) { - this.cache.delete(key); - } - this.cache.set(key, value); - if (this.cache.size >= this.capacity) { - let LRUKey = this.cache.keys().next().value; - this.cache.delete(LRUKey); - } - } -} - -let testCache = new LRUCache(7); -console.log(testCache); - -testCache.put('Item~1', 'Value~1'); -testCache.put('Item~2', 'Value~2'); -testCache.put('Item~3', 'Value~3'); -testCache.put('Item~4', 'Value~4'); -testCache.put('Item~5', 'Value~5'); -testCache.put('Item~6', 'Value~6'); -console.log(testCache); -testCache.put('Item~7', 'Value~7'); -//item 1 is evicted -console.log(testCache); From c95b278c9aae293dddd71cb4e008cc607716965b Mon Sep 17 00:00:00 2001 From: Damon Alfaro Date: Thu, 8 Apr 2021 20:06:53 -0500 Subject: [PATCH 5/6] corrected initial server cache --- src/CacheClassServer.js | 139 +++++++++++++++------------------------- 1 file changed, 51 insertions(+), 88 deletions(-) diff --git a/src/CacheClassServer.js b/src/CacheClassServer.js index ba1c7ac..21982c1 100644 --- a/src/CacheClassServer.js +++ b/src/CacheClassServer.js @@ -17,52 +17,9 @@ console.log(await redis.ping()); export class Cache { constructor( - // initialCache = { - // ROOT_QUERY: {}, - // ROOT_MUTATION: {}, - // } initialCache = { - ROOT_QUERY: { - 'actor(id:1)': 'Actor~1', - movies: ['Movie~1', 'Movie~2', 'Movie~3', 'Movie~4'], - actors: ['Actor~1', 'Actor~2', 'Actor~3', 'Actor~4', 'Actor~6'], - 'movies(input:{genre:ACTION})': ['Movie~1', 'Movie~4', 'Movie~5'], - }, + ROOT_QUERY: {}, ROOT_MUTATION: {}, - 'Movie~1': { - id: '1', - title: 'Indiana Jones and the Last Crusade', - actors: ['Actor~1', 'Actor~2', 'Actor~6', 'Actor~7'], - genre: 'ACTION', - releaseYear: 1989, - }, - 'Movie~2': { - id: '2', - title: 'Empire Strikes Back', - actors: ['Actor~1', 'Actor~3'], - releaseYear: 1980, - }, - 'Movie~3': { - id: '3', - title: 'Witness', - actors: ['Actor~1', 'Actor~4'], - releaseYear: 1985, - }, - 'Movie~4': { - id: '4', - title: 'Air Force One', - actors: ['Actor~1', 'Actor~5'], - genre: 'ACTION', - releaseYear: 1997, - }, - 'Movie~5': 'DELETED', - 'Actor~1': { id: '1', firstName: 'Harrison' }, - 'Actor~2': { id: '2', firstName: 'Sean' }, - 'Actor~3': { id: '3', firstName: 'Mark' }, - 'Actor~4': { id: '4', firstName: 'Patti' }, - 'Actor~5': { id: '5', firstName: 'Gary' }, - 'Actor~6': 'DELETED', - 'Actor~7': { id: '7', firstName: 'Christy' } } ) { this.storage = initialCache; @@ -76,9 +33,7 @@ export class Cache { } } - pullOutCache() { - - } + pullOutCache() {} // Main functionality methods async read(queryStr) { @@ -157,54 +112,65 @@ export class Cache { // go through root queries, remove all instances of bad hashes, add remaining hashes into goodHashes set rootQueryCleaner(badHashes) { const goodHashes = new Set(); - const rootQuery = this.storage['ROOT_QUERY']; - for (let key in rootQuery) { - if (Array.isArray(rootQuery[key])) { - rootQuery[key] = rootQuery[key].filter(x => !badHashes.has(x)); - if (rootQuery[key].length === 0) delete rootQuery[key]; - for (let el of rootQuery[key]) goodHashes.add(el); - } else (badHashes.has(rootQuery[key])) ? delete rootQuery[key] : goodHashes.add(rootQuery[key]); - } - return goodHashes; + const rootQuery = this.storage['ROOT_QUERY']; + for (let key in rootQuery) { + if (Array.isArray(rootQuery[key])) { + rootQuery[key] = rootQuery[key].filter((x) => !badHashes.has(x)); + if (rootQuery[key].length === 0) delete rootQuery[key]; + for (let el of rootQuery[key]) goodHashes.add(el); + } else + badHashes.has(rootQuery[key]) + ? delete rootQuery[key] + : goodHashes.add(rootQuery[key]); } + return goodHashes; + } // Go through the cache, check good hashes for any nested hashes and add them to goodHashes set getGoodHashes(badHashes, goodHashes) { - for (let key in this.storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - for (let i in this.storage[key]) { - if (Array.isArray(this.storage[key][i])) { - for (let el of this.storage[key][i]) { - if (el.includes('~') && !badHashes.has(el)) { - goodHashes.add(el); - } - } - } else if (typeof this.storage[key][i] === 'string') { - if (this.storage[key][i].includes('~') && !badHashes.has(this.storage[key][i])) { - goodHashes.add(this.storage[key][i]); + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + for (let el of this.storage[key][i]) { + if (el.includes('~') && !badHashes.has(el)) { + goodHashes.add(el); } } + } else if (typeof this.storage[key][i] === 'string') { + if ( + this.storage[key][i].includes('~') && + !badHashes.has(this.storage[key][i]) + ) { + goodHashes.add(this.storage[key][i]); + } } } - return goodHashes; } + return goodHashes; + } // Remove inaccessible hashes by checking if they are in goodhashes set or not removeInaccessibleHashes(badHashes, goodHashes) { - for (let key in this.storage) { - if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; - if (!goodHashes.has(key)) delete this.storage[key]; - for (let i in this.storage[key]) { - if (Array.isArray(this.storage[key][i])) { - this.storage[key][i] = this.storage[key][i].filter(x => !badHashes.has(x)); - } else if (typeof this.storage[key][i] === 'string') { - if (this.storage[key][i].includes('~') && badHashes.has(this.storage[key][i])) { - delete this.storage[key][i]; - } + for (let key in this.storage) { + if (key === 'ROOT_QUERY' || key === 'ROOT_MUTATION') continue; + if (!goodHashes.has(key)) delete this.storage[key]; + for (let i in this.storage[key]) { + if (Array.isArray(this.storage[key][i])) { + this.storage[key][i] = this.storage[key][i].filter( + (x) => !badHashes.has(x) + ); + } else if (typeof this.storage[key][i] === 'string') { + if ( + this.storage[key][i].includes('~') && + badHashes.has(this.storage[key][i]) + ) { + delete this.storage[key][i]; } } } } + } // cache read/write helper methods async cacheRead(hash) { @@ -288,7 +254,7 @@ export class Cache { // for each hash from the input query, build the response object const readVal = await this.cacheRead(hash); // return undefine if hash has been garbage collected - if (readVal === undefined) return undefined + if (readVal === undefined) return undefined; if (readVal === 'DELETED') return acc; const dataObj = {}; for (const field in fields) { @@ -311,17 +277,14 @@ export class Cache { } } // acc is an array within a Response object for each hash - try{ - const resolvedProm = await Promise.resolve(acc) + try { + const resolvedProm = await Promise.resolve(acc); resolvedProm.push(dataObj); return resolvedProm; - } catch (error){ - return undefined + } catch (error) { + return undefined; } - }, []); - - } // Case where allHashesFromQuery has only one hash and is not an array but a single string const hash = allHashesFromQuery; @@ -351,4 +314,4 @@ export class Cache { return dataObj; } } -} \ No newline at end of file +} From 05be862b96b942ca5b66948896408be1253740a1 Mon Sep 17 00:00:00 2001 From: Damon Alfaro Date: Thu, 8 Apr 2021 20:15:55 -0500 Subject: [PATCH 6/6] cleaned up console.logs --- src/CacheClassServer.js | 1 - test_files/rhum_test_files/destructure_test.ts | 18 +++++++++--------- test_files/rhum_test_files/readCache_test.ts | 15 +++++---------- test_files/rhum_test_files/writeCache_test.ts | 6 ++++-- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/CacheClassServer.js b/src/CacheClassServer.js index 21982c1..d6ffad4 100644 --- a/src/CacheClassServer.js +++ b/src/CacheClassServer.js @@ -27,7 +27,6 @@ export class Cache { } insertIntoRedis() { - // console.log('i am called'); for (let key in this.storage) { redis.set(key, JSON.stringify(this.storage[key])); } diff --git a/test_files/rhum_test_files/destructure_test.ts b/test_files/rhum_test_files/destructure_test.ts index 0d5ef7b..8dbc076 100644 --- a/test_files/rhum_test_files/destructure_test.ts +++ b/test_files/rhum_test_files/destructure_test.ts @@ -20,13 +20,10 @@ Rhum.testPlan('destructure.ts', () => { test.createQueriesObjTestData, 'queries' ); - console.log('RESULTS', results) Rhum.asserts.assertEquals(test.createQueriesObjResultsData, results); }); Rhum.testCase('findQueryFields test', () => { - console.log(test.findQueryFieldsTestData); const results = findQueryFields(test.findQueryFieldsTestData); - console.log(results); Rhum.asserts.assertEquals(test.findQueryFieldsResultData, results); }); Rhum.testCase('findClosingBrace test', () => { @@ -59,14 +56,17 @@ Rhum.testPlan('destructure.ts', () => { Rhum.asserts.assertEquals(test.newAliasTestResult, result); }); }); - + Rhum.testSuite('destructure fragment tests', () => { - Rhum.testCase('destructure fragment tests - results in two seperate queries', () => { - const result = destructureQueries(test.fragmentTestData); - Rhum.asserts.assertEquals(test.fragmentResultData, result); - }); + Rhum.testCase( + 'destructure fragment tests - results in two seperate queries', + () => { + const result = destructureQueries(test.fragmentTestData); + Rhum.asserts.assertEquals(test.fragmentResultData, result); + } + ); Rhum.testCase('destructure fragment tests - results in one query', () => { - const result = destructureQueries(test.fragmentTestData2); + const result = destructureQueries(test.fragmentTestData2); Rhum.asserts.assertEquals(test.fragmentResultData2, result); }); Rhum.testCase('destructure fragment tests - nested fragments', () => { diff --git a/test_files/rhum_test_files/readCache_test.ts b/test_files/rhum_test_files/readCache_test.ts index aca925f..2d6a1e3 100644 --- a/test_files/rhum_test_files/readCache_test.ts +++ b/test_files/rhum_test_files/readCache_test.ts @@ -48,16 +48,11 @@ Rhum.testPlan('read method on Cache class', () => { Rhum.asserts.assertEquals(result, test.multipleQueriesResObj); } ); - Rhum.testCase( - "should accept alias queries", - async () => { - const cache = new Cache(test.aliasCache); - // await console.log('cache', cache) - const result = await cache.read(test.aliasQueryString); - // await console.log('result', result) - Rhum.asserts.assertEquals(result, test.aliasResObj); - } - ); + Rhum.testCase('should accept alias queries', async () => { + const cache = new Cache(test.aliasCache); + const result = await cache.read(test.aliasQueryString); + Rhum.asserts.assertEquals(result, test.aliasResObj); + }); }); Rhum.testSuite('populateAllHashes()', () => { diff --git a/test_files/rhum_test_files/writeCache_test.ts b/test_files/rhum_test_files/writeCache_test.ts index 67f0ec6..6b7d3a4 100644 --- a/test_files/rhum_test_files/writeCache_test.ts +++ b/test_files/rhum_test_files/writeCache_test.ts @@ -9,7 +9,7 @@ */ import Cache from '../../src/CacheClassBrowser.js'; -import {Cache as CacheServer} from '../../src/CacheClassServer.js'; +import { Cache as CacheServer } from '../../src/CacheClassServer.js'; import { Rhum } from 'https://deno.land/x/rhum@v1.1.4/mod.ts'; import { test } from '../test_variables/writeCache_variables.ts'; @@ -39,9 +39,11 @@ Rhum.testPlan('write method on Cache class', () => { Rhum.asserts.assertEquals(test.originalCache, cache.storage); } ); + // The following test requires the redis server to be started to test functionality. + // // Rhum.testCase( // 'alias test case', - // async () => { + // async () => { // const cache = new CacheServer(test.originalCache); // await cache.write(test.aliasQuery, test.aliasResponse); // await console.log(cache.storage);