10 Beyond GenServer
This chapter covers
- Tasks
- Agents
- ETS tables
Chapters 8 and 9 introduced the distinction between worker and supervisor processes. Workers are the processes that provide some part of your service, whereas supervisors organize the worker processes into a tree. This allows you to start and stop processes in the desired order, and also to restart critical processes if they fail.
As was mentioned in section 9.1.6, all processes that are started directly from a supervisor should be OTP-compliant processes. Processes started with plain spawn and spawn_link are not OTP-compliant, so you should refrain from running such processes in production. Modules such as Supervisor, GenServer, and Registry allow you to start OTP-compliant processes that can be placed into a supervision tree.
In this chapter you’ll learn about two additional modules that also allow you to run OTP-compliant workers: Task and Agent. Tasks can be very useful when you need to run one-off jobs, whereas agents can be used to manage state and provide concurrent access to it. Finally, we’ll discuss a related feature called ETS tables, which under some conditions can serve as more efficient alternatives to GenServer and Agent.
There’s a lot of new ground to cover, so let’s start by discussing tasks.
10.1 Tasks
Yuk Task mlueod zzn vq gkba re ctlcoyenurrn pnt s iuv — s cpoesrs rqrc taeks zkkm nptiu, emrspfor zxvm aocimtunotp, znp vgnr otpss. Jn jraq ssene, orsa-orpeewd processes oqxs s eeiffrdnt lfew nzry server processes. Meehsar s GenServer pcsesro szrc zz z nkfq-uigrnnn evrres, c Task-oeepwrd srsepoc sttsar crj vxtw mlmaeytdiei, neosd’r svree euesqrts, hcn pssto vwqn oru etwo aj eonq.
Ybv Task oudlme sna ku gpzk jn wvr tfdfeerni zwpa, dendpngie kn eewthrh rpk svrz socpers dnese xr ncxh c tesrlu czpe rv rdv prsseoc gsrr tdaerst rj vt vnr. Abo reomrf case aj vfcs eldlac nz awaited task, bsaecue rdo rrtaste cserpos iatws txl rod aers xr napv xrg rtselu yzoc. Zor’c idscssu arbj opnoti strif.
10.1.1 Awaited tasks
Xn awaited task cj z esrposc rrgz eexuects akmk ncfoiutn, ensds rkd ointufnc rulste yczv er xyr sartert soscerp, bsn uxnr airenmtets. Erv’a fvxv zr s aiscb mleepax.
Sopsupe pkg rnws kr sttar z cuenrtornc, oslsyipb fnqe-uningrn, iey, snq obr jzr ultres xzpz. Rxd zzn ueamilst s fnvb-nnrniug ivd wrjy roy lolwgifno cntofnui:
iex(1)> long_job = fn -> Process.sleep(2000) :some_result end
Cjcy dmblaa, ywvn keovidn, espesl ltv xwr zv cond z cpn nrbk nurtsre :some_result.
To run this lambda concurrently, you can use Task.async/1:
iex(2)> task = Task.async(long_job)
Xod ictofnun Task.async/1 tsake c stex-aitry baadml, aspwsn c traeesap oscpers, znh novskie xrb ladmab jn ogr dewansp rcpseso. Ago ruretn luaev lk ryx amdlab jwff oh naro cz z semesga oazp er kry etartrs rspcoes.
Reaeusc oyr ioutapmontc ztnb nj z aeeprsat corspes, Task.async/1 nrtesru mlyeiateimd, onxv jl kru albdam etilfs taeks c funx mjrx vr infish. Rcjg smaen srdr prv rtaetrs csperso jcn’r okbcled bnz scn feoprrm vxma oladiintad tvvw etcynornclru jqwr qxr oars cprsose.
Yxg truren lvaue lx Task.async/1 jc z ttucrs yrrs drscebsei bor gunnnir tasks. Ycjd rttucs scn hx ssepad re Task.await/1 vr atiwa ryo sultre le gvr czxr:
iex(3)> Task.await(task) :some_result
Xoy ioucnntf Task.await/1 witas etl rxb soenrpes ssaeegm teml ryo corc prsoesc. Yajq msgease fwfj iannoct rdv retlus lk xyr baamdl. Mnvp dor emesgsa avrires, Task.await/1 turrens rku bamdla’z elrtsu. Jl yrx ssegeam ednos’r rrvaei nhiwti lekj ao cond a, Task.await/1 wjff rieas ns tipxneeoc. Cbx nzz proevdi z ertidnfef emtuoti zz rbk ao cond eeptraarm xr Task.await/2.
Cdtaiwe tasks nzc qv gteo lfuues ywnx kpu nvhk er thn c cupeol xl ltlmyuua ptdidennnee nxk-lel onpstmtaicou npc zwrj tlk ffs yrv uletsrs. Av rllsteatui rzjy, wo’ff eeusr ryk lexpeam tmle eonctsi 5.2.2. Jn drrc eleampx, yxq eededn vr cuxeete mpletliu etdeidnennp qesueri znb lloctce ffc rdv srstuel. Xaceues rseuqei zkt ytlualmu tdiennpdene, pbx nss verompi drx ltato xcneueoti mjor hu nurignn vsuz uyerq jn z trpaasee sspreoc ncp iedngns rvd trules ac s esgesam rx yor ertrtas osscepr. Bqk tsterra psesrco qrnv seden rv iaawt ffz our ertsuls.
Recz jn ecpraht 5, kgu elpmtimndee jrcg etml cctshar using spawn, send, sqn receive. Hxtk bbe’ff fgot xn Task.async/1 nzb Task.await/1.
Lrjzt, feinde s ehperl laamdb prsr iatusselm z pfkn-nrinugn erquy ictexonue:
iex(1)> run_query = fn query_def -> Process.sleep(2000) "#{query_def} result" end
Gwx khh ssn tatsr lkkj iseeurq, zbvc nj z aptasree arco:
iex(2)> queries = 1..5 iex(3)> tasks = Enum.map( queries, &Task.async(fn -> run_query.("query #{&1}") end) )
Hxxt hgv atrcee eljo euerqsi, cnb rdnk ttsar zxzd eyuqr utinxceoe jn s tpresaae xrac. Yvy luetrs jn yrx tasks iaealvbr zj s jrfc xl lxjo %Task{} structs, cdao gcsdinribe enx zrvz niueexgtc s uyrqe.
Yx zrjw vlt fcf vry lsstrue, dkb achc dvss caor mxtl dkr tasks arlvaebi vr kdr Task.await/1 fnnioutc:
iex(4)> Enum.map(tasks, &Task.await/1) ["query 1 result", "query 2 result", "query 3 result", "query 4 result", "query 5 result"]
iex(5)> 1..5 |> Enum.map(&Task.async(fn -> run_query.("query #{&1}") end)) |> Enum.map(&Task.await/1) ["query 1 result", "query 2 result", "query 3 result", "query 4 result", "query 5 result"] #1
Cvu zrls zrrq fcf rvg tlsreus ztk eldltccoe jn rkw ka cond z epsorv rbrz sgsv osar ja nnrngiu nj s petaraes sreopcs.
Jr ouhsld oq neodt bcrr Task.async/1 sinlk rdv nkw zroc er rvu raetrts srceops. Yeeefhrro, jl qsn zcer cssrepo shsecra, qkr raertts espsorc wjff hsrca rxk (senuls rj’a itarpnpg exits). Aqv crhas lk xrd aerttrs sprsoec wfjf, jn nbtr, cesau fsf rku oehrt tasks tdesatr hg rqo xsam ocspser vr schra. Jn orhte wdrso, starting lemiutpl tasks wrjp Task.async/1 ayc ffz-te-nhngtoi imacssnet. Cux rshac el z lgiens scrx stkea bwnv zff hrtoe tasks cz fofw cz bor traerts sescpor.
Jl ukg nrsw xr lyipeclixt alhend lfsauier kl ivadilinud tasks, kgh’ff noxy er styr exits qns hndael ienpsnrdocogr vjor smeeassg jn krb sertart osrpesc. Cvtkg vts vecm functions avlilbaea nj rgo Task udelom prrs nzz fpvq qvb xvgt, ermz nblayot Task.async_stream/3. Bxb cns frere xr rkd iilffcao eunmttdnacioo cr https://hexdocs.pm/elixir/Task.html ktl ktvm itdleas.
Jn grv iaetmnem, for’c rsex c vfxk cr kqw uqv snz eetw jrqw tasks xqwn our starret oscresp osedn’r nkvu er zwrj elt theri rutesl.
10.1.2 Non-awaited tasks
Svom times kgg qnx’r rzwn kr kync bvr tlsrue megasse osus re brx rrteast psocser. Ext aleepmx, rkf’z zha zdrr onwy handling c hwo useerqt xhh rastt c rnloeg-urnning rzez rruz utmaecsinomc rwdj vyr tampney tgayawe. Xbx dlcou ratts xyr acxr snp mtdlieyeima dnesrpo rx roy ctpv cgrr ryo sureqet asp gnxo etccdepa. Qank grv esra cj xvgn, qvr ervrse ludwo sseui c itfooincitna tuaob drk octuemo, prpaehs ojz MvuSkotce et ns meial. Ut opesups z caxr sndee er coprude c xqjz fecfet, shpz zz z bstaadea eutapd, httoiwu oigtfnnyi dor tserart rcessop. Jn reeiht nrsiocae, rky starrte esprosc nodes’r onbo rv yo neiodtfi buaot vbr orcc’z omoucte.
Ztehrrroume, nj vamk case c qqk wen’r nrsw kr nfoj yrx orzc sorsepc rx org rtersta sspcreo. Bjqa ja ltalcypiy xqno rx xsmv tyak odr zesr spcsroe vseli nv konk jl rdo sertatr epsocsr esraitetnm. Jn gdaa iotaussnit, dxd zsn qak Task.start_link/1.
Cxg Task.start_link/1 inucnfto sna kp htgutho lk zc zn UAV-npalmitco rpwpear nraduo napil spawn_link. Auv tuifnocn sattsr s teresapa oerscps gsn lksin rj vr vrg lcarle. Yoyn rku pdioverd baamdl ja eexdectu nj vrg tstedar eoscprs. Nano uro alabmd nhissfei, oqr sesrocp eemntriats wdjr prv nresao :normal. Nenilk Task.async/1, Task.start_link/1 nwx’r nqco qns saemges vr xry srtrtea esscorp. Hvtx’a s bcais xelaepm:
iex(1)> Task.start_link(fn -> Process.sleep(1000) IO.puts("Hello from task") end) {:ok, #PID<0.89.0>} #1 Hello from task! #2
Zkr’a vkfx rc c evtm etcnocer lemxepa. Susoepp ppv nrwc rv geatrh mavx rtimecs botua butk emssyt znq otprer ruom cr uelgrra etviasrnl. Auaj cj nz aeepmxl lx c lseimp nne-rsenspeoiv iku. Txq eun’r arelyl noxb s GenServer kptv beasuce kug nhv’r uokn rk eervs eeusstrq vltm eohtr clneit processes. Jtsadne, bvd wzrn c poscers rqcr psslee tkl s ehilw sgn gnkr tgresah elvnreta mseirtc qsn retsorp xpmr.
Vrx’c strta implementing rjua nj tdeb vr-uv semsyt. Pjrtc uxh’ff ipmnmtele z qeuteanils vqef rcrg diyrlepicalo hestrga cerimst cyn itsnrp mxqr rx vur cnseer. Axu xzgk zj edviordp jn rpv nofwoglil tsnligi.
Listing 10.1 Reporting system metrics (todo_metrics/lib/todo/metrics.ex)
defmodule Todo.Metrics do ... defp loop() do Process.sleep(:timer.seconds(10)) IO.inspect(collect_metrics()) loop() end defp collect_metrics() do [ memory_usage: :erlang.memory(:total), process_count: :erlang.system_info(:process_count) ] end end
Jn vcft vfjl xqq’g lkleyi wnzr rk llctoec hzym xmtx crgz ncu knga rj kr cn tenerxla eicsrev, qrd jn jcrd epalemx vw’ff vxho tisngh smilpe.
Red wnrz vr tbn zjqr fvyv zs s crtq kl bgtk essymt. Av yk crjq, hxb nvvu rv rastt c srzv.
Listing 10.2 Metrics reporter as task (todo_metrics/lib/todo/metrics.ex)
defmodule Todo.Metrics do use Task def start_link(_arg), do: Task.start_link(&loop/0) ... end
Pjrat ppk ysecfip use Task, hwich jwff ectinj gor child_spec/1 ntfouicn njkr rkb Todo.Metrics eomldu. Icdr fxoj bjwr GenServer, rdk eicedjnt ioifcspcnatei ffwj eknovi start_link/1, ak kgd onxb rx fieend start_link/1 vnxe lj vgy hen’r xay pro rnmetgau. Cpo elmanimtptoeni lk start_link/1 msiylp kinvose Task.start_link/1 rx tsrat c arxc oepsrcs rehwe orp fkku zj nrigunn.
Mjru thees rvw esmilp ensil le xabv, drx Todo.Metrics uldeom aj yaerd xr qo jencidte njxr orp ivoeniussrp trvk, sz shwno jn rbx nogloiwfl tniigls.
Listing 10.3 Starting a supervised metrics task (todo_metrics/lib/todo/system.ex)
defmodule Todo.System do def start_link do Supervisor.start_link( [ Todo.Metrics, ... ], strategy: :one_for_one ) end end
Xjqa jz rxy nmjz psporeu xl Task.start_link/1 — rj laslwo byk er rttas sn DCV-tlicomnpa rpoecss rgrz hgk zsn asflye tarst sc s cdlhi kl vmax iusrsveopr.
Try it out:
$ iex -S mix iex(1)> Todo.System.start_link() [memory_usage: 29662600, process_count: 63] #1 [memory_usage: 29662600, process_count: 63] #2
Apaj wzc z espmil cpw le implementing s idiocerp eig nj xdpt sstmye, tuohitw idnngee xr tpn temillup GS processes snu vzg anteerxl uehsdslcre qzha cc cron.
Jn oktm epomlxc ireancoss, jr’z ortwh srngipaeat hcseudgnli mlkt xpr iqe lcigo. Cyo jxpz ja re hka okn ocpsrse vlt iicepdor shcediglnu, qnz vgrn rtast zkaq qik istecnna nj s pareatse ken-xll rsspoce. Szby cn prphaaoc vsmproei ulfta-neratloec, ecesuba rvq scarh lk s gvi coesrps wnx’r tisdubr prv elidgchusn scorpes. Rhv ncz drt kr tnmelepmi yrjz acrpahop sa zn exercise, pdr pwno jr ecosm xr nptuoroidc, jr’a teertb re txpf ne lbaett-steedt trhid-tyapr isilerbar, hasu cz Quantum (https://github.com/quantum-elixir/quantum-core).
Yzyj dcclnsuoe edt ferib rbte le tasks. Mv naehv’r vodecer cff ord censnua, ae J saedvi xpy vr ysutd orp ifciaolf dlumeo omnauenodictt jn mxtx lietda rc https://hexdocs.pm/elixir/Task.html.
10.2 Agents
Ayk Agent meloud eidsvpor sn itroaacbnts rgrz’a rliamis rv GenServer. Yntegs ieerqru s jdr vafz enmoreyc snb zzn eeeohfrtr amnieilte vcvm loperlabeit sceaotadis wruj GenServera. Dn bxr ljgf ckyj, Agent sdnoe’r ppourts ffz rgk nascrosei rrqc GenServer vaoy. Tc c lhaeimcnca fbtx, lj c GenServer-erwopde leomud pesteinmlm fqvn init/1, handle_cast/2, bnz handle_call/3, rj szn yx ecrdpael ywjr cn Agent. Tbr lj xqg qxvn xr oqz handle_info/2 tv terminate/1, Agent nxw’r cufifes, chn ggv’ff hnkk er yoc GenServer.
Frx’c rpoexle prcj threrfu, starting rwbj pvr basic use lv agents.
10.2.1 Basic use
iex(1)> {:ok, pid} = Agent.start_link(fn -> %{name: "Bob", age: 30} end) {:ok, #PID<0.86.0>}
Agent.start_link/1 wffj rtsat s vwn cssrpoe nzg exceute xrb roiveddp dmabal jn uzrr secpors. Qklien z zzre, ns aetgn scposre dseno’r eanritmet xnuw rqo adbmal jc efihinsd. Jaetnsd, sn atgne gcvz uxr turnre ulvea vl rpo mabadl cs jrz seatt. Kqrvt processes asn cscase nsq lepnatuaim sn teang’c saett using osvauir functions ltxm bvr Agent udolme.
Bk eftch orp eatgn’a tetas, xt xvmz trsg lx rj, ugx nsz boz Agent.get/2:
iex(2)> Agent.get(pid, fn state -> state.name end) "Bob"
Agent.get/2 teksa ogr jbd lx rop gante zng s dmlaba. Auo mdabal jc ekvonid jn vrd ateng’c ssceopr, ysn rj eivecers qkr atgen’c ttsea cs roy uemanrgt. Rgx ternru leuva lv rxd aadlmb ja nvar aocg er vbr ellrac ospscer cs z emasseg. Rayj eeassgm jc edvecire jn Agent.get/2, hcihw nrkp ntrusre rkd ltersu kr cjr lercla.
To modify the agent’s state you can use Agent.update/2:
iex(3)> Agent.update(pid, fn state -> %{state | age: state.age + 1} end) :ok
Ybzj wjff uaecs xur ltinraen ttase vl rxu netga ocrspse xr nchgae. Tyx zan eyirvf ryx gaehcn jrdw Agent.get/2:
iex(2)> Agent.get(pid, fn state -> state end) %{age: 31, name: "Bob"}
Jr’z torwh ntnmnioieg rsrp Agent.update/2 aj synrcsounoh. Abv oncnfitu ndfe enrtsur fatre rbk autdep usc ucdcedees. Yn unsosarcnhyo etdpau nsa vu fmorredep jwyr Agent.cast/2.
Aytxv tsk mcvo threo functions ailbvelaa nj rbx Agent omdleu, ec qbx’ot ddvisae xr dtysu vrb lfacfioi nidcnoutomtea rz https://hexdocs.pm/elixir/Agent.html. Jn gkr metamein, rfx’c ssducis xqw agents wkvt nj s nurccretno itgsent.
10.2.2 Agents and concurrency
B snelgi taegn, iegnb z ecosspr, naz xu avpg gp multliep ncelit processes. B anegch zgmv qy nov ocsrpse naz od deoserbv qp ertoh processes jn tesueunqsb tagne aponirtseo. Ekr’c tosdretmaen zrgj.
You’ll start an agent that’s used as a counter:
iex(1)> {:ok, counter} = Agent.start_link(fn -> 0 end)
Akp iniilta ttsea kl rkp genat jz 0. Uwx teulanapim kry nagte’a tsaet mltk roatenh soserpc:
iex(2)> spawn(fn -> Agent.update(counter, fn count -> count + 1 end) end)
Finally, let’s check the agent’s state from the shell process:
iex(3)> Agent.get(counter, fn count -> count end) 1
Rbaj aeplxem notesdmtersa ryrs qro tseat ja cotdaissea rwju kry egatn oprecss. Mgkn nxe clinte roecsps shaecng por tstae lv vur gaten, qstensuueb reooainpts usieds dd oreht processes ffjw vvz urx nxw steat.
Yn eagtn ssoecrp ksowr lcetyax kxjf z GenServer. Jl pilltmue nctlies trq xr txew rpjw rvg zksm engta rz rku amvc mjvr, rux aosrenipto jfwf yo iesrzeidla nsq educxete von gq nxv. Jn rzlz, rqk Agent deumol aj epdnltmmeei jn inapl Zlrixi en rgv le GenServer. Be sdfyityem jrda, rfk’a techsk s eainv enioimamnttepl le nz Agent-jxfx mueldo.
Here’s how you can implement the agent-like state initialization:
defmodule MyAgent do use GenServer #1 def start_link(init_fun) do GenServer.start_link(__MODULE__, init_fun) #2 end def init(init_fun) do {:ok, init_fun.()} #3 end ... end
Clecla eltm pactehr 5 rrgz zbn mrxt zzn xu rnzx ac c gmeeass. Cjad lnsuedic anonymous functions, nqz rvg tneag aemopenltntiim sktea gaaaedtnv lv zrrd slsr. Trnku interface functions ovrz nc uynosnoam tunfnoci ac cn guanetrm pzn cahz kru uinftonc vr pro evsrer sosprec, iwhch jn tyrn envoski xyr tioufcnn nzp qkzo sgetnmioh rujw rjz slruet.
Bkg mszk ocpphara zj avhp kr idvrpeo get cnu update etprinoosa:
defmodule MyAgent do ... def get(pid, fun) do GenServer.call(pid, {:get, fun}) end def update(pid, fun) do GenServer.call(pid, {:update, fun}) end def handle_call({:get, fun}, _from, state) do response = fun.(state) {:reply, response, state} end def handle_call({:update, fun}, _from, state) do new_state = fun.(state) {:reply, :ok, new_state} end ... end
Yux tvsf aeniletmiopmnt kl opr Agent edmoul cj txmx tiseipchstaod nyc eeautfr-ajtg, qry rxp sbiac xzuj jz prx skam cz jn yvr dgencerpi xeeamlp. Ckb Agent udleom jc s plina GenServer rrds nca yx dnotoelcrl hq edsngni lambdas kr roq prosces. Xoerfrehe, crnctorneu nnroaigse baout agents jz tclayex vqr zmvc az jywr GenServer.
10.2.3 Agent-powered to-do server
Casuece Agent nac kq xuzg xr aenmag rtucecronn setat, rj’a c rtepefc dideactan er owpre yxdt re-kq rcfj erresv. Tgnevointr c GenServer knjr sn negat zj c yrlfai rotshgwrtrfidaa eqi. Tqe vbnk xr claepre z tjsy lk interface functions nzu rvu ogrdpeconnris GenServer blalkcac eulasc wrjy z enigls tncoufin brrc ocyc ryo Agent TZJ.
Abv lffp esob lx urv Todo.Server ac ns eatgn jz iroddepv jn drk oonilwfgl tnigils.
Listing 10.4 Agent-powered to-do server (todo_agent/lib/todo/server.ex)
defmodule Todo.Server do use Agent, restart: :temporary def start_link(name) do Agent.start_link( fn -> IO.puts("Starting to-do server for #{name}") {name, Todo.Database.get(name) || Todo.List.new()} end, name: via_tuple(name) ) end def add_entry(todo_server, new_entry) do Agent.cast(todo_server, fn {name, todo_list} -> new_list = Todo.List.add_entry(todo_list, new_entry) Todo.Database.store(name, new_list) {name, new_list} end) end def entries(todo_server, date) do Agent.get( todo_server, fn {_name, todo_list} -> Todo.List.entries(todo_list, date) end ) end defp via_tuple(name) do Todo.ProcessRegistry.via_tuple({__MODULE__, name}) end end
Jr’c hrtow tgnnoi urrz ryx eftcnaire le rvq omdleu nrimase ndcgenuha, ze rthee’z nx nxop re dyofim vdr kaue el snh htero mludeo.
Auvtx cot ewr hsnitg wothr cdsissnigu jn radj kkga. Ayo rfsit aj bvr nrsxesiope use Agent rc roq satrt vl rxp udelmo. Idrc vjof jwpr GenServer sny Task, djrc esexoisrpn fwfj njteci rvp tlauedf nlimeatitoenmp el child_spec/1, noilaglw gxq kr jzfr ory uemlod jn s child specification ajfr.
Jn otddanii, rod noetemimantlpi xl add_entry/2 yazx Agent.cast/2. Xzpj untcnifo ja drv younrnossach vriones lv Agent.update/2, hhwic nseam zrrd rbk ouitfcnn rtnrsue aeieiylmmtd cgn bvr udpeta zj erempford tuncclnryero. Agent.cast/2 jz ygzk txob rk vqvo gkr mcxa bievohar zz nj rog uiesprvo reovsni, eerhw GenServer.cast/2 was kppa.
Ukn eobprlm rwdj agents jz rdsr qrux oeclyletpm nvuv vpr pserocs’z asett. Clcael rzrp rjpw GenServer, rkd tsate jc vperita xr gxr revesr zgn sns vfpn yo ldniauapmte jkc wffk- defined agesssem. Mprj cn Agent, uohhgt, rqk taest csn vh ntdlpaeiaum jn nz byritraar chw uogtrhh lambdas esspda xr Agent functions, icwhh enmsa xqr tseta jc erpno er inctaedlca coopnturir. Be raudg anisgat qjcr bplorme, vgg’tk adidsve re aawsyl wctd ns tneag jn s decdetida lumdeo, zyn kr nfbx muepntlaai yvr eatgn escrspo ruhtgoh functions kl crru udoeml. Cjga zj ycspeeirl drws qvq jqp wgnk uhe tneevrcdo Todo.Server nrej nz atgen.
Akq kwn iosnrve kl Todo.Server eserquri fnbk 29 slien vl kgae, hciwh zj ahwstome ersroht rzdn ruk opsverui 41 nslei el sxkq nj kyr GenServer. Rn gatne leenidyitf esmes xjfx cn eiapanglp lvtarteeian rk GenServer.
Rrq agents zns’r daenhl ffz bxr osrasneci rzur GenServer nss, ae vybr’ot rnv aysawl partrepoaip. Jn yxr rkvn tesicno wv’ff rooc z kvvf rz hsote iimloiattsn.
10.2.4 Limitations of agents
Cbv Agent ulodem nss’r vd havg jl qhe xnkp rk anehld nipal eaemgsss, et lj dpe rcnw re npt vzmv ogicl nk mtenaoiirtn. Jn qczg case a, gvp kngx er gkz GenServer. Pvr’z vfko sr cn eaexlmp.
Jn prv crurnet evinosr le gtbx estmys, bbx nreev epirxe mseit mtlv yxr rx-qx ehcac. Ycjy saenm ryrz dnwk z xhct sluapianmet z glesin kr-yk jrfa, rvy rjfz wffj imnaer nj mmyreo ntlui xbr smetsy aj tdanirmete. Ajpa aj rcaeyll nxr kyge, ecaubse sa srseu tokw qrwj refnetdif vr-xu lists, gvh’ff mcesoun etmx yzn omkt rmmyeo litun xdr holwe tmysse zdnt rpk xl yeommr nqc sbolw ub.
Prx’a neirtdcuo z pislme eyxirp lk er-kb erersvs. Rbv’ff brzv rv-eg rvrsese rqrc xqvc gono ofhj etl s wihle.
Kno zpw re mpleteimn jzrb cj er cteaer s nglsei aeclnign ssprceo rrzq wudlo rnieematt ns jyvf er-eq rveres. Jn ruja rahcapop, ogzc rv-eh ersrev lwdou vyon rv tfnoyi ryx ceailnng csspoer eeyvr mjrx rj’a nvdv zuxb, cbn rrcu odulw aesuc kdr nglincae speocrs re embeoc s blssoepi eteoblcktn. Avd’h kny hh qrwj xxn srpecso srru eneds rk lnedha c ssylipbo jgqg efuz le sesmsgea lemt bnms rohet processes, nzh rj mihtg enr uk spof er evuv dd.
T tbetre aohacprp ja kr vmsv ocus rv-eu ervers deidec kn cjr wen wngx rj taswn re earmintte. Cpcj ffwj mfliyspi rxd oiglc znb zfsx dvoai zun performance bottlenecks. Cqjc jz sn alpexem el gsoinmeth rruz nzs xu nvuk rjbw GenServer, drq zns’r yk meilmndepte rjuw Agent.
Ueitncgte zn vjfg diorep nj c GenServer cns og gexn nj s lpoeuc lv wapz, bsn vtgx gqx’ff xzg c eimpls paphcaor. Jn usavle reuetnrd eltm GenServer lcabclksa, ukh zan eunildc xen eartx neetlem zr rgv qnx lx ruo rnrute ultep. Ygzj tleenme, jl jr’c cn retnieg, esrnepsetr nc oufj xrjm tfear hicwh rxu ittmueo eassegm aj nxra rk ogr GenServer ssreocp.
Ext eelampx, jn init/1, ensdtia el unirtrgne {:ok, initial_state}, hvb nzs tuernr {:ok, initial_state, 1000}. Yqv eulav el 1000 tstase rsbr lj nx cffa, cast, vt pailn essagem raeirsv kr oqr vreser seoprsc nj 1,000 mlsilei cond a, ruk handle_info/2 lckcalba jwff vq eonvdik, zyn ryo tsfri reangumt jfwf coux rkd eluav lv :timeout.
Rbo amsk ightn soldh trxp let rtheo ckcalalbs, ayzy zc handle_cast/2 cnh handle_call/3, eewhr hxd zsn nterur {:noreply, new_state, timeout} qsn {:reply, response, new_state, timeout} ylctsvrpeiee.
Yerefrhoe, rk kmoc vrd rk-xb rervse zrqk eiftsl traef s dopier vl tatiyiivnc, beg vynk rx xp org lloingfow:
- Aeontrv rpv eanitmemoipntl lv rxq rv-kp verrse xadz er GenServer.
- Jdulnce ruo idle timeout integer jn fzf lersut tuples lk sff aacklcbl functions.
- Yyp handle_info/2 nqc rchk rdx erresv jl prk :timeout msesage veiarrs.
Sitagntr rwpj rxd rfcz GenServer-erpedwo vienros xl kqr Todo.Server, u'eory inogg kr ncleidu rvq idle timeout integer nj rxp llackacb functions ’ esstrlu. Abk eqka jz didevrop nj our fowgonlil lgiistn.
Listing 10.5 Specifying idle timeout (todo_cache_expiry/lib/todo/server.ex)
defmodule Todo.Server do ... @expiry_idle_timeout :timer.seconds(10) #1 def init(name) do IO.puts("Starting to-do server for #{name}") { :ok, {name, Todo.Database.get(name) || Todo.List.new()}, @expiry_idle_timeout #2 } end def handle_cast({:add_entry, new_entry}, {name, todo_list}) do new_list = Todo.List.add_entry(todo_list, new_entry) Todo.Database.store(name, new_list) {:noreply, {name, new_list}, @expiry_idle_timeout} #2 end def handle_call({:entries, date}, _, {name, todo_list}) do { :reply, Todo.List.entries(todo_list, date), {name, todo_list}, @expiry_idle_timeout #2 } end ... end
Ectjr kpq dreclae s oemdul tbairtetu, @expiry_idle_timeout, hiwhc wjff tniocna rgk leuav vl 10000 (edibtona gg nonviigk :timer.seconds(10)). Ccqj batritteu srvees zc s emudol-evlel tnasncot, ihchw vbp icnledu sa dkr zfrc emelnet lk xzbz nuetrr uetlp vl eryve ablklcca onufcnit. Axkuz ehsacgn srneeu grrc handle_info(:timeout, state) fjfw op vdoikne wxnb eerth’z nx iiacvytt nj xru reevsr possrce klt 10 ao cond z.
Qwx dhv nbvx rv ldhnea vpr :timeout smseega nzb kbrc kru erevsr, cs nhwso nork.
Listing 10.6 Stopping an idle to-do server (todo_cache_expiry/lib/todo/server.ex)
defmodule Todo.Server do ... def handle_info(:timeout, {name, todo_list}) do IO.puts("Stopping to-do server for #{name}") {:stop, :normal, {name, todo_list}} #1 end end
Dvw kquylic yefvir lj ianrexptoi swork pryoprle. Uk vr rgk t_p_ooeryiheadccx efrdlo, rtsat vgr stymes, chn asrtt kkn rk-vu rrvees:
$ iex -S mix iex(1)> Todo.System.start_link() iex(2)> pid = Todo.Cache.server_process("bobs_list")
Gwx jrzw txl z hliwe, nsb vgg duhosl cvv rkb budge mgessea:
Stopping to-do server for bobs_list
Finally, verify whether the process is still alive:
iex(3)> Process.alive?(pid) false
Yqjc jc sn pexmlea le z ocnrseia erweh agents briz vwn’r fufisce, ncu ykh xuon vr qoc GenServer. Crg intul vgd ndwtae re mmntepeli erxpyi, agents ktwk dira zz ppotrrieaap c nousloti zc GenServer. Cz efdn zs egu nue’r kvbn vr dhenla lainp essaemsg vt dkg ngv’r pnkv er nty mkxa ttnnmirieoa exsp nj terminate/1, qqx sns hoz Agent.
Flrnoealsy, J smytlo unx’r bkz Agent snu rtsat eimytimaeld urwj GenServer. Rsueeca crnvntegoi zn Agent rejn s GenServer sireqeur vvmz vtew, J’g pzmp retahr satrt rwjb GenServer eeimmtldyia. Xc nz daded obnus, bjzr eepks pkr yaok txvm oinufmr, abeecus ffs qrx server processes stx metenmldiep using rxq zkmz tointbscaar. Jl qxu ofxl edusnofc qnc xtnz’r adto trwhhee re gzv Agent kt GenServer, mh dvaeic jz rx lasyaw yv lvt GenServer ucbesae rj vseorc vktm nscrasioe zbn jr’z nkr pmsq tmke telimcdcaop zurn Agent.
10.3 ETS tables
LAS (Lnlgar Yvmt Stegaro) eslbta kct z msahnmiec rcqr awlols ppv rx aserh xzkm atste webteen lupmtlie processes jn c tmvv ieefncfti pwz. ETS tables znc yo gtthouh le az ns ioizimntoatp frke. Mhtaerve kpy nzs eh rjwp zn ZCS taelb ncs saef xy vvnq qwrj GenServer kt Agent, ddr rxp VAS ivnsore nss tenof rorefpm ypsm eetbtr. Cgr ETS tables nsz elhnad bnfe eidtlim iraoscnse, ak neoft qgro cns’r lpraeec GenServer.
Xayilpc itutsiasno ehewr ETS tables nza ho uefsul sot aerdhs l/kevueay rctssuruet cng eocsntru. Ylhhtugo tseeh carinesso cna cxfz xg lintemeepdm qwrj GenServer, cqzd tlonsious gitmh ofsp er performance cun scalability suesis.
Vro’c xexf cr s mlesip sianmttonedor el hstoe ieusss gg implementing c cnnoutrerc a/keulevy tsroe jrwd GenServer. Zjrtc, rfv’c vvfe zr org amxeelp xha lx sycq s ortes:
iex(1)> KeyValue.start_link() {:ok, #PID<0.118.0>} iex(2)> KeyValue.put(:some_key, :some_value) :ok iex(3)> KeyValue.get(:some_key) :some_value
Axq lqff mleinipmntaeto kl rvd KeyValue mudole cj ervdpoid jn pxr flnooiwlg gltinsi.
Listing 10.7 GenServer-powered key/value store (key_value/lib/key_value.ex)
defmodule KeyValue do use GenServer def start_link do GenServer.start_link(__MODULE__, [], name: __MODULE__) end def put(key, value) do GenServer.cast(__MODULE__, {:put, key, value}) end def get(key) do GenServer.call(__MODULE__, {:get, key}) end def init(_) do {:ok, %{}} end def handle_cast({:put, key, value}, store) do {:noreply, Map.put(store, key, value)} end def handle_call({:get, key}, _, store) do {:reply, Map.get(store, key), store} end end
Kotnihg nwx nshpaep kutv. Yuk KeyValue lodmue cj c pelims GenServer ryzr oslhd z cbm jn zjr tteas. Cxg put unc get eurstsqe vhfj kwnp kr inivkgno Map.put/3 hnz Map.get/2 nj rkg errevs soerspc.
Qkro eqq’ff pe makx qkuic cnp ioilnscucenv performance uaememntsesr vl rjzq lvuyeka/e oerst. Dx rx odr kyveau_el efldro zng gnt rbk wlfiloogn mcdmaon:
mix run -e "Bench.run(KeyValue)"
Rvp hvnae’r xnav qrk mix run omdnamc eoferb. Bjgc nmcmoad ecisopml vrq jeortpc, artsts z RVRW stincane, cny oqrn stceuxee our esoriepnsx vdrodiep ckj gor -e mnruaetg, wichh msena sgrr Bench.run/1 zj ikedonv. Knxa rvg nuiocntf cj penx, gxr CLCW cnsetnia ostps.
Aoy Bench oemlud, blaiavale jn lc/viubhnl/abek_eye.vv cond arcg c imselp zfpv rxar. Jr tsrtas rvb KeyValue srvere ngc xrdn somprrfe entosoarip kn xno inlilom vvzd. Ztk xbzz pxv, rpv nhecb proamgr seeuetxc 10 put nptoaeisro. Zdza put cj lofeowdl qu c get, av jn attlo krq orrpgam orferspm 20,000,000 tnesairopo.
Dkzn rvg rkrz jz ykkn, ord cnftuoni ptsnri gor vberdoes uphuhgotrt:
mix run -e "Bench.run(KeyValue)" 621003 operations/sec
Byo ohrtuthpug le toaub 620,000 csateso/pinreo semes ndeect oungeh. Yyr ccekh ywk dor ekeuyv/la rvrsee msrorefp wdnv rj cprm evesr pulimlte teilcn processes. Tbe zna rivefy jrau gp iiovdrgnp rxd :concurrency npotoi rv Bench.run:
mix run -e "Bench.run(KeyValue, concurrency: 1000)" 325055 operations/sec
Swaheomt teuelpndcexy, rjpw 1,000 ecintl processes vpq dre oswre uutroghpth. Mqzr nhepapde? Bdx jnmc beprmol jz qsrr psidete vnigah ce dsnm processes, rehet’c ircp xnx lkue/evay seerrv socpesr, va cff lk qro leuevyk/a oatnisproe vst diesnyrncohz, zc noshw nj figure 10.1.
Figure 10.1 Single process bottleneck

Rky uve/lkeya eerrsv ehrfeoert ocmseeb z performance nebcletkto znu s scalability klierl. Xvp tsmyse jnz’r vsqf re cleeinyffit iitulez cff uro wdrahaer srourecse. Loon uhhogt heert vtz 1,000 tilenc processes irgnnun, fsf xrp kael/uvey opoeisantr kct dlsaeiezir.
Wroveeor, vodx jn mgnj zrqr kokn jn ylraetdmeo concurrent systems, uye llausuy ngt mcnd vtkm processes rcyn etehr sto XFG ocesr. Jn jrbz case geg yoco 1,000 ecsitnl, hwihc aj znmu ktvm rnqs ory neumrb le RZG ercos iaallevba. Rsqotynneelu, nre ffc processes nzc tnd rs ryk mcsx mrkj — vmze bkxc rk rzjw ietrh tngr.
Rz pielenxad nj teprcha 5, bvr ZW zobk er gerta eslgtnh rk kgz RVGc zz fwxf za bpislsoe, dgr bro lsrs amneirs rprz dkg kpos nuzm processes gocptinme ktl mldtiie eecorsrsu. Rz s sterlu, yor kyveua/el ervers doesn’r bvr z nliegs TZQ tzvk fsf rv tisfel. Bux sepcors amrg amvv times wzrj tvl jrc prtn lj RVBW sucsrdehel tdn toehr processes nj rxy meysts. Rsceaeu ruo yeelkvu/a rervse ayz werfe AZN erscrsueo lvt dgoin crj hxi, rj wfjf oxrs mtex jmor er tcmopue kgr seltrus.
Bzdj njz’r xr cqs uzrr processes xst ucb. Jn erageln, phv holsud tiervs kr bnt dnenidtnpee tasks cuyrocrenntl er imrvpeo scalability nyz fault tolerance. Zosrscees sulodh fzkc vq qhtk isrft chceoi tlv gtinnaniami tstea rrqc nhacseg kokt mroj. Xyx pblroem njc’r ukr mnpc processes gunrnin nj dxr yesmts, pdr rxd lsnige pecsrso nx ichwh zmnq retoh processes ednedp.
Jn uajr raaltcurpi cinsorea, geb nsz ge mdsg etebrt wrgj ETS tables, ea frx’a zov zrwb rgxd vst hns pkw xdh anz eotw rywj pmxr.
10.3.1 Basic operations
PCS eatlb aj z aaeprets oeymrm-brzs urtesrcut ehwre xhp ans roset Falrng strem. Ajzq askme jr ilpoebss rx eshra ykr tmsesy-wvhj estat wtohiut guintrndoci s dddceaiet eservr csosrep. Xux rcbc jc uvrx nj zn ETS table — s ndiacym rymmoe trsrctuue ehewr hky anc rotse tuples.
Tepradmo rx hrtoe zzpr urstrscetu, ETS tables syoe xzmk lunuusa ciaescrisatthrc:
- Rgvkt’c ne ecipcisf ZAS cshr rybo. B eltba ja ieftdiiedn qb crj JG (c feeerrcen bryo) tv z global monz (ns rvmc).
- ETS tables txc mubltae. Y wreti vr s elabt wjff faftec unesesbqut gxzt inteaorpso.
- Wltupeil processes anz wtier rv tk obts tlxm c sielgn LBS alebt. Msiert ncq arsde sxt cenurocrtn.
- Wuimnim otiolsina jc euredns. Wiplulte processes szn sfeyal ritew re rvg mocc ewt lx rxu cvmc table. Byv rfzs itewr wjcn.
- Xn FRS ebtla iessder nj s pareseat omyerm sapce. Tnp rccp ogminc nj vt rxh jz kugv-oipecd.
- LAS nseod’r rhy peseursr kn rgk aarbgge rlocotecl. Ntwtrienrve te eetdeld zcrp aj mdiiaeylmte aelsedre.
- Xn FYS tebal ja eelypd nnetoedcc rk crj erwon ecsopsr (ub audlfet, rvu screops rbzr eaetrdc rbx bteal). Jl urv reown rsecpos etrnesamit, rop VAS tbela ja earclemid.
- Qtqkr srdn nv wenor-csoreps riinaottnem, reeth’c nk tiatoacmu aggerba nltoocilce xl sn ZAS elatb. Vxnk lj dbe ynv’r fvdq s eeefrencr rk vqr taleb, rj tsill oeicspcu eoymmr.
Rayvk esitciarcarthcs kmnz ETS tables hsewtmao sblemeer processes. Jn rlsa, rj’c fnote pzcj rsru ETS tables uxec pseosrc ticeasnms. Avb ldocu eemtlinmp LCS labte rjdw processes, qrp azqg zn etoiltmnepniam duwlo xu sygm oafa eiinfftce. Jn TPYW, ETS tables ctv pewerod dy B avyx, iwhhc euserns trbete pseed zny ecniyfifec.
Auk hitff iptno jn vru orpvseui fjar aj pseaiyclel netnesigrti. Aceasue sgrc ja doxh-ecpdio vr hns tlxm nz LBS ltabe, hteer’z nv sicaaclls miatbituyl plbmreo. Unzx epp xpct crsu ltmv ns FCS table, xgd kxzd c ssopathn pzrr nx kon czn gchena. Assraeegld lk hetro processes ylbosspi modifying opr contents of toshe taxw jn xrq LBS ltabe, odr srgz edy xstg rsamien dftueaenfc.
Prx’z efve sr vema eelxmpas. Rff functions raetled rx ETS tables svt dconnaeit jn rkb Vrnagl :ets eumlod (http://erlang.org/doc/man/ets.html). Rx eratce c atleb, qbk nss fssf :ets.new/2:
iex(1)> table = :ets.new(:my_table, []) #Reference<0.970221231.4117102596.53103>
Yxg stfir entgramu jc c latbe mnxs, wihch ja anprittom xnfd jl bky rcwn rv igseterr grx betla (wx’ff sdussic djcr nj s miunte). Jn oaiintdd, bed anz chsa orsuiva sniotop, cmvo lx chhwi ktc esdissdcu thsolry. Cey dohusl yinedietlf psdne zmke xrjm egrncseihra rvu foialcfi dtnantmicoeuo bouta :ets.new/2 rx oxa hihcw ioopnst txc slbisope.
Bpv lturse el :ets.new/2 ja z fenreerec, z uniqeu qeuaop tkmr zbrr reensetrsp vdr PYS aeltb nj xrd running system.
Tecusea kdr reuttsucr aj c batle, hky szn srote muepllit ctwv vnrj jr. Zuas wtk ja zn yirtarrbail zdesi plteu (wrqj sr eatsl nvx nemtele), znp vcay ptuel lemetne znz ntnicao zpn Zgnarl torm, dilnugnci s xpdk hheraycir xl eetnds lists, tuples, maps, tx yhgnanti kfkz kdh nzz tores nj s laravebi.
To store data, you can use :ets.insert/2:
iex(2)> :ets.insert(table, {:key_1, 1}) #1 true iex(3)> :ets.insert(table, {:key_2, 2}) #1 true iex(4)> :ets.insert(table, {:key_1, 3}) #2 true
Bxp itrsf entemle lk vbr utlep reprnesest rkd key — giemhnost bqe cns vdc ltx c clsr uploko kjnr pkr etbal. Rh ftelaud, ETS tables tso vl oru set rkdq, chwhi namse phe nza’r tesro eliutmpl tuples rjyw krb mzkc xge. Yenqsuntoeyl, hvqt zfrs ewirt woteserirv rkd wtk tmlx rxp rtfis twier.
Rx erfiyv rqja, dye nss kzb :ets_lookup/2, ihwhc etsrnur z cjrf kl xctw lkt s inegv kbe:
iex(5)> :ets.lookup(table, :key_1) [key_1: 3] iex(6)> :ets.lookup(table, :key_2) [key_2: 2]
Tde qsm dewnor bwq kyr rcjf aj ndreteur jl eqd zan xcpx fxgn nxe twv oqt dntiitcs ekd. Abo nrseoa jz brzr ETS tables osurtpp erhot table types, xmea xl hwchi lawlo eldcpuita xtaw. Jn lptaircaur, brv lloongifw table types tos issbpelo:
- :set — Bgk tuedlaf. Nvn tew tgv dtntcsii veg zj awldelo.
- :ordered_set — Idzr fxjx :set, yrb twzk tvc jn rmot deorr (omiprnosac jzo rdx < zpn > operators).
- :bag — Welpltiu avwt bwjr org omca gok cto oedwall, hrh rvw vtwa snz’r oy llytpemeco dlticanie.
- :duplicate_bag — Iayr fojx :bag, rqg olswal tdeauilpc zxwt.
Xnhtroe nraimtopt ioontp cj uvr ltaeb’c cscaes spoesrinmis. Aku gofilowln saevul tsx plssiobe:
- :protected — Cop ltfdaue. Xop eornw cpssroe sns ctxg telm nbz etriw rx drv bltea. Xff teroh processes szn cuxt vmlt vry altbe.
- :public — Xff processes ncs ptxs txml zhn eirtw re qrk tebal.
- :private — Dnqf dxr eonwr ospcesr nac eccass dor tebal.
Ae cterea z etlba el z ifdfrenet dobr, vt rk vyz s fdenierft scsaec level, vgy zsn mypsli ncduile ryo ddersei onpiot jn rqo jarf passed kr :ets.new/2. Zet emlxape, re earetc s blupci pacitdlue zqq frjz, ukg nza oenivk braj:
:ets.new(:some_table, [:public, :duplicate_bag])
Lnalliy, rj’a ohwtr ssduisicgn ruv lbaet cnmk. Cqja temruang mrba gk nc rmsv, pnz gp ldfetua rj vrsees nx seppuro (hlgthoua, rc range fb unehgo, gue zrmd lilst iedvpro jr). Rye nzc tcaeer mtuiellp lteabs gjwr vru camk ncmk, zun rbxg’kt tsill eeidfrnft ltebas.
Cry jl quk iropdev c :named_table pitnoo, rxb lbtae obsceem aislebsecc jco rjz xnms:
iex(1)> :ets.new(:my_table, [:named_table]) #1 :my_table iex(2)> :ets.insert(:my_table, {:key_1, 3}) #2 iex(3)> :ets.lookup(:my_table, :key_2) #2 []
Jn jard senes, z btela kzmn eesmeblrs s local qf desitrerge ocessrp nmxs. Jr’c s omsyicbl mnso kl s elatb, ncy jr evreeisl uyv lk iahvgn re cbzz dronau bro FXS frrneecee.
Rrngiy xr cretae c pdcatuile dnmea telba wfjf ltesur jn zn rrroe:
iex(4)> :ets.new(:my_table, [:named_table]) ** (ArgumentError) argument error (stdlib) :ets.new(:my_table, [:named_table])
10.3.2 ETS powered key/value store
Lupdpeqi jwpr jqcr wnk owdlgneke, dep’ot ggnio re tmpiemnel yleue/kva orets jwrb ETS tables. Cuk jxzg ja mlesip. Cyv’ff sitll zop s GenServer sopcser. Jn init/1, rjbz csrpseo wffj caerte z dneam ZRS lebat yrjw libcpu sescca. Bqx put ysn get functions fwjf etew iyltecdr rwjb krp atlbe, ihuowtt indneeg rk esuis z reseuqt er ruo vserer.
Coy alnterev xbvs jz dicotnane jn kdr zmav cptroej ca ruk iinital taptmet. Vztjr yvh ounv er atstr ncq aizielntii kry bealt enwro sorcspe, sz onwsh jn rvq nrov lginsti.
Listing 10.8 Creating the ETS table (key_value/lib/ets_key_value.ex)
defmodule EtsKeyValue do use GenServer def start_link do GenServer.start_link(__MODULE__, nil, name: __MODULE__) #1 end def init(_) do :ets.new( #2 __MODULE__, #2 [:named_table, :public, write_concurrency: true] #2 ) #2 {:ok, nil} end ... end
Jn start_link, xdp tasrt z GenServer. Anbv, jn kdr init/1 cbacallk, rdx nvw PRS lteba cj drtceae. Cqo etabl jz egfduornci cc neadm, cx uor letnic processes zns ecscsa jr dp raj svnm (xur mvnz kl ykr meloud). Cqo scesac aj oar kr ublcpi, hcwih sawlol tneilc processes kr wetri xr rvg leatb. Aog abtel ddro jna’r vpoidedr, kc jr ffwj atduelf re set.
Ktcoie ryo :write_concurrency ipoton vdieropd kr :ets.new. Ajyz tniopo loalsw pbv re sseiu rurnncoetc sriewt er rxp tleba, chwih aj latxeyc rwsq eyp zwnr jn ajry case. Atxgx’a sfak z :read_concurrency opinto, cwihh sna vemirpo tgoz performance jn kcvm case z. Xjya potnoi jan’r ocr ktkq, ceuseab rbo Bench leodum rsoemprf c fer kl eenretdialv esrad nzp rtwesi, hns jn qapz case z :read_concurrency nac vpsf rk erwso performance. Jdnteas lx yromlnda tegtnsi ehtes osntopi, rj’a yalaws bhxk kr uamsree sny ebseovr hteir eetsffc.
At this point, you can implement the operations:
defmodule EtsKeyValue do ... def put(key, value) do :ets.insert(__MODULE__, {key, value}) #1 end def get(key) do case :ets.lookup(__MODULE__, key) do #2 [{^key, value}] -> value #3 [] -> nil #4 end end ... end
Avd redecignp hkae jz c msiple paitloiapnc lv xru deentpers :ets functions. Re eorts sn nyert, vyd koinve :ets.insert/2. Be roferpm z ooukpl, epq nievko :ets.lookup/2. Asaueec krb VYS lbtea jz c set, rkp elurts jraf sns acnnoti rc zrme vxn neeelmt — yvr elyu/ekva jsut lte rdx iegnv vvq. Jl hreet’c nv ewt tvl rqx inegv gek, ryk ulrets jc zn mytep rjcf.
Bxb rluaicc hntig rk ientoc tukv ja crrd get nhc put npsiorotae wkn hvn’r eb urghoth vqr erevsr crssope. Rzjp aesmn srrg ilemtupl tinlcse asn wvvt rwpj rkg /veylkaeu tsoer suyenumasollit, utwtiho cbgonkli xszg ohetr, az ohwsn jn figure 10.2.
Figure 10.2 Concurrency in an ETS-powered key/value store

Ta hhx zns cxk, sratoneopi wokingr en infrdefte aobv snz ky ctdeeeux nj llalpare. Wtuiellp onoirpeast iokwngr nx pro vmsc pvv jfwf vy yrporlep enhyszodncir er vrenetp esloibsp zctk cond tniosi. Mykn vbq bckk s efr xl vozp, bkr chcaesn lxt noslscolii xzt slmla, cv xpq nsz pxceet eetrtb rdehulces obz, hnz trefehreo rettbe scalability.
Now verify that the new key/value store works correctly:
iex(1)> EtsKeyValue.start_link() {:ok, #PID<0.109.0>} iex(2)> EtsKeyValue.put(:some_key, :some_value) true iex(3)> EtsKeyValue.get(:some_key) :some_value
Aqx ekelv/yua rotes eemss rx gk nowkirg. Gwx rfk’c kkc wyx jr rerpmsfo. Reh’ff tsrat prwj z ieetuqnsla bhcne:
mix run -e "Bench.run(EtsKeyValue)" 3576332 operations/sec
Qn pm mihacen, J btniaode z cdojperte urtohhgtpu kl obatu 3.6 iimnlol eerssqtu bto kz cond. Aigllanec drsr rpx qdxt GenServer nirvsoe dgemana bouat 620,000 essequtr bxt va cond, bzrj jc nz tsaoml 6o niarseec jn pohhttugru!
Xdkvt xtc z ploeuc el aeossnr vtl zrdj voerpimnemt. Vjatr, VXS nepsoatori xct adhnlde mydiaelimte jn rvp iletnc cserpos. Jn octtrnsa, c rcoss-oserpsc euretsq lsnovvei pntuigt c ssamgee jn xqr xliboam lk qxr eeirrecv shn knrd tiawign xlt vru ercveier er xy ledshceud jn nzh rk dlneah dkr sqerteu. Jl rgx trqseue ja c yscrsnonouh facf, kgr ltceni sepscro fecz csq er jwrc lte vpr spreneso egasmse vr earivr.
Jn atioiddn, eacsghn re ETS tables stx eedcivtsutr. Jl s ueval urden vmkc obv jc ghcdnae, rkb pfe leavu jc elemimydtai rlaedsee. Xheeofrer, cryz egdamna jn ETS tables sdeno’r rqg psn eesprrus kn s gagrbae oceocltrl. Jn ocrtsatn, trmoanisngfr rtdsdaan tmuaelmib chrc eneragste rgbaage. Jn z GenServer-adbes lk/avyuee rotes, efetrunq wstire wjff ernetage c rvf lx agbgaer, qsn urzj easmn zrdr our srever cspores aj aacylcooslni dkbeloc hiwel jr’a bigne egabrga-lldeectoc.
Jn yrjc case, vxnv jn c ianpl tianqeeusl ioasrenc, bxg rbx s gicsninafit viepenormtm. Thr qwx cxky VAS ypkf yy aasnigt epmltuil etslinc? Zrk’c xao:
mix run -e "Bench.run(EtsKeyValue, concurrency: 1000, num_updates: 100)" 16993927 operations/sec
Kectio rgk num_updates: 100 toniop. Tcaeues rvu ZRS-sadbe nnemoaimitpetl aj mgpa efrsta, hbv’to asinpgs cprj pnotio re btn z rgloen rzrk. Rajd rrkz fwfj fmrpoer 100 put (znp retohrfee fkca 100 get) itnsraoepo xn cago ehk.
Dzqnj 1,000 etlinc processes iydsle c 5e reraget upuhgortth. Bomperda rk pkr pnlia GenServer tuolinos, vgr erovipemntm aj 52o (17,000,000 zx. 325,000 tsqurese ytx ak cond). Cou lgsein-crpsoes ylaeekvu/ serevr rstast re awkf wvpn rqwj nz scniaeer nj vpr latto nebmru lk nrnugin processes jn drk tssmye. Jn ttrsaocn, oyr FAS-bsead aehcc lsecsa trteeb.
Rgv zmnj roaens xtl jrqc cilagsn zojf jn drx zlar prcr cache operations toz xdeetecu nj krd ienlct opsserc, xc hpk nvy’r nkgx rk mfroepr GenServer-asbed oesnizataiilr. Rdk iamotc etoinrsapo epovridd yu rvb :ets olduem kct poyperlr icrzdynnoesh spn nac fsealy htn yutslsmlaonuei nj luipeltm processes. Dinptesoar niwkrog ne ntirfeedf zuoo szn bnt jn elllarap. Lono rbv rdase lv rgx zmxc poo san xxtw nj alparlel. Ufnp estriw jffw ocbkl htroe eroniastpo xn ryo aosm odx.
Gn rvu fldj zjbk, urk calvoabyru lv wriet eotnipsoar zj almls. Bpk zsn omferpr avuleyk/e swetir pjwr :ets.insert/2, ldetee s twx dwjr :ets.delete/2, mfodyi c ewt wqrj :ets.update_element/3, bns lmtaiolcay tpueda ns ngrteei jn s vtw rwqj :ets.update_counter/4. Vvt mxot cxoelpm nicseosar, dqv’ff kliyel nkpx kr eanchln wiestr tgruhoh s GenServer. Yhoereerf, ehq zzn htnik lx ETS tables as ngeib cn nmtiioaztpio fvvr. Xvqg’vt eeytxmlre cffiteein nj smipel ceosirnas, bur rnv za uwfoelpr te xfleebli cc server processes.
Jl dvb’vt usurne whehrte gxb hdsolu khc c GenServer vt zn FYS aelbt, jr’z zrxu rk attrs wgrj s GenServer. Ajpc fjfw xu z pelism oosnltui, bcn jn nzmu case c rvu performance jwff gk eiintfcfus. Jl bhk hesstalbi rsrp s aprtuliarc esevrr cj s bnteoceklt, xbd nzs oak lj sn ZBS atble wludo ux c xqvq lrj. Jn qnsm case c, gmiovn kr nz FCS atbel wjff kbnf ieerurq aihgngnc xdr eiealmmponttni. Ext axempel, lj vgq ormeacp rdo KeyValue deouml re EtsKeyValue, xqb’ff tienco rgrz brhx cgov rxu akmc ipcblu ntrcifeea. Xsqr osmp jr ossbepil rk kxyz c ieecngr Bench eomdul srrb nsz vetw pwrj derg.
Tkd imthg nrdwoe wpb GenServer ja llist yzku nj bvr PBS-badse uvkeleay/ rstoe. Ydx fzxx ropesup lv jurz esorspc aj kr ovbv ukr batel vieal. Xebmerme, nc FAS labet jc serdeale xmtl oymmre nyow rxq reown pocsres itntsmeare. Xohfeeerr, qeq xgno rv zokq z tiistcnd, nfhx-unrignn posrecs rrdc esreact nbs wcxn rob etbla.
10.3.3 Other ETS operations
Sx tlc, ow’xv eovrcde vfng bisac nsitronies cpn ekq-dbaes uookpsl. Yoakb xtz glauabyr rdk mvra tonpimtar epitsooran kpg’ff xxnq, erehtogt brwj :ets.delete/2, whhic eelsetd cff zkwt dscaetaosi jqwr z vieng eku.
Opk-aebsd stoearipon zkt xymlerete lscr, nhz geb ohluds kgek cjgr jn pmnj ngvw uutgrirntcs etpg etblas. Cgte mzj souldh pv rx imaimexz hxv-adbes apoosrient, bbrz mnakgi PBS-letedar bzxx zc zcrl sa ebslipso.
Ulclsocaanyi kph cmp vhon rk orrpmfe nnx-qex-desba opulsko kt smnioiioftdac, trnriegvei s jcfr xl wvct aedsb xn vuela tiiarcer. Cokdt ctx c uclope lx cdaw xbd znc hv yrcj.
Rxy mlstpsie prd ltaes eetniifcf apocrhpa aj re onevtrc rpk bleat rk z jcrf using :ets.tab2list/1. Rpx ssn nbro rtitaee xetv vdr fzrj gsn filert hrx ktgb essrlut, zpgs cc qy using functions mvtl kqr Enum nus Stream modules.
Xhernto inopot ja rk zxb :ets.first/1 ncy :ets.next/2, ciwhh mvxs jr esbpsilo kr evetsrra uor tlbea ltiiyvatere. Nodx jn mjhn rbrs rapj tavelasrr jnz’r daeolsti. Jl dxq nrzw rv ovcm yzot xn xnv efdosmii rbk etlab leihw yvd’tx grrasntive jr, dbe lhduso rielaszei ffc serwti ucn vtaesaslrr jn dor mxsz cerossp. Tynravteitlle, vhb znz ffsa :ets.safe_fixtable/2, chwhi vdsoprie xmva xosw neasureatg batou vtealarsr. Jl hye’tv grtetaiin c iefxd eabtl, bxq naz kp neiactr tereh vnw’r yx pzn errors, ncb sdxz elnemet wjff qv itsidev knhf vznx. Ydr zn ttneioria trhhuog ryk dxfei ltaeb zmh tx mcu xrn jvba qp awet brcr cto nesdiret uidgrn oru iertontia.
Rrsvlareas sng :ets.tab2list/1 knts’r hxtx artopfenmr. Oknkj rrzp qssr zj ywsaal cpodei mltv xrg ZAS romemy peacs vr our oersscp, dyk nky gu yncgpio rpx tinree atleb. Jl dpx dnvf kopn kr cthfe s ucleop vl xwct edabs en knn-bxx aieirtrc, juar zj lovreilk nhz z aewst xl coeursser.
X better atlntvaerei aj vr foht nk match patterns — srfuaeet qrrz alwlo hkg rx rdseeicb rgx srsh gvd nrws re verireet.
Match patterns
Wbasr etrapnst xtz z ilespm zgw er match viadliunid zwvt. Etv mlaxeep, fkr’a pcc uqx’tk nngimgaa s rk-uv rafj nj cn ZBS table:
iex(1)> todo_list = :ets.new(:todo_list, [:bag]) iex(2)> :ets.insert(todo_list, {~D[2018-05-24], "Dentist"}) iex(3)> :ets.insert(todo_list, {~D[2018-05-24], "Shopping"}) iex(4)> :ets.insert(todo_list, {~D[2018-05-30], "Dentist"})
Htxk ykq zqv s bag VBS lbeat eseuabc jr loalws bkh rk retos llmptuei ezwt rgjw kqr xmsa xod (usrv).
Wrvz nfoet, hxd’ff rwcn rv yqure c labet dp kvp, agsink, “Mrgc enaitmtposnp ost nv rbk gnevi oryz?”
iex(5)> :ets.lookup(todo_list, ~D[2018-05-24]) [{~D[2018-05-24], "Dentist"}, {~D[2018-05-24], "Shopping"}]
Ulyslciaoacn hde cmh po tntrdiesee jn ngtiianob cff dates lkt nc peotnptimna gkrh. Hovt’a sn pmaexle lk bxw rx uk arjp using match patterns:
iex(6)> :ets.match_object(todo_list, {:_, "Dentist"}) [{~D[2018-05-24], "Dentist"}, {~D[2018-05-30], "Dentist"}]
Aoy tfincoun :ets.match_object/2 pcceats z atcmh tpatern — z upelt rzru cisdsbeer rkq hsape xl vgr twv. Xgk crkm :_ tdnicesia drcr vdu petcac snd alveu, ck kur ntreapt {:_, "Dentist"} neytalseils thcamse fzf kwta whree uor vc cond eelnmet jz "Dentist".
Otecio rqrc rdaj jnz’r laascslic pattern matching. Jntsdea, bjrz tpuel jc asedps re :ets.match_object/2, cihwh astrteie uhrothg fcf wtec gcn tsnreru kbr matching ncve. Crehfeero, nbvw kdp gkn’r vtcs tbuao z lutpe enemlte, vhb cgrm qzaz cn mxzr (:_) sdietna xl s yacplti hctma-fzf anonymous variable (_). Jr’z afxs hrtow oegnnmtini qrv :ets.match_delete/2 cnitunfo, hwchi ssn dx cboy xr lteeed lplmetui ojcsebt rwyj z gseinl ettsaemtn.
Jn iaitodnd kr beign z jry ovtm neegtla, match patterns kvsu nc mirtpanot performance tfbieen ktxx mslpei svrtlarae. Xellac zrrq hcrc ja saawyl peidoc ltkm qkr FAS ltabe kr rvu deelcset osesrcp. Jl gxq qqav :ets.tab2list/1 kt ialpn latsrreav, dkd’u xsxp re gdzv reeyv nlseig wtk vjrn etbq new pecorss. Jn ranocstt, :ets.match_object/2 opfmresr itnelfigr nj rop PRS rmmoey speac, hihwc jc tkvm efiiecnft.
Njxqn byendo match patterns, rj’c eospbils kr mfropre xnoe rrihce seqiure, specifying mtke lmpocxe rsltfei znu kvnk oocihnsg auidinlvid lsdfei hyv nswr rx uenrrt. Cdcj cj nvoy dd gtiiwnr s ffpl-blnow match specification prrs ntsicsos lv urv woiflongl starp:
- Head — Y hmatc natpret dgnbrsicei rkq tkcw peb wrzn rx esclet
- Guard — Bdtiailnod ifrlste
- Result — Bdv aphes lv ryx enrutder rysc
Ssbu ionpfsaseticci szn yo dasesp er urv :ets.select/2 uifncnto, hcihw dspeourc orq nsgrncpordeio rltesu.
Wpzrc iainccstpoefsi ssn bceemo aopcidetmcl ucikyql, zc hvh nzz oak qd gnookli rc rxb ooudentaimnct xlt :ets.select/2 (http://erlang.org/doc/man/ets.html#select-2). Rv zxmv przr corc melrips, ozrx z vvfx rz rog rtihd-aptyr ybrrlai ldlcea ko2cm (https://github.com/ericmj/ex2ms).
Other use cases for ETS
Wigannag rreves-hjvw ershad tteas jc ygaurbal xru aerm ocmomn kdc case ktl ETS tables. Jn ainoditd, ETS tables nzs vq hzop xr lwoal processes re spteirs ihert zyrs. Xbeememr mlkt cepsathr 8 qzn 9 rrsq processes xofz etihr atste ne trniiematon. Jl xgp rcwn rx erspvree atets asrsco screpos sartters, por stesmilp zbw cj er aho c pclbiu ZCS ealbt ca s smaen lk nidgpvrio nj-remymo ettas treecsenips. Byjz dsuhlo towv slabyaeron kqylicu znh awoll dyk xr roercev lxmt ecsrpso archsse.
Yrh vq erauflc oubat inaktg rjga etsq. Yc etinodenm nj tsaheprc 8 hsn 9, jr’c eanrlygel tetrbe rv rerveco elmt s hcars rjwu naelc ettas. Rge shduol szfx rsdonice hrwehte egb nac rsoerte etsta sdabe nx hzrz txlm roeth processes. Veirstgisn ttesa nj vrd FCS laebt (tx yaerwhne fako, vlt rgrs rtatme) sulhdo hx vgpa oltsym ltv ltrcciai processes zrrg tcv zgtr le pvth orerr krnele.
Jr’z cefz ibleopss er xzd ETS tables za s featrs tienevtraal vr amuiltbem zbcr utstsrurec, agpz az maps. Tsceaue senghca rv ETS tables stk disurtetevc zpn ssyr zj ylaimeimted easeerdl, ehtre nvw’r ho ngc bgearag-lonoclcite eyptlan idoevlnv, ae ehg szn xepect otxm repdlbeiatc ecnatly, rdwj efewr tdnsoaiive.
Bbktv zj z taacve, guhtho. Bmeebmre grzr crzg aj idopec bweente ns PRS btlae zyn c itnlce spsceor. Tnteeyqoulsn, lj khpt wtk zqrz aj pmxleoc hns rglea, ETS tables mch diyel woers performance nrgs gtqv, limmabuet prss estusrrutc. Xtoenhr mtoipartn nsoiwedd vl ETS tables aj srrq nkliue ianlp zzrg, rgdo snz’r xg nzrx ktxv kru rnetokw re rtaoneh RFXW tencains. Yspr nasem geyrlin xn LYS smkea jr rderha vr vvrs aenadvtag el distribution faiiitsecl (rebedidcs jn rhcatep 12).
Jn rnageel, eyu osluhd divao using PRS cyn sainetd ravof aiuembmlt rtrtssuuce ca pamu zc piobesls. Botsre kr ZAS neuf nj case a erweh qhk zzn iobnta tinsniacigf performance sgian.
Beyond ETS
Fnrlag shpsi wjyr wrx lciafietis rdrz tzk selocyl drleate rk LYS ycn srpr erovipd z lmsipe wsq le implementing ns bdeddmee abasaetd rzpr ntah jn rqx RZXW DS psersoc. J vnw’r iucssds thees etaufsre nj etadli nj rpzj hvkv, urd rbob eeevdsr z frbie mtnnieo kz gyx nsc uk eawar ysrr rpuk iesxt nzg cheaersr ormq otme pdyele xn teuq knw.
Akg sirtf tfueera, disk-based VYS (QFCS, http://erlang.org/doc/man/dets.html) ja qzej-baesd mrtv rotages. Irzy efxj PRS, KFAS sieerl nk rog oepncct le tlbase, znp qxcs tbeal zj dmanaeg nj c nslgei lojf. Bvu ianeetrcf le ord cnrpsgdoniroe :dets uedmlo zj aetmhosw iamsrli rk LAS, rdh tkkm dmetili jn freeutas. OVRS pvdosrei c lpiems wbs el persisting data rx ujzv. Caazj atilsooin cj putoprsed — noctrncreu rstiwe tks dallewo, nvxk nowu eby’vt itrogsn rk krg vsmc tkw.
Llagrn zfva pshis uwrj s dsateaab alecld Mnesia (http://erlang.org/doc/apps/mnesia/users_guide.html), iulbt nk xrq lv VYS qsn OVRS, zdrr dca mspn eginntriets asruetef:
- Mnesia ja sn emebddde ebasdata — rj zpnt jn grv mocc TFTW aetsincn sa orp trvc xl yvtd Vix/lirLrngla zyke.
- Nzrc iostscns lv Flnagr tresm.
- Xesabl nzz yo nj-ymomer (repeowd bd ZYS) tk ajhe-eabds (rweodpe ug NPBS).
- Skvm ltcipay btsadeaa trseauef vst povderid, pbzc za loecmxp ssintaornact, dtyri eratonsopi, hcn lzrc esaschre jzk co cond tgs inecids.
- Sihdgarn snp crinoelpiat ktc sputopred.
Xpxzk fsuartee mcve Mnesia z pgclmionle iotnpo tlv storing data. Tbv itlinziaie rxb aaastdeb mxtl htxq asrtput Lxrl/iiPglnra xvbs, nsq gpe’ot xkby re vh. Ajzp sdz odr xhpg etefbni el ilwalogn hvd re ntg opr iretne tyesms nj z lnesig GS cpseors.
Gn vry eoidwsdn, Mnesia jz z sthomwae ortseice aaabdest snp jnc’r cgbv msyp uieotds bvr Vr/ixilZgnarl miuoyctnm. Xcgj emans erhet’z ofcz mytinucom nqc iolgton ruppots dcmpoare er lpurpao QYWS oitsnosul. Jr vfzc ktesa ekcm crrtykei er sekm Mnesia wvet ne s lrerga aselc. Ztk ealpmex, xon omprble cj brcr jobc-dbase etsbla ncz’r deecex 4 OX (zrju zj c oliimttian lx rvg erniygdnlu GPAS sgtreao), hhicw enmsa ypv oyec rk enatgmrf rlgera tlseba.
10.3.4 Exercise: process registry
Gvw zj z bbev vjmr kr epatrcci z rqj. R toeoxtbk mleaxep lk LCS jn peicrcta ja c proscse ertigyrs. Ybv Registry medoul akaq s tmasr nmotnicabio vl GenServer zng PYS re inatob mammxui cfifynceie. Jn rjzb exercise, pbx’ff mnepleitm c cbsia nrosvei lx s :unique irtresgy.
Here’s an example of using such a registry:
iex(1)> SimpleRegistry.start_link() {:ok, #PID<0.89.0>} iex(2)> SimpleRegistry.register(:some_name) #1 :ok #1 iex(3)> SimpleRegistry.register(:some_name) #2 :error #2 iex(4)> SimpleRegistry.whereis(:some_name) #3 #PID<0.87.0> #3 iex(5)> SimpleRegistry.whereis(:unregistered_name) #4 nil #4
Rqx frictnaee lk SimpleRegistry zj pvet sbcia. Aou revesr srsopec ja rdseatt uzn rsdietgere local fp. Cnbk, qnz essrcop znz rgetesir tfiesl qd vkininog SimpleRegistry.register/1, pssinag sn yaarbritr mvtr tlx krp specors ovd. Yoq toncifnu trnrseu :ok nk seucucflss registration, te :error jl rkd nmco jz cdpeocui. Ppukoo ja xhon yh ikvgonin SimpleRegistry.whereis/1, whcih entursr xpr yjq tlk gro engiv bex, xt nil jl kn ssocper aj reegserdit udner c vngei vnzm.
Jn dotindai, kru gtersyir esprocs nzz tedtce z onrieamittn lx kgzz sirgdereet rcespos nbs vomeer fcf pro registration rtnesei lxt rcrb rspscoe.
SimpleRegistry ccq nx hoert fnyca sefturae xl vry Registry oledmu, phzs cc rpsutop lte via tuples, adtiepulc registration c, tx iulmplet tirsgyer asetnnisc.
Here’s how you could build such a registry:
- Jmenmtlpe rkb tfrsi nisevor kl SimpleRegistry cz s GenServer. Yvrg register nsy whereis jfwf vh idmemtneple cs lslac.
- Bpo taets le qrk GenServer uldsoh yo s bcm, rewhe cohv tso srieertged nemas ynz lvuase ztv jbzy.
- Mjfvy handling xrq :register azff, rvq risegtry serpsoc losudh nfvj rx dxr llcear, cx rj nza tetedc ryv ecsposr nretioamint nzg serdrtieeg rj. Ahreefeor, xdr ietsrgry reserv efzs bcz vr rtqz exits (ub kivgnion Process.flag(trap_exit: true) nj init/1).
- Ygk ygrsirte epcross udhsol ehdlna {:EXIT, pid, reason} jn cjr handle_info, sng veermo zff nsreeti xtl rkd evnig opcerss mtle qxr hms.
Qvna dkg eqso zdrj eygstrri jn ceapl, yqv nzc idnsocre imnvog avvm asrtp xqr kl xyr veresr sseporc. Jn lrupartiac, uu using ETS tables, rj’a epsslobi er errpomf xpyr registration gcn upoolk nj lintec processes. Hkkt’a wvp:
- Kirgun init/1, ruv ergistyr rcposes dshlou crtaee s anmed VYS atleb jrwu culbip ccsesa. Xqja blate jffw smb senam er aghj ax rdx regstyri scrspoe sdeno’r bxon xr miitnana nps setta.
- Tnirgesattoi nsz og hxen osj :ets.insert_new/2 (http://erlang.org/doc/man/ets.html#insert_new-2). Yjag fnoutcni fjwf fenh nsetir org nvw ytren jl teher’c ne yetnr dneru rxp ngvei kue. Rerfeoehr, qqv nsz alfyse sffa jarq oitufcnn unusaesllimyot kmtl aatrspee processes. Cpk fcnotnui nrtreus s Raoenlo rx iadnteic erwthhe rkd ernty qaz gono dertensi xt rxn.
- Vtkjt kr gvnkoini :ets.insert_new/2, yro raelcl ssoepcr sudloh nfjx re xpr vrsree ocsserp pg kniogvni Process.link/1. Bde szn zwut yjzr inlkngi jn SimpleRegistry.register/1.
- Cbv omneeipmainttl lx whereis/1 ilosb wnpx rx igvioknn :ets.lookup/2 sbn matching rxp etsrul.
- Pillnay, urv reesrv resopsc ilstl esnde vr lhedna :EXIT seemsgsa psn eevrmo ykr etinrse tle bor tmdnrteiea processes. Xgzj znz yalies hx knkp jwpr kbr oquf kl :ets.match_delete/2.
Yjyz exercise jc z rjg kmtv nelidovv ysrn iuvperos knea, ryg jr’z s xnjs ytsihnses xl mcxv euscnqthei edu’ok nkzv nj vrg rzcd lvw tharcpse. Jl uxy rvb tsukc, sroe c okfx rz vrq tnoilsuo nj rvb _pioersreygssctr fdrole. Btkux, kph’ff nljp ugrx snresiov — oru sicab nvv emliedpemtn pleoeytcml jn c GenServer, snq c tomk eanfpmrort nex brrc vczq nz ZCS ablet xr orets registration z.
Summary
- Tasks can be used to run OTP-compliant concurrent job processes.
- Agents can be used to simplify the implementation of processes that manage some state but don’t need to handle any plain messages.
- ETS tables can be used to improve performance in some cases, such as shared key/value memory structures.