Comments (12)
is there a way to make const cacheBuster$ = new Subject<void>();
specific to each instance of each child service?
from ts-cacheable.
Hey, thanks for your question. Will take a look after work :)
from ts-cacheable.
I ran into this exact same situation about a week ago. A solution to this would make this library even more amazing.
from ts-cacheable.
@jonstorer the issue you are experiencing is due to the fact that the decorators are executed immediately, once your class is parsed. Therefore, you will really have only one cache storage, which will be for your Repository class.
In short, each method is cached by combining the class constructor name + the property key (method name), unless you pass cacheKey
as a custom config.
Obviously none of those options work for you, so there are two solutions to your problem.
Option 1
Use a decorator mixin (I have used that in a project of mine and it works well)
import {PCacheable, PCacheBuster} from 'ngx-cacheable';
import {Subject} from 'rxjs';
import {Duration} from '@app/enums/duration.enum';
/**
* A base mixin decorator which will cache to our default service methods like getById, query and etc
*/
export function BaseApiCache(
serviceName: string,
cacheDuration: number = Duration.FIVE_MINUTES,
cacheBusterSubject: Subject<any> = new Subject(),
excludeMethods: string[] = []
): Function {
/**
* A mixin function which will take all properties of BaseApiCache and stamp them
* on the provided constructor (i.e all services which require the basic get/set/update functionality)
*/
return function (derivedCtor: Function): void {
class BaseApiCache extends Api {
cacheBuster = cacheBusterSubject;
@PCacheable({
cacheKey: serviceName + '#getById',
maxAge: cacheDuration,
cacheBusterObserver: cacheBusterSubject.asObservable()
})
public getById<TParams, TData>(
id: string,
asset?: string,
params?: TParams,
subEntities?: string
): Promise<TData> {
return super.getById(id, asset, params, subEntities);
}
@PCacheable({
cacheKey: serviceName + '#query',
maxCacheCount: 5,
maxAge: cacheDuration,
cacheBusterObserver: cacheBusterSubject.asObservable()
})
public query<TParams, TData>(params?: TParams, asset?: string, subEntities?: string): Promise<TData> {
return super.query<TParams, TData>(params, asset, subEntities);
}
@PCacheable({
cacheKey: serviceName + '#count',
maxAge: cacheDuration,
cacheBusterObserver: cacheBusterSubject.asObservable()
})
public count(
params: any
): Promise<number> {
return super.count(params);
}
@PCacheBuster({
cacheBusterNotifier: cacheBusterSubject
})
create<TData, TResponse>(data: TData, asset?: string): Promise<TResponse> {
return super.create<TData, TResponse>(data, asset);
}
@PCacheBuster({
cacheBusterNotifier: cacheBusterSubject
})
public upsert<TData, TResponse>(
id: string,
data: TData,
asset?: string,
isMultipart?: boolean,
subEntities?: string
): Promise<TResponse> {
return super.upsert<TData, TResponse>(id, data, asset, isMultipart, subEntities);
}
@PCacheBuster({
cacheBusterNotifier: cacheBusterSubject
})
public update<TData, TResponse>(id: string, data: Partial<TData>, asset?: string, subEntities?: string): Promise<TResponse> {
return super.update<TData, TResponse>(id, data, asset, subEntities);
}
@PCacheBuster({
cacheBusterNotifier: cacheBusterSubject
})
public remove(id: string, asset?: string, subEntities?: string): Promise<void> {
return super.remove(id, asset, subEntities);
}
}
const fieldCollector = {};
(BaseApiCache as Function).apply(fieldCollector);
Object.getOwnPropertyNames(fieldCollector).forEach((name) => {
derivedCtor.prototype[name] = fieldCollector[name];
});
Object.getOwnPropertyNames(BaseApiCache.prototype).forEach((name) => {
if (name !== 'constructor' && excludeMethods.indexOf(name) === -1) {
derivedCtor.prototype[name] = BaseApiCache.prototype[name];
}
});
};
}
And then use it like:
/**
* Make API calls to Parts endpoint.
*/
@BaseApiCache('partService', 1000000)
@Injectable()
export class PartService {
}
Option 2
I can add a cache config callback option of something like:
cacheKeyCallback
which you can add to each Repository
class method and will be called everytime, that method is called. In it, you can derive a unique cache key based on the service which the method is called from. So you can constructor a new key, which will essentially be somethign like PartService#find
.
What do you think about that?
from ts-cacheable.
Scratch that, option 2 is not really an option since there will be no way to subscribe to cache busters.
from ts-cacheable.
@angelnikolov is there anyway to reach into the method and pay attention to the route being called? that could be used as the cache key.
from ts-cacheable.
Well, it actually is looking into the route being called.
Let's say you have this Repository service method:
@Cacheable({
cacheBusterObserver: cacheBuster$
})
public findById(id: string): Observable<T> {
return this.http.get<T>(this.url(id));
}
Then you call it through the WidgetService
child by this.widgetService.findById('1')
.
This will essentially always go through the same Repository
service method through the prototype chain. Therefore, there will only be 1 cache storage. For every different service call, it will remove the previous cache and store a new one.
Say you first call the widgetService.findById
and then call partsService.findById
. The produced url will be different, so the cache for the widgetService
call will be evicted by the call of the parstService
's method.
The way to resolve that is either to use my proposal here or increate the maxCacheCount
of the base Repository class to a larger number, so all your method calls are cached.
from ts-cacheable.
in that example, the paths resolve to /widgets/1
and /parts/1
. Does that make a difference? Based on what you're saying, the cache keys would be different.
edit: they actually resolve to full urls. https://api.example.com/widgets/1
& https://api.example.com/parts/1
from ts-cacheable.
@jonstorer Yes, it would be different, but since you are using a single service (Base repository service) and a single method (findById), all your calls will go to the same cache storage and the second call's cache will override the first one's.
If you add maxCacheCount: 100
you will store a hundred of responses in the cache of RepositoryService#findById
. The other option is to use a mixin as described above, which will apply the decorator dynamically, to each service you want. Then you won't have this issue.
from ts-cacheable.
@jonstorer Anything to add here?
from ts-cacheable.
@angelnikolov I think there's an opportunity to identify your cache key further down in execution. I've not spent the proper time to research this, however, you have access to oldMethod
which should be the specific method on the specific subclass. Again, I've not done my research, but won't that evaluate to the correct class name and method name?
`${oldMethod.constructor.name}/${oldMethod.name}
I will investigate this more. However, it won't be until next week sometime as I'm slammed this week
from ts-cacheable.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
from ts-cacheable.
Related Issues (20)
- GlobalCacheConfig.storageStrategy not used in Angular 9? HOT 7
- Possibility to cache per instance HOT 7
- JSON parse type issues HOT 4
- Cache query params HOT 2
- Refresh data HOT 7
- invoking a function after maxAge ends HOT 1
- How to keep cache isolated between different users ? HOT 5
- inMemoryStorageStrategy: What is the in-memory size that can be utilized using the cacheable decorator? HOT 2
- Does this work with subscribe? HOT 1
- [@CacheBuster()][Feature Proposal] Instant cache busting HOT 3
- Caching for urls without parameters
- Adapt description about "Cache busting" when different parameters are used HOT 4
- Observable<XXXXXX>>' is not assignable to parameter of type 'TypedPropertyDescriptor<ICacheable<Observable<any>>>'. HOT 4
- Support for optional parameters HOT 2
- When browser close, it will reset the cache. HOT 1
- Release number HOT 1
- Bust only one Cache in maxCacheCount HOT 3
- Access cached date HOT 3
- Does Ts-Cachable support typed observables? HOT 4
- StorageStrategy - removeAtIndex add entity to parameters HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ts-cacheable.