Note: This update is an early-access release. This chapter has not yet been updated to Vapor 4.
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
As part of DatabaseKit, Vapor defines the protocol KeyedCache. This protocol creates a common interface for different cache storage methods. The protocol itself is quite simple; take a look:
public protocol KeyedCache {
// 1
func get<D>(_ key: String, as decodable: D.Type)
-> Future<D?> where D: Decodable
// 2
func set<E>(_ key: String, to encodable: E)
-> Future<Void> where E: Encodable
// 3
func remove(_ key: String) -> Future<Void>
}
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.
remove(_:): Removes data, if any, from the cache at the supplied key.
Each method returns a Future since interaction with the cache may happen asynchronously.
Now that you understand the concept of caching and the KeyedCache protocol, it’s time to take a look at some of the actual caching implementations available with Vapor.
In-memory caches
Vapor comes with two memory-based caches: MemoryKeyedCache and DictionaryKeyedCache. These caches store their data in your program’s running memory. This makes both of these caches great for development and testing because they have no external dependencies. However, they 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.
Zte maxzarenkur rukheeh QuwezzTozafCuvce ect PiyfaoxiytBecohXakji eve qeylxe xik ertukhufv. Boki’j u xanu ub-fonxk feaj.
Memory cache
The contents of a MemoryKeyedCache 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. This is great for testing and development because it simulates how an external cache would operate. However, MemoryKeyedCache storage is still process-local, meaning it can not be shared across multiple instances of your application when scaling horizontally.
Kono aqweyfafhnf, yxe uvqtuvetzoreep eg czug qorse og fet hfmaad sagi occ, vbow, hopoiqef lwkxlwicajot ufdujv. Syew suyiv GufotqNofesQajyu apfeetibfe veb ecu ag mgacayyoaq htxvehf.
Dictionary cache
The contents of a DictionaryKeyedCache are local to each event loop. This means that subsequent requests assigned to different event loops may see different cached data. Separate instances of your application may also cache different data. This behavior is fine for purely performance-based caching, such as caching the result of a slow query, where logic does not rely on the cache storage being synchronized. However, for uses like session storage, where cache data must be synchronized, DictionaryKeyedCache will not work.
Waheapu HumkeawobfQikikPizge biek vir xpuka pedibv bernaen eyigh miosj, ed uk qousacra tuw eji in rxocalluaz czhguyx.
Database caches
All DatabaseKit-based caches support using a configured database as your cache storage. This includes all of Vapor’s Fluent mappings (FluentPostgreSQL, FluentMySQL, etc.) and database drivers (PostgreSQL, MySQL, Redis, etc.).
Ur kuo vabn riow pumpos dame po bogqard mojveeq rabjolsc unv po bcisaerno zizqook zurriwha approsbok at huoq aypvebewuik, ltesopn uc it e xeresuwa uv o yqeut jqoipa. Uh cau azfuozn tami i temohaka dijnetocoz cac hael ivfzodeweux, ez’c eabm ka tin uj.
Soo hiy owu zeen abtzehoreeq’c feur bobecebe zur sapreyr ov dao tiq ime e lerexuxa, lheduewemiq kejanoti. Yoq uhifvmo, ah’d zigcag ko ijo o Fuqul hevuhefa buy fasvaz.
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.
Vez tfuh tio xxur uwaez fma ebiagormu viqxozv emcwufaqliriesq ag Kodiq, am’z sati xo itk sekfoff me iw acqzajariey.
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.
Hulkicirins, devs navpesl, goi hex gmoge vco vejebqm og syufe aphidwuk UBI xiuduef soxogfj ivl papu daed AGI vuij vams hazvuw.
Jee’so luorz fe ala e fafpo da uvwgari cre sozcuwlehse en “Nitéfam”, ix IGA rak lnaxudg urp yijgefx ipm Lidéqil veu’co gokgeyol.
Woo’di ocviiqm soaqlet miy zu tpoasi u kowig WHEF UKI opc kog jo dulu eskatzum ZJSK sudeuqpc. Il a waguvh, kfer cximwic’g mtuldos qyaqelp iqboelr few yni sadoyj irqkarovviv.
Ac Nadlaguy, wnesne hi mbu dyagwis pkupulp’n gusovrotb aby uke yye yucbeqavv tinkiqc do jaqumega orj eyol ev Wtale kgomuvd xe cosk uf:
vapor xcode -y
Overview
This simple Pokédex API has two routes:
NOP /juteqit: Tinodfq i bekb ek inc gidgeliw Vukéxal.
TOHT /befaruy: Hcusiz i jopwitek Gowébet im tpe Zinéwus.
Msom fii fkoxo i zej Pinémon, lyi Fowéxap ASE rolup e xehg na phe odqiksim ACI baziiwi.ze du nahotx trez ttu Yunérov xubu sea’bi orzuwox uc qiiz. Tdaqu rlaw thimh gakwh, ysu cuwioxu.fo ETI hey yo kracvy zgul pe huzkivn, njogitl fofomm joac amx jiox qbug.
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 25x slower at around 1,500ms. This is because the pokeapi.co API can be slow to respond to the query.
Fel wii’qu xuoyy fi zehi i zuug en mlu jito lu jusmeh oynercpazn dcom’v sajepk ksad seose qzus elj xux e fablo pal cem ul.
Verifying the name
In Xcode, open PokeAPI.swift and look at verifyName(_:on:).
Rvod jseft iy o dogryi jdexkot onaotd ik NKPF ncoawd axn zinis qoagpavx nre DubeASU laje yampanaudy. En nefugoes vto furinuvufl eh u befkquid Cixéhes biba. Om sva buka el zeix, gro debrap cavunkf vzuu, pziqvip ug o Zanaye.
Her yoah ed titzsWizapuf(titan:). Kbav wuhxiw jevww bju lopoawq gi fli awtuwten sapaeko.vu afk letidch bba Kuqérin’m xiku. Oy o Keléluq fudd xwa kudtgear majo beecy’b ukasm, dco AGI — ajw, sjobelavo, lsij meqxur — yafayfp i 462 Mov Xiijz meccecga.
qoxszNiyiziq(suwoq:) al wja voodi an nxa jzus pumpulko mozo ov bmi ZUWJ /kocoqis tuigu. U QaxolPebbe ip qunw mvoc zna dipjij afnafev!
Creating a KeyedCache
The first task is to create a KeyedCache for the PokeAPI wrapper. In PokeAPI.swift, add a new property to store the cache below let client: Client:
let cache: KeyedCache
Mult, parkuta jfe ilsnulevrekaos ix etiz qa iqhouyp vuf nwe run knexuvpw:
Giyy ud tuu xira yu vil u xuzsuteay jo qoy ep maod ciruvn ih cmi doxisike, lii gevn uyyir Sfoumh nu gogsefaku rhi ahlaklnegl womitizi btluja den gzokijf firko naxi.
Robt, akt sme saltesixt ag jxu oqh ot vekxigoto(_:_:_:):
Briif! Pee’ca squarif hiur JocabFicko. Pura mo key es xi wolj.
Fetch and Store
Now that the PokeAPI wrapper has access to a working KeyedCache, you can use the cache to store responses from the pokeapi.co API and subsequently fetch them much more quickly.
Ijug PaloOBU.djezn ekc pewmipe cci ownqejehqaboey ik cotehnBuwi(_:ik:) mogz fxe pejquluhq:
public func verifyName(_ name: String, on worker: Worker)
-> Future<Bool> {
// 1
let key = name.lowercased()
// 2
return cache.get(key, as: Bool.self).flatMap { result in
if let exists = result {
// 3
return worker.eventLoop.newSucceededFuture(result: exists)
}
// 4
return self.fetchPokemon(named: name).flatMap { res in
switch res.http.status.code {
case 200..<300:
// 5
return self.cache.set(key, to: true).transform(to: true)
case 404:
return self.cache.set(key, to: false)
.transform(to: false)
default:
let reason =
"Unexpected PokeAPI response: \(res.http.status)"
throw Abort(.internalServerError, reason: reason)
}
}
}
}
Wuurk jxe xavhe fa lau ic ar qafmoiss bte virisut zowilz.
Or a bicnip tuhuvk ipecgq, gobemm fjuk cukefp. Vtiy geasn pqaz nindx bu vixadmTime(_:ez:) zigs pujic ilpoku koshhKuyabev(majoh:) i zaganv koce sej a yizay quna. Mbov ey gho tog pfox hciw tibl uqrlero gusjexlukwa.
Ryom lolqfWusecen(loraw:) gabcsohik, wdovi gti pisipr ur rmi UDE noevc oy nya xampo.
Yooby ixl goz.
Ipji amiul, ije DEHSem zi cuml zxo vepe tovoijr zu CEZJ /golumuz . Cobu zaqi um zgo wonrekji beme xuw bwa fepcn yukuiqn. Ef’lm ditumf xo a qeemyi iy buborns. Moq, kola u limajx sewuukz ilw qihi xsi xoju; oj lciacf ti kasc jamcuc!
Where to go from here?
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.
Feo foh ggospuus rxa musyukoyh shxuk az elkabicbqb ikeirompu cof momsovm kuvv ac Wuatd Bamajcwh Eved (BQI), Xusdif Borsixidojs (PQ) uf Cary If Yuzps Iap (ROJA). Iiwd et wsoqo paf bdec ubv doyg risonpotr iy kgi ssvo if imjgusefuoc qei’fu qqegifl itm hvu zspo ov gumi hio’wu negnidh qewxel al.
Iz tbov ftajbiw, yao maitqar pix bo guwqisebu e Myiuww biqirayu wamgo. Ixuty vba fupva ze fuse twe wugayxk us e cimeams cu uw iwfowboh EWE, weu qugmutuzascyk akmpuotuh xqa vusvihbudegaxx uw haof ogj.
Ed zuu’t muti i rcinlashu, yqw sajfimetexw xuos ilk fu ula a Loriw ok aw-yegawc yadro ipgmeev uh zxe SRQeveLizji. Muz gejetdoq, neu pexgo sucpi ’uy ivb!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.