typescript-memoize icon indicating copy to clipboard operation
typescript-memoize copied to clipboard

Add a way to clear the cached values?

Open SimonGustavsson opened this issue 7 years ago • 4 comments

Hey! Cool package, thanks for that!

I have a suggestion...

In a long running node process it might be useful to memoize things like DB/file access. I appreciate this might not be a popular/expected thing to do at all, but I like the neatness of caching these things with a decorator! (Currently just playing around with this package).

So, these things can obviously change so it'd be neat to be able to clear this cache. Maybe after an interval, though that doesn't have to be a part of the package as long as there's a way of clearing it (wouldn't mind specifying @Memoize(timeToLive:480) in the decorator though!)

I quickly whipped this up that'll blast all cached maps that does the trick for me right now. It might be nice to just clear the cache for a specific function, but because the maps don't store the function name that's a bit trickier right now! :-)

export function clear<T extends { [key: string]: any }>(obj: T) {
    const keys = Object.getOwnPropertyNames(obj);
    const stub = '__memoized_map_';

    for (const key of keys) {
        if (key !== undefined && key.startsWith(stub)) {
            delete obj[key];
        }
    }
}

SimonGustavsson avatar Apr 07 '18 19:04 SimonGustavsson

I also need a way to manually clear the cache for specific functions. I want them to have no expiration date but trigger it manually.

MickL avatar Feb 18 '21 14:02 MickL

I've thought about this; I'm thinking some kind of hook would be the way to go, so you could write some arbitrary code to decide when/why to clear the cache. Hmm…

darrylhodgins avatar Feb 18 '21 15:02 darrylhodgins

Or you define an id / id-function that you put into the decorator and with the id you can clear the cache.

MickL avatar Feb 18 '21 15:02 MickL

An alternative approach which also serves to enable cross caching (two memoized getters that contain some of the same contents) would be to attach functions to the methods for allowing direct cache access.

An example of cross caching insertion and cache eviction:

class MyClass {
  private someOtherDataStructure: OtherDataStructure[];

  constructor(stuff: OtherDataStructure[]) {
    this.someOtherDataStructure = stuff
  }

  @Memoize()
  public getSubItemById(id: string): SubItem {
    const newSubItem = new SubItem(this.someOtherDataStructure.find(thing => thing.id === id);
    // Cross caching
    this.getSubItemByName.insert(newSubItem.name, newSubItem);
    return newSubItem;
  }

  @Memoize()
  public getSubItemByName(name: string): SubItem {
    const newSubItem = new SubItem(this.someOtherDataStructure.find(thing => thing.name === name);
    // Cross caching
    this.getSubItemById.insert(newSubItem.id, newSubItem);
    return newSubItem;
  }
  
  public removeSubItemById(id: string): SubItem {
    const subItem = this.getSubItemById(id);
    // Cache eviction
    this.getSubItemById.remove(subItem.id);
    // Cache eviction
    this.getSubItemByName.remove(subItem.name);
    return subItem;
  }
}

class SubItem {
  public id: string;
  public name: string;
  constructor(subStuff) {
    this.id = subStuff.id;
    this.name = subStuff.name;
  }
}

This pattern is great for lazy evaluation. Without this capability, it requires doing construction of the SubItems on the MyClass construction and then doing the find on the resulting items, which can do a large amount of unnecessary construction.

dyst5422 avatar Oct 06 '21 17:10 dyst5422