dimecres 20 de gener de 2010

Implementant servidors RPC

Recentment he implementat un servidor a la feina. Es tracta d'un programa relativament senzill que presenta una API en thrift, a on cada mètode retorna una llista de valors. A on és la gràcia? doncs la gràcia és la càrrega que ha d'aguantar aquest servidor, i saber i tenir en compte quins tipus de recursos hardware utilitzarà més.

Primer de tot ens hem de fer unes preguntes sobre el que li caurà a sobre:
  • Què aguantarà normalment, o sigui, quantes peticions per segon haurà de contestar?
  • Quantes peticions de mitjana?
  • Quantes peticions rebrà a l'hora punta?
  • Hi haurà algun cas excepcional (però que igualment s'haurà de cobrir) a on les peticions per segon es disparin?
  • Quin timeout tindrà el client que l'utilitzarà.

Següent, quins tipus de recursos hardware seran el coll de botella?
  • Utilitzarà molta RAM?
  • Farà moltes lectures a disc (el disc és local o bé remot?)
  • Utilitzarà molta CPU?
  • Utilitza recursos remots, com bases de dades?

Lògicament això depen totalment del tipus de servei que estem implementant, tots són diferents, però en el meu cas es tracta d'un servei a on el coll de botella és el disc. Estic llegint de diferents fitxers de text gegants, que canvien periòdicament i sense repetició de peticions del mateix valor, ah, i també necessito valors d'una base de dades remota. Què vol dir això?
  • El fet de que siguin fitxers gegants i bastants vol dir que no es poden llegir una vegada i tenir-los guardats a memòria. Per tant, cada vegada s'haurà de llegir de disc i…
  • Si mai es requereix el mateix valor vol dir que sempre s'hauran de llegir posicions diferents (i aleatòries) del fitxers i que per tant no val la pena tenir "valors populars" a la memòria cau perquè com a màxim es llegiran una vegada.
  • I de regal en la majoria d'invocacions he de consultar un valor en una base de dades.

Bé, tinguent en compte tot això… ho implementem. Uns dies després, s'haurà de provar.

Com ho proves un servei d'aquests?

Lògicament el servei tindrà una sèrie de testos unitaris i una sèrie de comprovacions. Però en aquest cas no em refereixo als testos d'integritat de dades i de funcionalitat. Em refereixo als testos de càrrega. Perquè no podem posar un servei en viu sense saber perfectament que aguantarà el que hagi d'aguantar. Oi?

La solució immediata és implementar un client que utilitzi l'API del servei. Una cosa tant senzilla com un bucle que invoqui mètodes i controlar els temps de resposta, amb això aconseguirem un nivel de càrrega baix, però ja tindrem per començar. Els següent pas és paral·lelitzar aquest client, fer que crei diferents threads, i que cada thread faci les seves peticions al servidor, aquí ja podem aconseguir un nivell de càrrega decent, tot i que dependrà de l'ample de banda de la màquina a on estiguem corrent el client. El següent pas és utilitzar aquest client en diverses màquines a l'hora contra el mateix servidor, aquesta és la única manera amb la que es podrà recrear un volum de trànsit "real".

Ara bé, a l'hora d'implementar el client, uns punts que s'han de tenir _molt_ en compte:
  • S'han d'invocar _tots_ els mètodes del servidor, amb paràmetres _reals_ i vàlids. Així recrearem les peticions òptimes que hauria de rebre el servidor.
  • S'han d'invocar _tots_ els mètodes del servidor, amb paràmetres _reals_ i _invàlids_. Un cop tinguis el servidor instal·lat i de "cara al públic" mai sabràs el que t'arribarà. No té sentit optimitzar el servidor si llavors un paràmetre fora d'ordre o fora de rang fa que saltin excepcions de forma interna o bé que es bloquegi. En el món real arribaran peticions invàlides, s'han de tenir en compte.
  • S'ha de provar el servidor a poder ser amb dades reals o bé de la mateixa magnitut de les dades amb que treballarà. El programa no tardarà el mateix en llegir un fitxer de 2Mb que un de 200Gb, depen del que facis fins i tot es pot quedar sense memòria. De la mateixa manera que si s'utilitza una base de dades no és el mateix fer una consulta a una taula amb 100 files d'una base de dades de test local que a una taula de centenars de milions de files en una base de dades en producció amb diversos usuaris utilitzant-la de forma concurrent.
En el meu cas vaig programar un client multithread en java amb tota una bateria de peticions vàlides i invàlides, que anava cridant de forma aleatòria, vaig tenir la sort de poder provar el servidor amb dades reals, ja que és només de lectura en una màquina exactament igual a la màquina de producció.

Pel tema de provar el client a l'hora en diverses màquines vaig poder utilitzar 40 màquines a l'hora amb 100 threads cadascunta, això son 4000 threads fontent canya al servidor :) .

