mariustwittercom Abstract Building server software in a largescale setting where systems ex hibit a high degree of concurrency and environmental variability is a challenging task to even the most experienced programmer Ef 64257ciency safety and robu ID: 29416
Download Pdf The PPT/PDF document "Your Server as a Function Marius Eriksen..." is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Alloperationsreturningfuturesareexpectedtobeasynchronous,thoughthisisnotenforced.DependentcompositionItiscommontosequencetwoasyn-chronousoperationsduetoadatadependency.Forexample,asearchenginefrontend,inordertoprovidepersonalizedsearchre-sults,mightconsultauserservicetorewritequeries.Given:defrewrite(user:String,query:String):Future[String]defsearch(query:String):Future[Set[Result]]then,toperformoursearch,werstneedtoinvokerewritetoretrievethepersonalizedquery,whichisthenusedasaparametertosearch.Wecanphrasethiscombinedoperationasafuturetransfor-mation:theoperationevaluatestothefuturethatrepresentstheresultofrewrite,appliedtosearch.ThesetransformationsarelooselyanalogoustoUnixpipes[18].Withanimaginedsyntax,wemightwritedefpsearch(user,query)=rewrite(user,query)|search( ); beingtheplaceholderargumentfortheresultfromthepipe.TheflatMapcombinatorperformsthiskindoftransformation:traitFuture[T]{defflatMap[U](f:T=Future[U]):Future[U]...}(ThetypeT=Future[U]denotesaunaryfunctionwhosear-gumentisoftypeTandwhoseresultisoftypeFuture[U].)ThusflatMap'sargumentisafunctionwhich,whenthefuturesucceeds,isinvokedtoproducethedependentfuture.flatMap,aswithallfuturecombinators,isasynchronous:itreturnsimmediatelywithafuturerepresentingtheresultofthecompositeoperation.PersonalizedsearchisastraightforwardapplicationofflatMap:defpsearch(user:String,query:String)=rewrite(user,query)flatMap{pquery=search(pquery)}(Scalaprovidesinxnotationforunarymethods;parenthesesaroundthemethodargumentareomitted,soisthedotformethoddereference.Functionliteralsareintroducedwithfparam=exprg.)psearchreturnsaFuture[Set[Result]],thusvalresult:Future[Set[Result]]=psearch("marius","finagle")issuesthepersonalizedsearchfornagle;resultisthefuturerepresentingtheresultofthecomposedoperation.Inthisexample,flatMapisusedtoresolveadatadependencybetweensearchandrewrite;psearchmerelyexpressesthis,withoutspecifyinganexecutionstrategy.HandlingerrorsflatMapshort-circuitscomputationwhentheouterfuturefails:thereturnedfutureisfailedwithoutinvokingthegivenfunctiontoproduceadependentfuture.Indeed,itstypebearswitnesstoitssemantics:whenaFuturefails,itdoesnothaveavaluetopresenttothefunctionproducingthedependentfuture.Theseerrorsemanticsareanalogoustotraditional,stack-basedexceptionsemantics.Itisoftenusefultorecoverfromsucherrors,forexampleinordertoretryanoperation,ortoprovideafallbackvalue.Therescuecombinatorprovidesthis;whereasflatMapoperatesoversuccessfulresults,rescueoperatesoverfailures.traitFuture[T]{...defrescue[B](f:PartialFunction[Throwable,Future[B]]):Future[B]}WhileflatMapdemandsatotalfunction,rescueacceptspartialfunctions,allowingtheprogrammertohandleonlyasubsetofpos-sibleerrors.Forexample,wecanmodifypsearchfromabovetoskippersonalizationifthequeryrewritingsystemfailstocompletewithinadeadline,sothatadegradedresultisreturnedinsteadofafailure:defpsearch(user:String,query:String)=rewrite(user,query).within(50.milliseconds)rescue{case_:TimeoutError=Future.value(query)}flatMap{pquery=search(pquery)}(fcase...isapartialfunctionliteralinScala;itmaycon-tainmultiplecaseclauses.withinisamethodonFuturethatreturnsanewFuturewhicheithercompleteswithinthegivendu-ration,orfailswithatimeouterror.)Ifthefuturereturnedfromrewrite(..).within(..)fails,andthepartialfunctionisde-nedforthespecicfailureinthiscase,wematchontheexcep-tiontypeTimeoutErrortheerrorisrecoveredbysupplyingtheoriginalqueryvalue.Sinceeachcombinatorreturnsafuturerep-resentingtheresultofthecompositeoperation,wecanchainthemtogetherasinthisexample.Thisstyleisidiomatic.ComposingmultipledependenciesServerscommonlyperformscatter-gather,orfan-outoperations.Theseinvolveissuingre-queststomultipledownstreamserversandthencombiningtheirresults.Forexample,Twitter'ssearchengine,Earlybird[5],splitsdataacrossmultiplesegments;afrontendserveranswersqueriesbyissuingrequeststoareplicaofeachsegment,thencombinestheresults.Thecollectcombinatorresolvesmultipledependencies.ForsomevaluetypeA,collectconvertsasequenceoffuturesintoafutureofasequenceofA-typedvalues:defcollect[A](fs:Seq[Future[A]]):Future[Seq[A]](SeqisScala'sgenericcontainerforsequences.)Asketchofascatter-gatheroperationfollows.Givenamethodtoqueryseg-ments,defquerySegment(id:Int,query:String):Future[Set[Result]]weusecollecttorelatemultipledependencies;thesearchmethodfromthepreviousexamplecanthusbedened:defsearch(query:String):Future[Set[Result]]={valqueries:Seq[Future[Result]]=for(id0untilNumSegments)yield{querySegment(id,query)}collect(queries)flatMap{results:Seq[Set[Result]]--52;倀=Future.value(results.flatten.toSet)}}AswithflatMap,errorsinanyfuturepassedasanargumenttocollectpropagateimmediatelytothecomposedfuture:shouldanyofthefuturesreturnedbyquerySegmentfail,thecollectedfuturewillfailimmediately.YourServerasaFunction(Preprint)22013/9/27 thependingfuture(asreturnedbytheserver-suppliedService).ThisallowsustointerruptarequestinourfrontendHTTPserver,cancelingallongoingworkonbehalfofthatrequestthroughoutourdistributedsystems.Aswithourtracingsystem,thiswasim-plementedwithoutanyAPImodicationsorotherchangestousercode.Whileinterruptsviolatethepuredataowmodelpresentedbyfutures,consumersarestilloblivioustotheirproducers.Interruptsareadvisory,anddonotdirectlyaffectthestateofthefuture.Interruptsarenotwithoutproblems.Theyintroducenewseman-ticcomplications:Shouldcombinatorspropagateinterruptstoallfutures?Oronlytheoutstandingones?Whatifafutureissharedbetweenmultipleconsumers?Wedon'thavegreatanswerstothesequestions,butinpracticeinterruptsareusedrarely,andthenalmostexclusivelybyFinagle;wehavenotencounteredanyproblemswiththeirsemanticsortheirimplementation.4.3FiltersFiltershaveheldtheirpromiseofprovidingclean,orthogonal,andapplication-independentfunctionality.Theyareuseduniver-sally:Finagleitselfusesltersheavily;ourfrontendwebserversreverseHTTPproxiesthroughwhichallofourexternaltrafcowsusealargestackoflterstoimplementdifferentaspectsofitsresponsibilities.Thisisanexcerptfromitscurrentcongura-tion:recordHandletimeandThentraceRequestandThencollectJvmStatsandThenparseRequestandThenlogRequestandThenrecordClientStatsandThensanitizeandThenrespondToHealthCheckandThenapplyTrafficControlandThenvirtualHostServerFiltersareusedforeverythingfromlogging,torequestsanitiza-tion,trafccontrol,andsoon.Filtershelpenhancemodularityandreusability,andtheyhavealsoprovedvaluablefortesting.Itisquitesimpletounittesteachoftheseltersinisolationtheirinterfacesaresimpleanduniformwithoutanysetup,andwithminimalmocking.Furthermore,theyencourageprogrammerstoseparatefunctionalityintoindependentmoduleswithcleanbound-aries,whichgenerallyleadstobetterdesignandreuse.Wehavealsousedltersextensivelytoaddresslower-levelconcerns.Forexample,wewereabletoimplementasophisticatedbackuprequestmechanismusingasimplelterinabout40linesofcode(seeAppendixA).EngineersatTumblr,whoalsouseFinagle,report[16]theuseofalowlevelltertodeduplicaterequeststreams.4.4ThecostofabstractionHighlevelprogramminglanguagesandconstructsdonotcomeforfree.Futurecombinatorsallocatenewfuturesonthegarbagecollectedheap;closures,too,needtobeallocatedontheheap,sincetheirinvocationisdeferred.Whilewe'vefocusedonreducingtheallocationfootprintsandindeedcreatedmanytoolsforallocationanalysisitisanongoingconcern.Thetaillatenciesofmostofourserversaregovernedbyminorheapgarbagecollections.Inisolation,thisimpliesonlyasmallser-vicedegradation.Howeverourlargefan-outsystemampliessucheffectsasoverallrequestlatencyisgovernedbytheslowestcom-ponent;withlargerequestdistributionoften100sofsystemsencounteringminorgarbagecollectionintherequestpathiscom-mon.DeanandBarroso[7]describesimilarexperiencesatGoogle.Afrequentsourceofunintentionalgarbagecollectionpressureistheeasewithwhichspaceleakscanbeintroducedbythein-advertentcapturingofreferencesinclosures.Thisisampliedbylong-livedoperations,forexample,closuresthataretiedtolifetimeofaconnection,andnotofarequest.Milleret.al.'sSpores[14]proposestomitigatethesetypesofleaksbygivingtheprogrammerne-grainedcontrolovertheenvironmentcapturedbyaclosure.Inmostofourservers,majorcollectionsarerare.Thisgivesrisetoanotherkindofspaceleak:ifaPromiseispromotedtothemajorheap(forexamplebecausetheoperationitrepresentstookanunexpectedlylongtime),itsreferentvalue,evenifitsusefullifetimeisminiscule,survivesuntilthenextmajorgarbagecollection.Developmentdisciplineisanimportantmitigatingfactor.Inordertoensurethatallocationregressionsaren'tintroduced,wehavedevelopedatool,JVMGCPROF[9]whichrunsregularlyalongwithourtests,providingreportsonper-requestallocationratesandlifetimes.Thisisanareaofongoingeffortwithmanyintriguingpossibil-ities.SinceFinaglecontrolslogical-to-physicalthreadmultiplex-ingandisawareofrequestboundaries,itcanbiasallocation.Thisopensupthepossibilitythat,withthecooperationoftheunderlyingJVM,wemaymakeuseofregionallocationtechniques[13].4.5Futures,Services,andFiltersatTwitterThesetechniquesareusedtogetherwithourRPCsystem[10],throughoutourproductionruntimesystems.Almostallofourmod-ernserversoftwareisimplementedinthisway,includingourfron-tendservingsystem,applicationservers,webcrawlingsystems,databasesystems,andfan-outanddatamaintenancesystems.Thesystemsareemployedinamajorityofmachinesacrossmultipledatacenters.We'vefoundthesetechniquesexcelespeciallyinmiddlewareservers,whoseinterfaceisaServiceandwhosedependenciesareotherServices.Suchmiddlewarereducestoeffectivelybigfuturetransformers:it'scommontoexpresstheentiresysteminadeclarativefashion.Theadvantagesofuniformityextendbeyondindividualserversoftware.Statistics,tracingdata,andotherruntimeinformationisobservedwiththeuseofacommonsetoflters.Theseinturnexportsuchruntimeinformationinauniformly,sothatoperatorscanmonitoranddiagnoseoursystemswithoutknowingspecics.OurofinedataprocessingsystemshavenotyetadoptedthesetechniquesastheyareoftenbuiltontopofotherframeworkssuchasHadoop.5.RelatedworkLwt[22]isacooperativethreadinglibraryforOCamlwhosechiefabstraction,thelightweightthread,issimilartoourFuture.Dataowprogramminglanguages[8,21,23]alsoemphasizedependenciesbetweencomputations,performingconcurrentgraphreductionintheirruntimes.However,dataowconcurrencyde-mandsdeterminacy,requiring,amongotherthings,timinginde-pendenceandfreedomfromnondeterminateerrors(mostsuchlan-guagesrequirefreedomfromanyerrors).Thusinitsrawform,dataowlanguagesareunsuitableforsystemsprogramming.Royet.al.[19]proposeintroducingnondeterminateportstosplitadataowprogramintopure(determinate)parts,connectedbynon-deterministicchannels.Thisisintriguing,butinsystemsprogram-ming,nearlyallconcurrencyisnondeterministic.(Indeed,youcouldarguethatdeterministicconcurrencyisbetterdescribedasparallelism.)Haskell[2]andGo[1]providecheapuser-spacethreads,reduc-ingtheoverheadofthread-basedconcurrency.Theseruntimesman-agethreadsasacheapresource,andfreestheprogrammerfromtheYourServerasaFunction(Preprint)52013/9/27 A.BackuprequestlterclassBackupRequestFilter[Req,Rep](quantile:Int,range:Duration,timer:Timer,statsReceiver:StatsReceiver,history:Duration,stopwatch:Stopwatch=Stopwatch)extendsSimpleFilter[Req,Rep]{require(quantile0&&quantile100)require(range1.hour)private[this]valhisto=newLatencyHistogram(range,history)private[this]defcutoff()=histo.quantile(quantile)private[this]valtimeouts=statsReceiver.counter("timeouts")private[this]valwon=statsReceiver.counter("won")private[this]vallost=statsReceiver.counter("lost")private[this]valcutoffGauge=statsReceiver.addGauge("cutoff_ms"){cutoff().inMilliseconds.toFloat}defapply(req:Req,service:Service[Req,Rep]):Future[Rep]={valelapsed=stopwatch.start()valhowlong=cutoff()valbackup=if(howlong==Duration.Zero)Future.neverelse{timer.doLater(howlong){timeouts.incr()service(req)}flatten}valorig=service(req)Future.select(Seq(orig,backup))flatMap{case(Return(res),Seq(other))-525;=if(othereqorig)lost.incr()else{won.incr()histo.add(elapsed())}other.raise(BackupRequestLost)Future.value(res)case(Throw(_),Seq(other))-525;=other}}}Thebackuplterissuesasecondaryrequestifaresponsehasnotarrivedfromtheprimaryrequestwithinagiventime,parameterizedbytheobservedrequestlatencyhistogram.WhenaresponsearrivesthersttoarrivesatisfythefuturereturnedbyFuture.selecttheotherrequestiscancelledinordertoavoidunnecessarywork.(ServicesfurnishedbyFinagleareloadbalanced,sothatrepeatedinvocationstoservicearelikelytobedispatchedtodifferentphysicalhosts.)YourServerasaFunction(Preprint)72013/9/27