-
Notifications
You must be signed in to change notification settings - Fork 88
NS-CACHE: Eviction from the cache #6086
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,11 @@ | |
'use strict'; | ||
|
||
const _ = require('lodash'); | ||
const assert = require('assert'); | ||
|
||
const P = require('../../util/promise'); | ||
const dbg = require('../../util/debug_module')(__filename); | ||
const config = require('../../../config'); | ||
// const config = require('../../../config.js'); | ||
// const mapper = require('./mapper'); | ||
const MDStore = require('./md_store').MDStore; | ||
|
@@ -35,12 +37,17 @@ const builder_lock = new KeysLock(); | |
class MapBuilder { | ||
|
||
/** | ||
* @param {nb.ID[]} chunk_ids | ||
* @param {nb.ID[]} chunk_ids | ||
* @param {nb.Tier} [move_to_tier] | ||
* @param {boolean} [evict] | ||
*/ | ||
constructor(chunk_ids, move_to_tier) { | ||
constructor(chunk_ids, move_to_tier, evict) { | ||
this.chunk_ids = chunk_ids; | ||
this.move_to_tier = move_to_tier; | ||
this.evict = evict; | ||
guptsanj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// eviction and move to tier are mutually exclusive | ||
if (evict) assert.strictEqual(move_to_tier, undefined); | ||
|
||
/** @type {nb.ID[]} */ | ||
this.second_pass_chunk_ids = []; | ||
|
@@ -67,7 +74,7 @@ class MapBuilder { | |
} | ||
|
||
/** | ||
* @param {nb.ID[]} chunk_ids | ||
* @param {nb.ID[]} chunk_ids | ||
*/ | ||
async run_build(chunk_ids) { | ||
await system_store.refresh(); | ||
|
@@ -80,7 +87,7 @@ class MapBuilder { | |
* Note that there is always a possibility that the chunks will cease to exist | ||
* TODO: We can release the unrelevant chunks from the surround_keys | ||
* This will allow other batches to run if they wait on non existing chunks | ||
* @param {nb.ID[]} chunk_ids | ||
* @param {nb.ID[]} chunk_ids | ||
* @returns {Promise<nb.Chunk[]>} | ||
*/ | ||
async reload_chunks(chunk_ids) { | ||
|
@@ -93,6 +100,7 @@ class MapBuilder { | |
const loaded_chunks = loaded_chunks_db.map(chunk_db => new ChunkDB(chunk_db)); | ||
dbg.log1('MapBuilder.reload_chunks:', loaded_chunks); | ||
|
||
|
||
/** @type {nb.Block[]} */ | ||
const blocks_to_delete = []; | ||
|
||
|
@@ -127,7 +135,10 @@ class MapBuilder { | |
// const tiering_status = node_allocator.get_tiering_status(chunk.bucket.tiering); | ||
// chunk.tier = mapper.select_tier_for_write(chunk.bucket.tiering, tiering_status); | ||
// } | ||
|
||
if (this.evict) { | ||
chunks_to_delete.push(chunk); | ||
return; | ||
} | ||
if (!chunk.parts || !chunk.parts.length || !chunk.bucket) { | ||
const last_hour = this.start_run - (60 * 60 * 1000); // chunks that were created in the last hour will not be deleted | ||
dbg.log0('unreferenced chunk to delete', chunk); | ||
|
@@ -151,15 +162,27 @@ class MapBuilder { | |
'blocks_to_delete', blocks_to_delete.length); | ||
|
||
await Promise.all([ | ||
this.evict && map_deleter.delete_parts_by_chunks(chunks_to_delete_uniq), | ||
map_deleter.delete_blocks(blocks_to_delete), | ||
map_deleter.delete_chunks(chunks_to_delete_uniq), | ||
|
||
]); | ||
|
||
// Deleting objects with no parts here as the delete_parts_by_chunks need to finish before | ||
// any attempt is made to delete the objects. | ||
if (this.evict) { | ||
const objects_to_gc = _.uniq(loaded_chunks_db.flatMap(chunk => chunk.objects)) | ||
.filter(obj => Date.now() - obj.create_time.getTime() > config.NAMESPACE_CACHING.MIN_OBJECT_AGE_FOR_GC); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @guymguym Should we not do this cached object aging check at the start of the map builder for the eviction case? This check is just marking object entries as deleted when they are aged out AND objects have no parts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question - here are my thoughts: For chunks (with their parts and blocks) we trigger eviction based on capacity and evict chunks from the cache based on LRU. FIFO is even easier to implement just by not moving chunks to the front of the LRU on hit - and FIFO is age based. So if we will prefer simple age based vs access based caching, we can do that - actually we should just add this for dev/test. For objects_md cleanup - keep in mind that even if we are limited on cache capacity and have to evict, it is still very much likely that cached object_mds have not been deleted or overwritten in the hub, so keeping them is not a bad thing at all. But we do need to evict eventually if the workload will read too many of these to the cache than we can store in the DB. Today we do not have a capacity limit for object_mds and we do not order them in LRU. However we can easily iterate object_mds in FIFO order by traversing the _id index in order or creation in the cache and then check if this object has no parts and then delete. Ultimately I think object_md cleanup calls for a separate worker - because the iteration subject and order are completely different than tier_ttf_worker. In its own worker we should check how close we are to the object_mds limit we can store in the DB (either config or calculate based on PV), and cleanup in FIFO order. For now I thought we can piggy back the chunks eviction, BUT adding the age condition I suggested is presenting an issue - if we evict all chunks/parts of an object but it is too young and we filter it out from objects_to_gc, we may never come back to evict more chunks for this same object_md and it will never be evicted. So I would remove the filter for now, but in any case to make it work well we need to move this to its own worker. |
||
if (objects_to_gc.length) { | ||
dbg.log1('MapBuilder.delete_objects_if_no_parts:', objects_to_gc); | ||
await Promise.all(objects_to_gc.map(map_deleter.delete_object_if_no_parts)); | ||
} | ||
} | ||
return chunks_to_build; | ||
} | ||
|
||
/** | ||
* @param {nb.Chunk[]} chunks | ||
* @param {nb.Chunk[]} chunks | ||
*/ | ||
async build_chunks(chunks) { | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.