Öncelikle konuya cache veya caching nedir diye başlamayacağım konuyla ilgili çoğu kişinin bunları bildiğini varsayıyorum. Ancak konunun daha iyi anlaşılabilmesi açısından caching teknolojisinin web uygulamaları için öneminden kısaca bahsetmekte fayda olduğunu düşünüyorum.
- Database üzerindeki yükü azaltır. (requestlerde uygulama dbye gidip veriyi çekmek yerine client-side veya server-side tarafında tutulan veriler response edilir.)
- Back-end app/api üzerindeki yükü azaltır. (verileri cacheten getirmek dbden getirmekten çok daha az maliyetli veya maliyetsiz olduğu gibi aynı şekilde back-end tarafındaki logicleriniz, fonksiyonlarınız çalışmadan client tarafına veri gösterileceğinden back-end tarafındaki uygulamanızda rahatlayacaktır.)
- Uygulama Performansını artırır. (aradan back-end ve db yükünü azalttığımızda otomatik olarak uygulamamız hızlanmış ve clientlarımız daha hızlı bir şekilde veriye ulaşabiliyor hale gelecektir.)
- Tabiki farklı senaryolarda çok daha farklı faydalar sağlayabilir ve maddelerimizi artırabiliriz.
Günümüz şartlarında teknolojinin her alanında olduğu gibi web teknolojileri de hızla gelişmeye devam etmekte ve kullanıcıların beklentileri değişmektedir. Durum böyle olunca bir web uygulamasından beklenen en önemli kriterler arasında sayfaların yüklenme hızları yer almaktadır. Şöyle düşünelim hemen hemen hiç kimse aynı işleri yapan 2 uygulama varken daha yavaş olanı tercih etmeyecektir.
Bu yazıda Angular 9.1.12 üzerinde ts-cacheable modülünü inceliyor olacağız.
Öncelikle https://jsonplaceholder.typicode.com/posts adresi üzerindeki datayı çekmek ve clienta sunmak için basit bir uygulama oluşturdum. Ardından post adında bir component oluşturarak bunu app comonent içinde tanımladım daha sonra ise component içinden post servisimi çağırarak dataları ilgili apiden getirmek için basit bir html tablo oluşturdum. Post servisimiz şöyle görünecektir.
getPosts():Observable<Post[]>{
return this.httpClient.get<Post[]>(this.userApi + "/posts");
}
addPost(post: Post):Observable<Post>{
return this.httpClient.post<Post>(this.userApi+ "/posts", post)
}
Şimdilik eksik bir şey yok gibi sıra geldi ts-cacheable modülümüzü uygulamamıza eklemeye
ts-cacheable modülünü kurmak için aşağıdaki komutu çalıştıralım.
npm i ts-cacheable --save
Browserımızın geliştirici konsolunu (F12) açtığımızda, Network tabı altında sayfayı her yenilediğimizde requestlerimizi görebiliyoruz. Postlarımız için yapılan get requestin üzerine tıklayıp sağ taraftaki timing menüsünde requestimizin sürelerine ait bilgileri şöyle görünüyor.
Artık elimizde ilgili apinin ilgili endpointine giderek verileri getiren bir uygulama var gelelim caching mevzusuna.
Öncelikle servisimizin getPosts() metoduna ‘ts-cacheable’ modülüyle birlikte gelen @Cacheable() decarator’unu ekleyelim ve aşağıdaki gibi storage strategymizi localstoragestrategy olarak belirleyelim.
import { Cacheable, CacheBuster, GlobalCacheConfig, LocalStorageStrategy } from 'ts-cacheable';
GlobalCacheConfig.storageStrategy = LocalStorageStrategy;
@Cacheable()
getPosts():Observable<Post[]>{
return this.httpClient.get<Post[]>(this.userApi + "/posts");
}
Tekrar browserımıza dönecek olursak, geliştirici konsolunu kapatmadıysanız bu değişiklikler sonucunda sayfa ilk yenilendiğinde postları getiren get requestimize ait zamanları ve requestimizi görüyor olacağız ancak sayfayı tekrar yenilersek requestimizin olmadığını göreceğiz.
Uygulamamız bu sefer veriyi getirmek için servis üzerinden ilgili kaynağa ulaşmak yerine ilk requestimizde browser üzerine kaydedilen veriyi hızlıca getirdi.
Storage tabı altındaki local storage altında bir key value pair görüyoruz. Sayfayı yenilediğimizde gelen cachelenmiş verinin tutulduğu kısım tamda burası. Burada tutacağını uygulamamızın anladığı kısım ise biraz önce belirlediğimiz storagestrategy, dolayısıyla artık sayfayı her yenilediğimde get requestim veri kaynağı olarak apinin ilgili endpointine gitmek yerine browserımın local storage’ında kaydedilen veriyi getiriyor.
Peki her şey iyi fakat, siteye girdim verilerim artık cacheten geliyor, ancak database’deki veriler güncellenirse, yeni veri eklenirse veya silinirse ne olacak?
Servis üzerinden post, put, delete gibi requestler apimize iletildiğinde local storageımızdaki cache’imizin otomatik olarak yenilenmesi bu durumu çözmemize yetecektir. Bu konfigürasyonu yapmak için öncelikle ts-cacheable modülünün içindeki @Cachebuster() decorator’unu servisimizdeki addPost() metodumuza ekliyoruz ve servisimiz artık şöyle görünüyor.
import { Cacheable, CacheBuster, GlobalCacheConfig, LocalStorageStrategy } from 'ts-cacheable';
GlobalCacheConfig.storageStrategy = LocalStorageStrategy;
const cacheBuster$ = new Subject<void>();
@Injectable({ providedIn: 'root'})
export class PostService {
private userApi: string = "https://jsonplaceholder.typicode.com"
constructor(private httpClient: HttpClient) { }
@Cacheable({
cacheBusterObserver: cacheBuster$
?})
getPosts():Observable<Post[]>{
return this.httpClient.get<Post[]>(this.userApi + "/posts");
}
@CacheBuster({
cacheBusterNotifier: cacheBuster$
})
addPost(post: Post):Observable<Post>{
return this.httpClient.post<Post>(this.userApi+ "/posts", post)
}
}
@Cachebuster() decatoru sayesinde artık servisimizden apimize bir post eklediğimizde cache’imizde temizlenmiş oluyor.
Önemli bir not olarak cache temizleme işlemini yaparken servisimizdeki metodlarımızın observable olmasına dikkat etmeliyiz aksi takdirde cache yenileme işlemi çalışmayacaktır.
Gelelim servisimizin başına eklediğimiz
const cacheBuster$ = new Subject<void>();
konusuna, burada tanımladığımız cacheBuster$ sayesinde post tarafından observable olarak dinleyebildiğimiz(subscribe olabildiğimiz) addPost() metodu üzerinden bir işlem gerçekleştiğinde subject tipindeki cacheBuster$’ımız tetiklenerek addPost() metodumuzdaki değişiklik yapıldığına dair getPost() metoduna haber veriyor ve cachedeki datanın silinmesini sağlıyor. (Subject tipinin kullanım amacı Observable olan metodumuzun birden fazla tüketici için aynı anda aynı yayını yapabilmesidir.)
Sonlara doğru gelirken bir kaç faydalı özellikten daha bahsederek son noktayı koymuş olalım.
import { globalCacheBusterNotifier } from 'ts-cacheable';
globalCacheBusterNotifier.next();
globalCacheBusterNotifier’i istediğiniz yerde kullanarak uygulamadaki tüm cachelerinizi silmesini sağlayabilirsiniz.
@Cacheable() decoratorunun içinde maxAge, slidingExpiration, maxCacheCount gibi konfigrasyonları yapabilirsiniz.
Ayrıca IstorageStrategy sınıfını implente edip custom caching strategy oluşturarak Redis, indexedDb gibi ortamlarda caching işlemlerini kolayca yapabilirsiniz.
export abstract class IStorageStrategy {
abstract getAll(cacheKey: string): Array<ICachePair<any>>;
abstract add(entity: ICachePair<any>, cacheKey: string): void;
abstract updateAtIndex(index: number, entity: ICachePair<any>, cacheKey: string): void;
abstract removeAtIndex(index: number, cacheKey: string): void;
abstract removeAll(cacheKey: string): void;
}
Sonuç olarak;
- Angular uygulamımızda caching nimetlerinden yararlanarak verilerimiz için her defasında back-ende gidilip databaseden getirilmesinin önüne geçmiş olduk.
- Databaseimizdeki veriler güncellendiğinde browserımızın localstorage’ında tutulmakta olan cacheimizinde silinerek bir sonraki requestte güncel verinin cachelenmesini ve client tarafına en güncel verinin ulaştırılmasını sağladık.
Küçük projelerde caching’in yarattığı farklar gözle görülecek kadar büyük olmasa da nispeten orta ve büyük ölçekli uygulamalarda caching ve cache yönetimi, kaynakların kullanımı açısından çok önemli bir bir role sahiptir.
Cache işlemlerini yapmak için ts-cacheable modülü küçük ve orta ölçekli uygulamalarda kullanışlı ve kolay olmakla beraber kompleks caching senaryolarımızda ihtiyaçlarımızı karşılayamayabilir.
ts-cacheable modülü hakkında değineceklerim şimdilik bu kadardı, umarım faydalı olmuştur. Aşağıdaki bağlantılardan projenin kaynak koduna ve bu yazıyı yazarken yararlandığım kaynaklara ulaşabilirsiniz.
Source Code: https://github.com/kdrkrgz/angular-caching
Sources:
Be First to Comment