-
Notifications
You must be signed in to change notification settings - Fork 43
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
Services inheriting from common base need different caches. #63
Comments
is there a way to make |
I ran into this exact same situation about a week ago. A solution to this would make this library even more amazing. |
Hey, thanks for your question. Will take a look after work :) |
@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. Option 1Use 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 2I can add a cache config callback option of something like: |
Scratch that, option 2 is not really an option since there will be no way to subscribe to cache busters. |
@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. |
Well, it actually is looking into the route being called.
Then you call it through the |
in that example, the paths resolve to edit: they actually resolve to full urls. |
@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. |
@jonstorer Anything to add here? |
@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.constructor.name}/${oldMethod.name} I will investigate this more. However, it won't be until next week sometime as I'm slammed this week |
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. |
I really like Cacheable. And I really want it to work for my needs! However, it's currently not working. Here's my scenario:
I have a base service called
Repository
. All services inherit from it. When I fetch data from each child service, they share the same cache & the service that calls 2nd, gets the first service's data.The calls in the follow code block are nested for force an order for this example. Typically, there's a race condition and whoever resolves first wins. The response in the 2nd call should be
Widget[]
, however, it comes back asPart[]
because of the cache.widgets.service.ts
parts.service.ts
repository.service.ts
The text was updated successfully, but these errors were encountered: