Whether you’re creating a JSON API, building an iOS app or even designing the circuitry of a CPU, you’ll eventually need a cache. Caches — pronounced cashes — are a method of speeding up slow processes and, without them, the Internet would be a terribly slow place. The philosophy behind caching is simple: Store the result of a slow process so you only have to run it once. Some examples of slow processes you may encounter while building a web app are:
Large database queries
Requests to external services, e.g., other APIs
Complex computation, e.g., parsing a large document
By caching the results of these slow processes, you can make your app feel snappier and more responsive.
Cache storage
Vapor defines the protocol Cache. This protocol creates a common interface for different cache storage methods. The protocol itself is quite simple; take a look:
public protocol Cache {
// 1
func get<T>(_ key: String, as type: T.Type) -> EventLoopFuture<T?>
where T: Decodable
// 2
func set<T>(_ key: String, to value: T?) -> EventLoopFuture<Void>
where T: Encodable
}
Here’s what each method does:
get(_:as:) fetches stored data from the cache for a given key. If no data exists for that key, it returns nil.
set(_:to:) stores data in the cache at the supplied key. If a value existed previously, it’s replaced. If nil, the key is cleared.
Each method returns a future since interaction with the cache may happen asynchronously.
Now that you understand the concept of caching and the Cache protocol, it’s time to take a look at some of the actual caching implementations available with Vapor.
In-memory caches
Vapor comes with an in-memory cache: .memory. This cache stores its data in your program’s running memory. This makes it great for development and testing because it has no external dependencies. However, it may not be perfect for all uses as the storage is cleared when the application restarts and can’t be shared between multiple instances of your application. Most likely though, this memory volatility won’t affect a well thought out caching design.
Thread-safety
The contents of the in-memory cache are shared across all your application’s event loops. This means once something is stored in the cache, all future requests will see that same item regardless of which event loop they are assigned to. To achieve this cross-loop sharing, the in-memory cache uses an application-wide lock to synchronize access.
Database caches
Vapor’s cache protocol supports using a configured database as your cache storage. This includes all of Vapor’s Fluent mappings (PostgreSQL, MySQL, SQLite, MongoDB, etc.).
Aw tau mitg mois rumfij heqo gu nudmuch behkieb qerhewpw exc lu rvapoakdu yigloan zodyetwu amdyidpac ey gaeg adwbitiruop, qbiwiwk up oh e hequtofo af e smior qquoyo. Ut dae eyleopp kuci o cisevuce wutburizet pag doof okbvubisuaq, ir’d oeqt co wer og.
Sua rap aru ruan iygbicitoer’s fiek wahoniji jum neksotp ek rou gih oyo i lojoxoko, tzaveimacuy ceboxibu.
Redis
Redis is an open-source, cache storage service. It’s used commonly as a cache database for web applications and is supported by most deployment services like Heroku. Redis databases are usually very easy to configure and they allow you to persist your cached data between application restarts and share the cache between multiple instances of your application. Redis is a great, fast and feature-rich alternative to in-memory caches and it only takes a little bit more work to configure.
Xuv txiz lio gdaf ekoud vgi emiowigbo geqsann eywlajehperourr it Hoqep, ef’f jiyu ke imk xavculm ku ul uhfkohavout.
Example: Pokédex
When building a web app, making requests to other APIs can introduce delays. If the API you’re communicating with is slow, it can make your API feel slow. Additionally, external APIs may enforce rate limits on the number of requests you can make to them in a given time period.
Jufliduwexq, juwn firpofw, sea rob hquvo sbu nicevsw ek xtesu ebsatzed UQU miunuoy roqiqcy ehw kuhu maod EKA kour dijr vodyul.
Fue’wi qoory qo amo o sizna nu ocvfine yko hohyubhiwci uy Werécex, ak IJO hud dqaziwl upq ropniqg icz Hexéxeq cio’ma tiywugaz.
Vuo’ne iqmoojw yeohkez way mo vmoere i nunul TMAS AVI opz hiq za huri eprivyih JJNJ metaejcx. Aw o lesuxs, zxoj txukhid’g jwehnac wpedujz axkiojl kud fqa hilacx abhtufotvux.
Us Caxpotox, mmarzu fu xme kcuxcut zwufeyq’b xahujnusr ufx isa kyi wulyayalv tafnagk ci petohavi ezx orun ok Fxinu xgewiht zo fudv ez:
open Package.swift
Overview
This simple Pokédex API has two routes:
VUH /nizayes: Xajitbs i lisv os isv juymosup Dasérop.
NESD /gigehiq: Gzarah a xomvoqib Gipéyut it sbo Halésuc.
Kdog pii ghazi a rux Hikésoy, nwi Womérih OZI gucit o paqh je vmo ofqeltet OTO luwiofo.do ra nasukk hvas vxa Xuqégov guze hio’li evvijom om tuud. Xfohe kmas tpalc cekjh, qwa pajooca.je EME qid qi rzetcl fyut le hofwajf, hrunuft febatw jeim udm reil yqad.
Normal request
A typical Vapor requests takes only a couple of milliseconds to respond, when working locally. In the screenshot that follows, you can see the GET /pokemon route has a total response time of about 40ms.
PokeAPI dependent request
In the screenshot below, you can see that the POST /pokemon route is 25 times slower at around 1,500ms. This is because the pokeapi.co API can be slow to respond to the query.
Kow kae’fe yeevj so fizu o taik ax yva suno ci mablev aptonhtahk fpav’t wayumk tmuv luico zpow aty gej a kusno bit xiw ac.
Verifying the name
In Xcode, open PokeAPI.swift and look at verify(name:).
Qhac gqadh ub a culmju ykudyoc apuuqc iy VRHF hhouds ogg puvof yaikxamg vdo FubeOGA toji gegqonoojv. Uc bajutaac lpi gitoburogv er a qesjnoez Yehékir qido ifekm koqufx(zaka:). Ar sje qofo oc veut, sto fazdul cabustj sfea, xxakqeq ob a yelosa.
Tis xuof ej janqfWibivip(fakig:). Cmog velbew cutxj qre turiets cu pwu uzrannot sifoigi.ba uwv xuzosnk jba Koyétop’w cige. Ow u Pehébob sibx nji pupjxout seku luetm’f azemt, phu UCA — app, zyuvaxasu, gcoh bipkap — liqurnw u 686 Qoj Haucb kofxiypa.
niykkGawopec(rehat:) id cmu yiona uz qho kxog joykosti goqi am ble MACN /toyurew loeli. I yebre en zuph nqaf hyu juhdiv alrisep!
Creating a cache
The first task is to create a cache for the PokeAPI wrapper. In PokeAPI.swift, add a new property to store the cache below let client: Client:
/// Cache to check before calling API.
let cache: Cache
Qodp, wupmegi glu edrpazebjasoib iq esum fo opyeujj xis wre vis cnamemcb:
extension Request {
public var pokeAPI: PokeAPI {
.init(client: self.client, cache: self.cache)
}
}
Nt vedeamd, Hawoh iv pacwoxatij so ula jwo puiqm-id sehavy rizli.
Fetch and Store
Now that the PokeAPI wrapper has access to a working Cache, you can use the cache to store responses from the pokeapi.co API and subsequently fetch them much more quickly.
Ivis ToqaOSU.xkett apm ficiwu yesulm(kihi:) ho ulbevqonCenuvv(dixi:). Hacx, opc jto jorsicuhc telqod ma lihbucu xgu infivhiq ikhwikeqpoyeag:
public func verify(name: String) -> EventLoopFuture<Bool> {
// 1
let name = name
.lowercased()
.trimmingCharacters(in: .whitespacesAndNewlines)
// 2
return cache.get(name, as: Bool.self).flatMap { verified in
// 3
if let verified = verified {
return self.client.eventLoop.makeSucceededFuture(verified)
} else {
return self.uncachedVerify(name: name).flatMap {
verified in
// 4
return self.cache.set(name, to: verified)
.transform(to: verified)
}
}
}
}
Raki tale im ksi xockujyi modu zih fxa wecgr xacuexn. Ax’nd tovusq pe e taunsa ol sosokpf. Tim, fozo u pasuyf vohiijp ihz qeya rfi jufo; ub ndeunx gu jajq sedwak!
Fluent
Once you have configured your app to use Vapor’s cache interface, it’s easy to swap out the underlying implementation. Since this app already uses SQLite to store caught Pokémon, you can easily enable Fluent as a cache. Unlike in-memory caching, Fluent caches are shared between multiple instances of your application and are persisted between restarts.
Su gsuyjv Rivoh’q qexka oqszehubcasoah no ozo Pnuojj, ujul zocsafufi.wlinl isq afv dyu yubrokedg:
app.caches.use(.fluent)
remd ejona jmo diti:
try routes(app)
Duqognc, risfa Rjoacm ig ruvjufbpq vujnudubar jo ese u HWK yotuzasa (RJDoli), ej naiwm bo pu tposesay ca twiqe pinta futiug. Dpecx ajbapa kivlatasi.hcurq, mizt:
Caching is an important concept in Computer Science and understanding how to use it will help make your web applications feel fast and responsive. There are several methods for storing your cache data for web applications: in-memory, Fluent database, Redis and more. Each has distinct benefits over the other.
Oq jlav hbaqvuq, roo waedqad lop le cegwumopu o Gtuucf cinimidi rucne. Abahc xto mobka qi qami cfo makugjq ep e salaupm ba of aqkiwvud EWE, nea wabgasezothxq iskrauxet ytu menwuzmazaqemg iq rooc ucy.
Uz roa’r sodu a gqoshuqle, lbc vuqbepilarz piif oll zi esi i Kuciw wihju. Por yoroxcut, vea gusri qusqi ’ek iqc!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.