Skip to content

Commit

Permalink
add cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
joyc-bq committed Apr 1, 2024
1 parent a5b2f86 commit e3ba7a4
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 111 deletions.
151 changes: 85 additions & 66 deletions common/lib/utils/concurrent_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,78 +15,97 @@
*/

export class ConcurrentMap<K, V> {
protected map: Map<K, V> = new Map<K, V>();

size() {
return this.map.size;
protected map: Map<K, V> = new Map<K, V>();

size() {
return this.map.size;
}

get(key: K, defaultItemValue?: any, itemExpirationNano?: any): V | undefined {
return this.map.get(key);
}

clear() {
this.map.clear();
}

computeIfPresent(key: K, defaultValue: V | null = null, remappingFunc: (key: K, existingValue: V) => V | null): V | null {
const existingValue: V | undefined = this.map.get(key);
if (existingValue == null) {
return null;
}

get(key: K, defaultItemValue?: any, itemExpirationNano?: any): V | undefined {
return this.map.get(key);
}

clear() {
this.map.clear();
}

computeIfPresent(key: K, defaultValue: V | null = null, remappingFunc: (key: K, existingValue: V) => V | null): V | null {
let existingValue: V | undefined = this.map.get(key);
if (existingValue == null) {
return null;
}
let newValue: any = remappingFunc(key, existingValue)
if (newValue != null) {
this.map.set(key, newValue);
return newValue;
}
else {
this.map.delete(key);
return null;
}
const newValue: any = remappingFunc(key, existingValue);
if (newValue != null) {
this.map.set(key, newValue);
return newValue;
} else {
this.map.delete(key);
return null;
}

computeIfAbsent(key: K, mappingFunc: (key: K) => V | null): V | null {
let value: V | undefined = this.map.get(key);
if (value === undefined) {
let newValue: V | null = mappingFunc(key);
if (newValue !== null) {
this.map.set(key, newValue);
return newValue;
}
return null;
}
return value;
}

putIfAbsent(key: K, newValue: V): V | null {
let existingValue: V | undefined = this.map.get(key)
if (existingValue == null) {
}

computeIfAbsent(key: K, mappingFunc: (key: K) => V | null): V | null {
const value: V | undefined = this.map.get(key);
if (value === undefined) {
const newValue: V | null = mappingFunc(key);
if (newValue !== null) {
this.map.set(key, newValue);
return newValue;
}
return existingValue;
}

remove(key: K): boolean {
return this.map.delete(key);
return null;
}
return value;
}

removeIf(predicate: (v: any, k: any) => V): boolean {
let originalSize = this.size()
this.map.forEach((v, k) => {
if (!predicate(v, k)) {
this.remove(k)
}
});
return this.size() < originalSize
}

get keys() {
return this.map.keys
}

get values() {
return this.map.values
putIfAbsent(key: K, newValue: V): V | null {
const existingValue: V | undefined = this.map.get(key);
if (existingValue == null) {
this.map.set(key, newValue);
return newValue;
}
return existingValue;
}

remove(key: K): V | undefined {
const value = this.map.get(key);
this.map.delete(key);
return value;
}

removeIf(predicate: (v: any, k: any) => V): boolean {
const originalSize = this.size();
this.map.forEach((v, k) => {
if (!predicate(v, k)) {
this.remove(k);
}
});
return this.size() < originalSize;
}

removeMatchingValues(removalValues: Array<V>): boolean {
const originalSize = this.size();
this.map.forEach((v, k) => {
if (removalValues.includes(v)) {
this.remove(k);
}
});
return this.size() < originalSize;
}

applyIf(predicate: (k: any, v: any) => V, apply: (k: any, v: any) => V): void {
const originalSize = this.size();
this.map.forEach((v, k) => {
if (predicate(k, v)) {
apply(k, v);
}
});
}

get keys() {
return this.map.keys;
}

get entries() {
return this.map.entries;
}
}
94 changes: 49 additions & 45 deletions common/lib/utils/sliding_expiration_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
limitations under the License.
*/

import { chain } from "lodash";
import { ConcurrentMap } from "./concurrent_map";

class CacheItem<V> {
Expand All @@ -34,85 +33,90 @@ class CacheItem<V> {
return this.expirationTimeNanos;
}

updateExpiration(expirationIntervalNanos: bigint): bigint {
this.expirationTimeNanos = process.hrtime.bigint() + expirationIntervalNanos;
return this.expirationTimeNanos
updateExpiration(expirationIntervalNanos: number): CacheItem<V> {
this.expirationTimeNanos = process.hrtime.bigint() + BigInt(expirationIntervalNanos);
return this;
}
}

export class SlidingExpirationCache<K, V> {

private _cleanupIntervalNanos: bigint = BigInt(10 * 60 * 1_000_000_000); // 10 minutes
private shouldDisposeFunc: () => Promise<K>;
private itemDisposalFunc: () => Promise<K>;
protected _map: ConcurrentMap<K, CacheItem<V>> = new ConcurrentMap<K, CacheItem<V>>;
private _cleanupIntervalNanos: bigint = BigInt(10 * 60 * 1_000_000_000); // 10 minutes
private _shouldDisposeFunc: (item: V) => boolean;
private _itemDisposalFunc: (item: V) => void;
protected _map: ConcurrentMap<K, CacheItem<V>> = new ConcurrentMap<K, CacheItem<V>>();
private _cleanupTimeNanos: bigint;

constructor(cleanupIntervalNanos: number, shouldDisposeFunc: () => Promise<K>, itemDisposalFunc: () => Promise<K>) {
constructor(cleanupIntervalNanos: number, shouldDisposeFunc: (item: V) => boolean, itemDisposalFunc: (item: V) => void | null) {
this._cleanupIntervalNanos = BigInt(cleanupIntervalNanos);
this.shouldDisposeFunc = shouldDisposeFunc;
this.itemDisposalFunc = itemDisposalFunc;
this._shouldDisposeFunc = shouldDisposeFunc;
this._itemDisposalFunc = itemDisposalFunc;
this._cleanupTimeNanos = process.hrtime.bigint() + this._cleanupIntervalNanos;
}

get size(): number {
return this._map.size()
}

set cleanupIntervalNs(value: bigint) {
this._cleanupIntervalNanos = value;
return this._map.size();
}

get keys(): IterableIterator<K>{
return this._map.keys();
set cleanupIntervalNs(value: number) {
this._cleanupIntervalNanos = BigInt(value);
}

get items(): IterableIterator<CacheItem<V>> {
return this._map.values();
computeIfAbsent(key: K, defaultValue: V | null = null, mappingFunc: (key: K) => V, itemExpirationNanos: number) {
this.cleanUp();
const cacheItem = this._map.computeIfAbsent(key, (k) => new CacheItem(mappingFunc(k), process.hrtime.bigint() + BigInt(itemExpirationNanos)));
if (cacheItem === null) return null;
else cacheItem.updateExpiration(itemExpirationNanos).item;
}

get(key: K): V | undefined {
this.cleanUp();
let cacheItem = this._map.get(key)
if (cacheItem != null) return cacheItem?.item; else null;
const cacheItem = this._map.get(key);
if (cacheItem != null) return cacheItem?.item;
else null;
}

computeIfAbsent(key: K, defaultValue: V | null = null, mappingFunc: (key: K) => Promise<K>, itemExpirationNanos: bigint) {
remove(key: K): void {
this.removeAndDispose(key);
this.cleanUp();
let cacheItem = this._map.computeIfAbsent(key, () => new CacheItem(mappingFunc(key), process.hrtime.bigint() + itemExpirationNanos))
return

}

remove(key: K): boolean {
return this._map.remove(key);
removeAndDispose(key: K): void {
const cacheItem = this._map.remove(key);
if (cacheItem != null && this._itemDisposalFunc !== null) {
this._itemDisposalFunc(cacheItem.item);
}
}

removeAndDispose(key: K): boolean {
return this._map.remove(key);
removeIfExpired(key: K): void {
const cacheItem = this._map.get(key);
if (cacheItem == null || this.shouldCleanupItem(cacheItem)) {
this.removeAndDispose(key);
}
}

removeIfExpired(key: K): boolean {
let cacheItem = this._map.get(key)
if (cacheItem == null || this.shouldDisposeFunc)
return this._map.remove(key);
shouldCleanupItem(cacheItem: CacheItem<V>): boolean {
if (this._shouldDisposeFunc !== null) {
return process.hrtime.bigint() > cacheItem.expirationTimeNs && this._shouldDisposeFunc(cacheItem.item);
}
return process.hrtime.bigint() > cacheItem.expirationTimeNs;
}

shouldCleanupItem(cacheItem: CacheItem<V>): boolean {
if (this.shouldDisposeFunc != null) {
return process.hrtime.bigint() > (cacheItem.expirationTimeNs && this.shouldDisposeFunc(cacheItem.item))
clear(): void {
for (const [key, val] of this._map.entries()) {
if (val !== null && this._itemDisposalFunc != null) {
this._itemDisposalFunc(val.item);
}
}
return process.hrtime.bigint() > cacheItem.expirationTimeNs
}

protected cleanUp() {
let currentTime = process.hrtime.bigint();
if (this._cleanupTimeNanos < currentTime) {
const currentTime = process.hrtime.bigint();
if (this._cleanupTimeNanos > currentTime) {
return;
}
this._cleanupTimeNanos = process.hrtime.bigint() + this._cleanupIntervalNanos;
this._map.forEach((v, k) => {
this.removeIfExpired(k);
});
for (const k of this._map.keys()) {
this.removeIfExpired(k);
}
}
}
}

0 comments on commit e3ba7a4

Please sign in to comment.