Add cache stat averages for key hits

This commit is contained in:
FoxxMD
2021-08-04 14:40:47 -04:00
parent efd31c5f21
commit 7b8a89e918
7 changed files with 140 additions and 35 deletions

View File

@@ -1,4 +1,5 @@
import {Duration} from "dayjs/plugin/duration";
import {Cache} from 'cache-manager';
import Poll from "snoostorm/out/util/Poll";
import Snoowrap from "snoowrap";
@@ -1250,8 +1251,13 @@ export interface OperatorConfig extends OperatorJsonConfig {
interface CacheTypeStat {
requests: number,
miss: number,
missPercent?: string,
identifierRequestCount: Cache
identifierAverageHit: number
requestTimestamps: number[]
averageTimeBetweenHits: string
}
export interface ResourceStats {
[key: string]: CacheTypeStat
[key: string]: CacheTypeStat;
}

View File

@@ -43,6 +43,10 @@ a {
content: ":";
}
.newRow {
margin-top: 15px;
}
.has-tooltip {
/*position: relative;*/
}

View File

@@ -433,6 +433,8 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
Object.keys(curr.stats.cache.types as ResourceStats).forEach((k) => {
acc[k].requests += curr.stats.cache.types[k].requests;
acc[k].miss += curr.stats.cache.types[k].miss;
acc[k].identifierAverageHit += Number.parseFloat(curr.stats.cache.types[k].identifierAverageHit);
acc[k].averageTimeBetweenHits += curr.stats.cache.types[k].averageTimeBetweenHits === 'N/A' ? 0 : Number.parseFloat(curr.stats.cache.types[k].averageTimeBetweenHits)
});
return acc;
}, cacheStats());
@@ -440,6 +442,8 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
const per = acc[curr].miss === 0 ? 0 : formatNumber(acc[curr].miss / acc[curr].requests) * 100;
// @ts-ignore
acc[curr].missPercent = `${formatNumber(per, {toFixed: 0})}%`;
acc[curr].identifierAverageHit = formatNumber(acc[curr].identifierAverageHit);
acc[curr].averageTimeBetweenHits = formatNumber(acc[curr].averageTimeBetweenHits)
return acc;
}, cumRaw);
const cacheReq = subManagerData.reduce((acc, curr) => acc + curr.stats.cache.totalRequests, 0);

View File