Ah, una eina molt pràctica i senzilla per paral·lelitzar comandes en bash és dsh.

Vaja, tarda molt, es queda sense memòria o es penja.

Bé, el servidor funciona bé amb un thread, ja li costa més amb 100 threads i deixa de respondre en 4000. Què passa?

Això ja depén bastant del llenguatge d'implementació. En el meu cas vaig utilitzar Java i una molt bona eina per trobar problemes d'eficiència és jstack que permet veure volcats de la pila d'una aplicació corrent sense matar-la.

Bàsicament es tracta d'executar aquesta aplicació al servidor i llavors buscar entra els volcats de pila dels diversos threads el que estan BLOCKED, en cas de que n'hi hagi vol dir que són threads que estan en l'espera d'alguna cosa. Això pot donar pistes de llocs a on es pot optimitzar el codi.

i finalment, us deixo amb:

les receptes de l'àvia per optimització en Java.

Algunes coses que convé recordar a l'hora de programar en Java (tot i que algunes són de calaix):
  1. En cas de que s'utilitzi bases de dades. És important que les connexions estiguin en un "pool" i que no es crein/destrueixin cada vegada. En cas d'utilitzar Spring/Hibernate, cal saber què s'està fent. Aquests frameworks ténen moltes classes i cadascuna fa una cosa, convé utilitzar la que toca.
  2. També en cas de que s'utilitzi una base de dades. Vulguis o no, pot ser costós, sobretot si es fa moltes vegades. Cal pensar sobre el resultat que n'obtenim. Spring/Hibernate utilitzen Cache per emmagatzemar valors de forma transparent, això és una cosa que també es pot fer a nivell d'aplicació. Abans d'anar a la capa de dades, pensem-ho dues vegades, si ja hem buscat exactament allò a la base de dades quina és la probabilitat de que hagi canviat? si és poca, no cal fer la consulta. Posem el resultat en cache i ja ho preguntarem de nou al cap de 1 minut, 10 minuts o 1 hora.
  3. Utilitzem llistes? LinkedList o ArrayList? Utilitzem conjunts? HashSet o TreeSet? la elecció de l'estructura de dades òptima pot tenir implicar unes diferències de recursos i de velocitat impressionants. Ah, i lògicament si sabem més o menys la mida que tindrà la llista resultat, inicialitzem la talla de la llista al crear-la. En aquest cas, la api de sun és la vostra amiga.
  4. Estem concatenant strings? doncs StringBuilder.
Espero que l'article sigui clar i que almenys el checklist de coses a provar estalvïi problemes a algú :)

diumenge 8 de novembre de 2009

SOAP fail

Estic veient una tendència a la xarxa?

Blog de Github, post sobre RPC's

"XML-RPC, SOAP, and other XML based protocols are hardly even worth mentioning. They are unnecessarily verbose and complex. XML is not convertible to a simple unambiguous data structure in any language I’ve ever used. I’ve wasted too many hours of my life clumsily extracting data from XML files to feel anything but animosity towards the format."

Un altre, blog que es lamenta dels SOAP's.

Del paper sobre Thrift (by Facebook): Thrift és un RPC que han fet els de Facebook per ús intern, al paper on n'expliquen les bases fan una comparació amb altres frameworks i dónen els motius pels quals no els han utilitzat, en cas de SOAP, simplement: 

"SOAP. XML-based. Designed for web services via HTTP, excessive XML parsing overhead."

Estic convençut d'haver llegit més històries recentment, però vaig ser massa mandrós per anar-les apuntant. 

En definitiva. No utilitzeu SOAP Web Services. Algun dia explicaré perquè els hi tinc tanta mania. 

dijous 29 d’octubre de 2009

Spring killed the Java star.

Un títol suggerent no ? 

Els que em coneixen sabran que mai he sigut un fan ni de Spring ni d'altres frameworks ni convencions de programació. No sé, sempre he sigut de la vella escola

Anem en compte. Són uns grans frameworks i realment ajuden en el desenvolupament, sobretot d'aplicacions d'un nivell avançat i complicat. I tot i que els utilitzo poc, els utilitzo. 