@@ -475,40 +475,72 @@
<span>
- Results from <span class="font-mono">window</span> criteria.
</span>
<label>Author Criteria</label>
<span><%= data.stats.cache.types.authorCrit.requests %> | <%= data.stats.cache.types.authorCrit.miss %> (<%= data.stats.cache.types.authorCrit.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.author.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.author.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Author Criteria</label>
<span class="newRow"><%= data.stats.cache.types.authorCrit.requests %> | <%= data.stats.cache.types.authorCrit.miss %> (<%= data.stats.cache.types.authorCrit.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">authorIs</span> results
</span>
<label>Item Criteria</label>
<span><%= data.stats.cache.types.itemCrit.requests %> | <%= data.stats.cache.types.itemCrit.miss %> (<%= data.stats.cache.types.itemCrit.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.authorCrit.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.authorCrit.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Item Criteria</label>
<span class="newRow"><%= data.stats.cache.types.itemCrit.requests %> | <%= data.stats.cache.types.itemCrit.miss %> (<%= data.stats.cache.types.itemCrit.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">itemIs</span> results
</span>
<label>Comment Check</label>
<span><%= data.stats.cache.types.commentCheck.requests %> | <%= data.stats.cache.types.commentCheck.miss %> (<%= data.stats.cache.types.commentCheck.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.itemCrit.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.itemCrit.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Comment Check</label>
<span class="newRow"><%= data.stats.cache.types.commentCheck.requests %> | <%= data.stats.cache.types.commentCheck.miss %> (<%= data.stats.cache.types.commentCheck.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">cacheUserResult</span> results
</span>
<label>Submissions</label>
<span><%= data.stats.cache.types.submission.requests %> | <%= data.stats.cache.types.submission.miss %> (<%= data.stats.cache.types.submission.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.commentCheck.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.commentCheck.averageTimeBetweenHits %>s</span>
<span>
</span>
<label>Comments</label>
<span><%= data.stats.cache.types.comment.requests %> | <%= data.stats.cache.types.comment.miss %> (<%= data.stats.cache.types.comment.missPercent %>) miss</span>
<span>
</span>
<label>Content</label>
<span><%= data.stats.cache.types.content.requests %> | <%= data.stats.cache.types.content.miss %> (<%= data.stats.cache.types.content.missPercent %>) miss</span>
<label class="newRow">Submissions</label>
<span class="newRow"><%= data.stats.cache.types.submission.requests %> | <%= data.stats.cache.types.submission.miss %> (<%= data.stats.cache.types.submission.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.submission.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.submission.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Comments</label>
<span class="newRow"><%= data.stats.cache.types.comment.requests %> | <%= data.stats.cache.types.comment.miss %> (<%= data.stats.cache.types.comment.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.comment.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.comment.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Content</label>
<span class="newRow"><%= data.stats.cache.types.content.requests %> | <%= data.stats.cache.types.content.miss %> (<%= data.stats.cache.types.content.missPercent %>) miss</span>
<span class="newRow">
- footer/comment/ban/message content.
</span>
<label>UserNote</label>
<span><%= data.stats.cache.types.userNotes.requests %> | <%= data.stats.cache.types.userNotes.miss %> (<%= data.stats.cache.types.userNotes.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.content.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.content.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">UserNote</label>
<span class="newRow"><%= data.stats.cache.types.userNotes.requests %> | <%= data.stats.cache.types.userNotes.miss %> (<%= data.stats.cache.types.userNotes.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.userNotes.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.userNotes.averageTimeBetweenHits %>s</span>
<span>
</span>
</div>
</span>

View File

@@ -166,7 +166,7 @@ export class Manager {
};
if (this.resources !== undefined) {
const resStats = this.resources.getStats();
const resStats = await this.resources.getStats();
data.cache = resStats.cache;
data.cache.currentKeyCount = await this.resources.getCacheKeyCount();

View File

@@ -109,6 +109,7 @@ export class SubredditResources {
};
const cacheUseCB = (miss: boolean) => {
this.stats.cache.userNotes.requestTimestamps.push(Date.now());
this.stats.cache.userNotes.requests++;
this.stats.cache.userNotes.miss += miss ? 1 : 0;
}
@@ -135,23 +136,56 @@ export class SubredditResources {
return 0;
}
getStats() {
async getStats() {
const totals = Object.values(this.stats.cache).reduce((acc, curr) => ({
miss: acc.miss + curr.miss,
req: acc.req + curr.requests,
}), {miss: 0, req: 0});
const cacheKeys = Object.keys(this.stats.cache);
return {
cache: {
// TODO could probably combine these two
totalRequests: totals.req,
totalMiss: totals.miss,
missPercent: `${formatNumber(totals.miss === 0 || totals.req === 0 ? 0 :(totals.miss/totals.req) * 100, {toFixed: 0})}%`,
types: Object.keys(this.stats.cache).reduce((acc, curr) => {
types: await cacheKeys.reduce(async (accProm, curr) => {
const acc = await accProm;
// calculate miss percent
const per = acc[curr].miss === 0 ? 0 : formatNumber(acc[curr].miss / acc[curr].requests) * 100;
// @ts-ignore
acc[curr].missPercent = `${formatNumber(per, {toFixed: 0})}%`;
// calculate average identifier hits
const idCache = acc[curr].identifierRequestCount;
// @ts-expect-error
const idKeys = await idCache.store.keys() as string[];
if(idKeys.length > 0) {
let hits = 0;
for (const k of idKeys) {
hits += await idCache.get(k) as number;
}
acc[curr].identifierAverageHit = formatNumber(hits/idKeys.length);
}
if(acc[curr].requestTimestamps.length > 1) {
// calculate average time between request
const diffData = acc[curr].requestTimestamps.reduce((acc, curr: number) => {
if(acc.last === 0) {
acc.last = curr;
return acc;
}
acc.diffs.push(curr - acc.last);
acc.last = curr;
return acc;
},{last: 0, diffs: [] as number[]});
const avgDiff = diffData.diffs.reduce((acc, curr) => acc + curr, 0) / diffData.diffs.length;
acc[curr].averageTimeBetweenHits = formatNumber(avgDiff/1000);
}
return acc;
}, this.stats.cache)
}, Promise.resolve(this.stats.cache))
}
}
}
@@ -162,9 +196,13 @@ export class SubredditResources {
async getActivity(item: Submission | Comment) {
try {
let hash = '';
if (item instanceof Submission && this.submissionTTL > 0) {
hash = `sub-${item.name}`;
await this.stats.cache.submission.identifierRequestCount.set(hash, (await this.stats.cache.submission.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.submission.requestTimestamps.push(Date.now());
this.stats.cache.submission.requests++;
const cachedSubmission = await this.cache.get(`sub-${item.name}`);
const cachedSubmission = await this.cache.get(hash);
if (cachedSubmission !== undefined) {
this.logger.debug(`Cache Hit: Submission ${item.name}`);
return cachedSubmission;
@@ -172,11 +210,14 @@ export class SubredditResources {
// @ts-ignore
const submission = await item.fetch();
this.stats.cache.submission.miss++;
await this.cache.set(`sub-${item.name}`, submission, {ttl: this.submissionTTL});
await this.cache.set(hash, submission, {ttl: this.submissionTTL});
return submission;
} else if (this.commentTTL > 0) {
hash = `comm-${item.name}`;
await this.stats.cache.comment.identifierRequestCount.set(hash, (await this.stats.cache.comment.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.comment.requestTimestamps.push(Date.now());
this.stats.cache.comment.requests++;
const cachedComment = await this.cache.get(`comm-${item.name}`);
const cachedComment = await this.cache.get(hash);
if (cachedComment !== undefined) {
this.logger.debug(`Cache Hit: Comment ${item.name}`);
return cachedComment;
@@ -184,7 +225,7 @@ export class SubredditResources {
// @ts-ignore
const comment = await item.fetch();
this.stats.cache.comment.miss++;
await this.cache.set(`comm-${item.name}`, comment, {ttl: this.commentTTL});
await this.cache.set(hash, comment, {ttl: this.commentTTL});
return comment;
} else {
// @ts-ignore
@@ -206,6 +247,8 @@ export class SubredditResources {
const hash = objectHash.sha1({...options, userName});
this.stats.cache.author.requests++;
await this.stats.cache.author.identifierRequestCount.set(user.name, (await this.stats.cache.author.identifierRequestCount.wrap(user.name, () => 0) as number) + 1);
this.stats.cache.author.requestTimestamps.push(Date.now());
let miss = false;
const cacheVal = await this.cache.wrap(hash, async () => {
miss = true;
@@ -251,6 +294,8 @@ export class SubredditResources {
// try to get cached value first
let hash = `${subreddit.display_name}-${cacheKey}`;
if (this.wikiTTL > 0) {
await this.stats.cache.content.identifierRequestCount.set(cacheKey, (await this.stats.cache.content.identifierRequestCount.wrap(cacheKey, () => 0) as number) + 1);
this.stats.cache.content.requestTimestamps.push(Date.now());
this.stats.cache.content.requests++;
const cachedContent = await this.cache.get(hash);
if (cachedContent !== undefined) {
@@ -311,6 +356,8 @@ export class SubredditResources {
if (this.filterCriteriaTTL > 0) {
const hashObj = {itemId: item.id, ...authorOpts, include};
const hash = `authorCrit-${objectHash.sha1(hashObj)}`;
await this.stats.cache.authorCrit.identifierRequestCount.set(hash, (await this.stats.cache.authorCrit.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.authorCrit.requestTimestamps.push(Date.now());
this.stats.cache.authorCrit.requests++;
let miss = false;
const cachedAuthorTest = await this.cache.wrap(hash, async () => {
@@ -345,6 +392,8 @@ export class SubredditResources {
try {
const hashObj = {itemId: item.name, ...states};
const hash = `itemCrit-${objectHash.sha1(hashObj)}`;
await this.stats.cache.itemCrit.identifierRequestCount.set(hash, (await this.stats.cache.itemCrit.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.itemCrit.requestTimestamps.push(Date.now());
this.stats.cache.itemCrit.requests++;
const cachedItem = await this.cache.get(hash);
if (cachedItem !== undefined) {

View File

@@ -876,16 +876,26 @@ export const removeUndefinedKeys = (obj: any) => {
return newObj;
}
const timestampArr = () => {
const arr: number[] = [];
arr.length = 20;
return arr;
}
const statMetricCache = () => {
return cacheManager.caching({store: 'memory', max: 50, ttl: 0});
}
export const cacheStats = (): ResourceStats => {
return {
author: {requests: 0, miss: 0},
authorCrit: {requests: 0, miss: 0},
itemCrit: {requests: 0, miss: 0},
content: {requests: 0, miss: 0},
userNotes: {requests: 0, miss: 0},
submission: {requests: 0, miss: 0},
comment: {requests: 0, miss: 0},
commentCheck: {requests: 0, miss: 0}
author: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
authorCrit: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
itemCrit: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
content: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
userNotes: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
submission: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
comment: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
commentCheck: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0}
};
}