Actualment a last.fm estem buscant gent, tenim dues posicions obertes:

  • Enginyer de Dades: Necessitem un enginyer que es manegui bé treballant amb moltes dades (Terabytes) i Java. 
  • Enginyer de Catàleg: Hauria de tenir un nivell avançat de Java + SQL + Postgres. 

Que ho sapigueu. 

Bé, estem fent entrevistes de feina i tenim alguns problemes. Bàsicament tenim moltes aplicacions de gent que sap Hibernate, però no domina SQL i sap Spring, però no domina Java. Fatal.

Per què? doncs per els següents motius:

  • Bases de dades: Hibernate es una solució meravellosa. Un cop tens tota la història configurada et permet accedir a la base de dades de forma còmoda, sense històries i sense SQL. Meravellós oi? doncs sí. Ara bé! què passa quan hi ha una urgència i s'han de fer queries complicades a la base de dades des d'un terminal per ssh. Ja l'hem liada. Doncs no pot ser. 
  • Java: Aquest tema és més general. Molta gent que sap Spring té clares com fer les coses, però moltes vegades no sap perquè es fan d'aquesta manera. Si t'estàs enfrontant amb un projecte clàssic, com és tenir una base de dades amb un nivell de domini ben definit, un nivell amb els controladors i el nivell de vistes el que t'importa és fer les coses el més bé i el més còmodament possible, en aquest cas Spring (o qualsevol altre framework) és una solució idònia.  Quan estàs en una situació estranya què s'ha de fer? doncs tirar de l'API de Java. I si és una situació estranya cal dominar-la. 

El cas de last.fm és bastant peculiar. El meu equip treballa amb moltes quantitats de dades, és per això que entre d'altres eines utilitzem Hadoop. Però no sempre es soluciona tot amb Map Reduce. 

Amb un volum d'entrada de dades normals, l'elecció entre un HashMap o un TreeMap  (lògicament per alguna tasca auxiliar i no guardar les dades!) pot sembla anecdòtica. La diferència serà de microsegons. Ara bé, si l'entrada és de Terabytes de dades la diferència pot arribar a ser d'hores en quan a velocitat, apart de que podem rebentar el límit de memòria de la màquina virtual. Per tant, en el nostre cas, són coses que s'han de saber. 

Lògicament són coses que s'aprenen amb experiència, però estic veient que aquests tipus de coneixements, els coneixements sobre com funciona un llenguatge o un compilador a fons manquen cada vegada més i més. Aquest tipus de frameworks estan apartant coneixements bàsics d'informàtica que sempre s'haurien de tenir en compte.

Bé, això són tant sols unes reflexions personals. Cal saber de tot i aquests frameworks són importants, ara bé, com tot, s'ha de saber quan s'han d'utilitzar i quan no. 

dijous 30 de juliol de 2009

Technical documentation guidelines.

After some years of working as a developer I've realized the lack of standard guidelines when it comes to writing technical documentation. Not user manuals. Technical documentation, the one you should write when you develop a system, or the one you should get when you start maintaining a system somebody developed.

So, no standard guidelines around ? (at least that i liked), no problem, open source, open world. I made my own.

I started researching, getting technical documentations from different projects, getting the opinions from lots of good professional developers and using lots of common sense (my common sense, well, and sometimes other people's common sense) and this is what i came to:

Ok, I tried, I promise, I tried. I cannot format the document in Blogspot. I've created a static html page. You can find the article: HERE

dissabte 11 de juliol de 2009

Hive

Aquest post parlo de Hive, una eina que han fet a Facebook que converteix un llenguatge d'accés a dades semblant SQL a treballs map-reduce per fer consultes als directoris guardats en el DFS de Hadoop.

Un dels problemes que tenim els usuaris de Hadoop és que estem treballant amb uns volums de dades considerables. En cas contrari seria millor utilitzar Python o Erlang o una base de dades relacional (insisteixo una vegada més, Hadoop si i només si s'ha de treballar amb moltes dades). Com ja he explicat en altres posts un dels sub-projectes de Hadoop és el sistema de fitxers, fet a inspiració del Google File System (GFS). És un sistema redundant i distribuït, per tant cada fitxer està fragmentat per la xarxa i n'hi ha diverses còpies. Això implica que sigui molt difícil perdre un fitxer (ja que n'hi ha 3 còpies diferents), però al mateix moment es fa més complicat poder accedir a aquest fitxer, ja que qualsevol manipulació que se li vulgui aplicar (sense un treball map/reduce de Hadoop) força que s'hagi de baixar del clúster, i aquests fitxers poden arribar a ser molt grans (molt molt grans).

Això a efectes pràctics significa que no es pot fer de forma senzilla un 'grep' per buscar continguts. El fitxer està repartit pel clúster per tant, s'ha de fer un treball map-reduce per fer el grep. El codi de hadoop ja porta implementada aquesta utilitat, però igualment és incòmode i poc potent.

En principi no hauria de ser un problema, ja que normalment interessa processar una entrada i un cop tenim la sortida a punt la baixem tota del clúster per servir-la a la pàgina web, per inserir-la a la base de dades o el que sigui. El problema ve quan necessites saber (urgentment) una certa línia d'un fitxer de 200 Gb que està fragmentat dins el clúster.

Potser això és una mica difícil d'entendre, un exemple:

Com sabeu fa poc es va morir Michael Jackson, doncs bé, quan la noticia es va escampar la gent va començar a escoltar la seva música. A last.fm vam començar a rebre scrobbles de Michael Jackson, aquests scrobbles, que basicament hi ha l'identificador d'usuari i la cançó que aquest ha escoltat es guarden al sistema de fitxers distribuït per a ser processats per Hadoop. No ens volíem esperar a que fos al vespre per saber quanta gent de més havia escoltat al difunt rei del pop. La solució era fer un programa (en Java o bé en Dumbo) per agafar aquestes dades i fer un grep per veure quants usuaris era, una cosa que utilitzant bash senzillament seria:

bash $> grep michaeljackonid fitxer_scrobbles | sort -k columna_usuari -u | wc -l

és en Java molt més pesat i complicat d'escriure.

Els de l'equip de dades ja havíem tingut diverses peticions d'aquestes anteriorment, i ja tenim una sèrie de programes (mal fets, a base de copy and paste, ràpids) per fer consultes. El principal problema és que d'aquesta manera s'acaba tinguent una bateria d'scripts de consulta que són impossibles de reutilitzar, totalment específics i que serveixen tant sols una vegada.

Però aquesta vegada va ser diferent, perquè des de feia uns dies estàvem jugant amb Hive!

Segons la seva pàgina, Hive és una infraestructura de 'warehouse' (magatzem de dades) que corre sobre Hadoop i permet la consulta de dades. Per tant, és en certa manera. Una infraestructura per córrer SQL sobre directoris de DFS.

Convé tenir en consideració els següent punt, molt important:

  • Hive no és una base de dades.


El que fa Hive és tenir en uns fitxers propis del DFS de Hadoop una sèrie de dades que en certa manera descriuen les estructures de directoris dels directoris normals del DFS. Un cop tenim això, la línia de comandaments de Hive permet fer consultes amb SQL. S'ha elegit aquest llenguatge de consulta perquè (desgraciadament?) és el que sap tothom i realment no fa falta crear-ne cap altre.

Aquesta consulta SQL és convertida per Hive en diversos passos, com podem veure en el document d'arquitectura en una sèrie de tasques Map Reduce que corren de forma normal al clúster.

Aquestes tasques senzillament agrupen les dades i les filtren tal com hem dit a la consulta, i en cas que sigui necessari les ordenen (order by), treuen duplicats (distinct)...

Per tant, amb Hive podem córrer una consulta sobre totes les dades del sistema de fitxers distribuït, de forma senzilla i sense haver de carregar totes les dades en una base de dades relacional.

A mi personalment, m'estalviarà moltes hores, ah, i per cert, l'augment d'oients de Michael Jackson el podeu trobar aquí.

dimarts 16 de juny de 2009

Nosql

Tot just la setmana passada es va celebrar la primera trobada Nosql a San Francisco, sobre bases de dades distribuïdes i no relacionals. Coincidint amb el Hadoop Summit 2009.

Bàsicament va ser una trobada dels principals desenvolupador / usuaris de bases de dades no relacionals (basades en Google BigTable i Amazon Dynamo), on cada desenvolupador va explicar les característiques del seu sistema.

Aquí us deixo un link del blog de'n Johan Oskarsson que hi va assistir amb els links a totes les transparències utilitzades així com els videos. Espero que us sigui de servei!

dilluns 15 de juny de 2009

Hadoop, the definitive guide.

S'acaba de publicar a O'Reilly 'Hadoop, the Definitive Guide'. Un llibre sobre Hadoop (lògicament) que ha escrit Tom White i en el qual he contribuït en un capítol d'experiències.

pd.- Lògicament sóc el culpable de la imatge de la pàgina 413.