Node.js og MongoDB efter et halvt år i deres selskab

Jeg har været med til at udvikle flere Node.js og MongoDB -applikationer, og de sidste 3-4 måneder har jeg været en del af et team på 4-5 mand hos e-conomic, der udvikler en løsning på Node.js og MongoDB fuld tid. Vi udvikler primært et REST API, der bruges af en webklient og en mobilapp. Senere skal API’et være offentligt ud til partnere. Her er lidt løst og fast om, hvad jeg synes om det, hvad vi har gjort og også en masse råd givet ud fra mine erfaringer.

Vildt sjovt

Generelt er jeg fuldstændig pjattet med at udvikle på platformen. JavaScripts dynamiske natur, mongo’s dokumenter, intet skema til data i mongo og mongo’s API er helt fantastisk at arbejde med. Du skal have lyst til at nørkle lidt med tingene, da de moduler der er til node, ikke alle er lige finpudsede. Det gode ved det er, at jeg kender detaljerne i, hvordan vores applikation fungerer. Og der er faktisk rigtigt mange open source node-moduler at vælge imellem.

Noget af det, jeg synes, der gør node-udvikling specielt fedt er, at der ikke er et mega stort framework, man skal lære. Jeg er overbevist om, at den minimale tilgang til tingene, og det at jeg som udvikler er så meget tættere på metallet har gjort, at vi har fået en løsning, hvor koden er vedligeholdelsesvenlig og gennemskuelig. Det er en rigtig god fornemmelse at være oprigtig stolt over den kode, vi har lavet.

Fungerer både på Linux, Mac og Windows

De fleste af os hos e-conomic udvikler i Webstorm på Windows, der har integreret node debugging, men en bruger også Mint en gang i mellem, en anden Ubuntu med Sublime text 2 og tidligere havde vi en der brugte Mac. Node og mongo kører upåklageligt på alle platforme, og det tager vitterligt kun 10 minutter at downloade og installere. Og indtil videre har vi ikke skullet ændre en eneste konfiguration for hverken Node eller Mongo. Det spiller bare!

Vores staging og produktionsmiljø kører Linux. Vi har kun oplevet meget få problemer mellem platformene, så indtil videre fastholder vi den strategi, at vores applikation skal kunne køre på både Windows, Mac og Linux. Så kan udvikleren selv vælge sit lokale OS efter smag og behag.

Denne multi-OS-strategi gør, at der er nogle få node-moduler, som vi ikke kan bruge, fordi de kun virker på Linux. Men indtil videre har vi kunnet finde en erstatning for de moduler eller helt undværet dem. Det største problem har været, at på Windows er fil- og mappenavne case-insensitive - det er de ikke på Linux.

Services, værktøjer og node-moduler, vi bruger

  • Git og github.com til versionsstyring, review af kode laves bandt andet ved at skrive kommentarer inline i commits på github.com.
  • Webstorm og Sublime text 2 som IDE
  • Test: Mocha som test-rammeværk, med chai til assertions, jsCoverage til code coverage rapporter, restler til at kalde vores API i integrationstests og injectr til dependency-injection.
  • Centrale node-moduler: MongoDB native driveren, express, gzippo, JSV til JSON skema-validering og grunt til build.
  • Loggly.com til logging i skyen
  • Hos Heroku.com og MongoHQ.com lejer vi testserver i skyen. Node og mongo er allerede installeret som services.
  • JMeter til loadtest
  • Snart har vi Team city som CI server, med busy pull fra github. Hvis jeres CI-server kan nås fra internettet, kan github også pushe til den på commits.
  • Jeg bruger dropbox til min kode. Det spiller fint sammen med git. Jeg kan uden videre stoppe arbejdet derhjemme og bare fortsætte hvor jeg slap på kontoret. Dropbox’s filversionering har reddet min røv flere gange, når jeg har gjort i nælderne, mens jeg lærte git.

Node som webserver til REST API

Der er mange meninger om kodestil, frameworks osv. til node. Vi var heldige, at vi var mange erfarne JavaScript- udviklere på vores team. Hvis JavaScript er nyt for dig, skal du være parat til at sætte dig ind i en hel del. Når det er sagt, så er serverside JavaScript meget forskelligt fra clientside JavaScript. Du kan ikke genbruge alle dine best-practices fra clientside JavaScript.

Det giver næsten aldrig mening at have den samme kode på klienten og på serveren. Men en sjælden gang kan JavaScript bo først det ene sted og så blive flyttet.

Med node kommer der en helt ny eventbaseret concurrency-model. Tingene er gjort nemmere, fordi ingen kode-stump kører samtidigt med en anden. Til gengæld er din kode delt op i en masse små bidder (callbacks), og der kan ske alt muligt mellem at bidderne bliver kørt. Nogle gange gælder det om at holde tungen lige i munden. Men pas på med at forfalde til at bruge et bibliotek som async første gang du støder på udfordringer. Ofte kan du klare dig ved at omstrukturere din kode en smule. Kode der bruger async kan være meget svær at læse for andre. Jeg tror kun vi bruger async et eneste sted i vores produktions-kode, og den kode har jeg en idé til at omskrive uden async og gøre simplere.

Undgå at kaste fejl og bruge try-catch, men brug i stedet node-konventionen, hvor du kalder din callback med fejlen som første parameter. Callbacken kaldes altid med null som første parameter, hvis der ikke er en fejl.

Brug function-hoisting i stedet for nestede callbacks. Navngiv din callback-funktion, så den beskriver det skridt, den laver i din callback-chain. Så undgår du effektivt callback-hell.

Du skal helst aldrig lave noget CPU-tungt i node. Hvis du vil køre et CPU- tungt job, bør du starte jobbet asynkront på en applikationsserver. Vi bruger for eksempel et par applikationsservere til PDF-generering.

Vores logs på loggly.com har været guld værd, når der har været bugs. Smart søgefunktionalitet.

Når vi laver loadtest, kan en enkelt JMeter klient ikke presse node og mongo nok. Der skal flere klienter til, hvis de virkelig skal svede.

Valg af web-framework og node- moduler

Der er mange web-frameworks til node. Express er det mest brugte, men der er også geddy, tako, flatiron og andre. Brug tid på at researche dem, og vælg det rigtige til dine behov. Personligt har jeg ikke meget fidus til dem, der gør meget ud af at bygge modeller. Med mongo og JSV synes jeg, det er overflødigt, men det er måske fordi vi bygger et REST API.

Med nodes package-manager, npm, kan du få en liste over mest brugte node- moduler. Når du skal bruge andres moduler, er det er en god idé at vælge et node- modul , der ligger højt på listen og har en god aktivitet på github.

Oprindeligt havde vi brugt Jasmine til vores tests, fordi det bliver brugt til test af vores clientside JavaScript. Vi skiftede undervejs til Mocha. Dels fordi den meget asynkrone natur af node-kode er meget nemmere at skrive i Mocha og dels fordi vi kunne få code coverage- rapporter med Mocha. Det kan man ikke med Jasmine.

Vores valg af JSV til JSON- skemavalidering i node-koden har været utrolig givtigt. Det kan jeg anbefale. Vi bruger JSV til at verificere data, der sendes til vores API, og kun indirekte til at verificere dele af det data, der gemmes i mongo. Til API-dokumentationen genererer vi et eksempel på den JSON, du kan sende til os udfra JSV-skemaet.

Mongo

Mongo er nem at udvikle op imod: Du begynder bare at skrive data ned i den, uden nogen form for konfiguration. Der er ingen DDL. Senere kan du tilføje indekser af performancegrunde.

Husk at beskytte dig mod JSON-injection og mass assignment-type-angreb.

Du skal selv stå for referentiel integritet i mongo - der er ingen foreign-key-constraints. Dem implementerer du selv i node-koden. Regn med, at du skal bruge noget tid på at omstille dig til en skemaløs dokument-database, hvis du er vant til relationelle databaser.

Mongo har atomare transaktioner på dokument-niveau, men ikke på tværs af flere dokumenter. Så hvis du har mange store transaktioner, der berører mange entiteter, er mongo ikke den rigtige database.

For hver skrivning i mongo kan du graduere, hvor sikker du vil være på at data er persisteret. Der er en glidende overgang fra ”fire and forget”, til data skal være skrevet i databasen på disk på N-servere i dit replicaset. Vælg den strategi, der passer til det data, du skriver fra gang til gang.

Du skal være parat til at indføre andre datastores til specielle brugsmønstre. Det er for eksempel ikke oplagt at bruge mongo til at trække rapporter i stor stil. Her skal du nok vælge et andet datastore til at trække rapporterne fra.

Du skal tænke grundigt over sikkerheden mellem node og mongo, specielt hvis du vil hoste i skyen. Som standard er datatrafikken mellem node og mongo ikke krypteret, og kryptering er ikke understøttet direkte i mongo-protokollen. Hvis dit data skal sikres, så skal forbindelsen sikres, fx med SSL. Det samme gælder trafikken mellem noder i et mongo-replicaset. Mongo har ikke et indbygget audittrail.

10gen der laver mongo har været imødekommende og vi har haft mange gode telefonmøder på skype med dem. De har været ret klare i mælet, når det handler om ting, som mongo ikke kan. De står også klar med konsulenter på timepris, der kan give dit design et review, fintune dine indekser osv.

Hosting

Det har været svært at finde en enkelt leverandør, der kan tilbyde 24/7-support og hosting af både mongo med replicasets og SSL samt node med loadbalancing og failover. Så vær forberedt på, at det kan tage noget tid at finde en leverandør, hvis du har krav til hosting, der lugter bare lidt af enterprise. Husk at node-serverne helst skal ligge i samme datacenter som mongo-serverne. Det gør de fx med heroku og mongohq, mens nodejitsu og mongohq ikke spiller godt sammen.

Hvis du hoster selv, eller selv står for node og mongo oven på lejet hardware, så er node nemt at hoste, mens mongo godt kan kræve en del operations-tid i produktion. Mongo har lidt udfordringer, for eksempel er det ikke nemt at få diskplads tilbage, når du sletter data. Mongo er som alle databaser glad for meget RAM.

Det var en stor succes, at jeg kunne test-deploye vores applikation gratis på Heroku.com og MongoHQ på en halv dag, så forretningen kunne se applikationen i drift. Det vil specielt imponere, hvis du arbejder i en organisation, der ikke er i skyen, så det at bestille servere koster kassen og tager lang tid. Når du først har sat det op, så tager det kun 1 minut hver gang, du deployer. Du skal bare push’e til et git repository på heroku, med en enkelt git kommand.

Blod på tanden?

Hvad siger du? Har du selv node og/eller mongo- erfaringer, du vil dele? Har du fået blod på tanden til at prøve node og mongo? Du kan downloade node og mongo gratis og installere dem på 10 minutter - og de kører som nævnt på både Windows, Mac og Linux.

Allan Ebdrups billede
Allan er ansat som Process & technology-lead hos Debitoor, hvor han arbejder med webbaserede regnskabsløsninger. Han arbejder med Lederskab, Continuous Delivery, Node.js og MongoDB.

Kommentarer (17)

Nikolaj Brinch Jørgensen

Det største problem har været, at på Windows er fil- og mappenavne case-insensitive - det er de ikke på Linux.

Men det kan I da bare lave om (hint: SFU)

Meget interessant artikel, og jeg glæder mig til at læse flere om projektet. Hvordan tester I? Er det Cucumber?

Rasmus Andersen

Har I tjekket detaljeret RAM-forbrug? Mongodb baserer sig jo på memory-mappede filer så det virtuelle hukommelsesforbrug vil se vildt ud, men hvordan ser det fysiske forbrug ud?

Nikolaj Brinch Jørgensen

Har I tjekket detaljeret RAM-forbrug? Mongodb baserer sig jo på memory-mappede filer så det virtuelle hukommelsesforbrug vil se vildt ud, men hvordan ser det fysiske forbrug ud


Det er svært at sige noget om. Afhængigt af OS configuration, kan det jo være at MongoDB kan have det hele i RAM, og ikke behøver bruge swap. Men det er vel OS der afgør dette.
Memory-mapped files, bruger virtual memory, så det kan være det hele loades i RAM.
Hvorfor er det interessant? De kan jo bare øge mængden af RAM i serveren, hvis den er for sløv.

Allan Ebdrup

Men det kan I da bare lave om (hint: SFU)

Det vil jeg undersøge, tak for hintet.

Meget interessant artikel, og jeg glæder mig til at læse flere om projektet. Hvordan tester I? Er det Cucumber?

Vi bruger en mere håndbåren oversættelse af acceptancekriterier, og det gode game trick med at tale sammen :-) Vi bruger BDD varianten af Mocha til tests. Vi har også en god mængde selenium-test til webklienten.

Når jeg læser om praktiske erfaringer med cucumber på nettet synes jeg billedet er noget broget. Har du da gode erfaringer med cucumber?

Jeppe Cramon

Tak for en god feedback rapport, der udmærker sig ved at være baseret på reelle erfaringer :)

Jeg studsede lidt over din mongo kommentar:

Mongo har atomare transaktioner på dokument-niveau, men ikke på tværs af flere dokumenter. Så hvis du har mange store transaktioner, der berører mange entiteter, er mongo ikke den rigtige database.

Dokument databaser har den fordel at de er gode til at gemme Aggregates (i DDD terminologi), hvor man samler entiteter og value objekter som naturligt hører sammen (logisk og transaktionelt). Se http://dddcommunity.org/library/vernon_2011 for en god beskrivelse af forskellige aggregate design tilgange.

Aggregates definerer i henhold til Life Beyond Distributed Transactions, http://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf, dine transaktions boundaries (specielt hvis du har et ønske om at kunne skalere både performance- og udviklings mæssigt).
HVIS man har en usecase der kræver atomar opdatering af to aggregates, bør man stærk overveje om ens model er rigtig (f.eks. bør man overveje behovet for at introducere stateful state machines også kendt som Sagas) eller om man har introduceret forretnings mæssigt kompleksitet for at undgå race conditions (som alligevel ikke kan løses med distribuerede transaktioner).

/Jeppe

Erik Cederstrand

Jeg har brugt MongoDB i et projekt, hvor jeg ofte slettede væsentlige dele af databasen og indlæste nye data. Altså mere cache og mindre DB-lignende brug. I hvert fald på FreeBSD voksede diskforbruget med tiden voldsomt. Jeg havde under 100MB data men endte med over 6GB data på disken.

Desuden er det rart med et ordentligt admin-værktøj til debugging. Jeg er f.eks. glad for SequelPro til MySQL. Her halter MongoDB efter. Der er MongoHub til OS X, men den er ustabil ad h*** til.

Robert Larsen

Meget interessant artikel. Jeg selv sidder og hakker på en Node baseret chatserver, som gerne skal kunne håndtere en 20-30 tusind samtidige forbindelser. Den bruger Socket.io baserede forbindelser, så alle browsere skulle kunne være med.

Allan Ebdrup

Dokument databaser har den fordel at de er gode til at gemme Aggregates (i DDD terminologi), hvor man samler entiteter og value objekter som naturligt hører sammen (logisk og transaktionelt).


Yep. MonogDB har en grænse på at et dokument maks må fylde 4MB, og i praksis er der en gang i mellem behov for transaktioner der berører flere dokumenter. Hvor ofte din applikation har brug for det, afgør om mongo er en oplagt database til din applikation.

Allan Ebdrup

Har I tjekket detaljeret RAM-forbrug? Mongodb baserer sig jo på memory-mappede filer så det virtuelle hukommelsesforbrug vil se vildt ud, men hvordan ser det fysiske forbrug ud?


Det kommer meget an på hvor stort dit "working set" er for din applikation. Det skal kunne være i memory med mongo. På en af de applikationer jeg har bygget var mongo databasen på 20GB, men pga brugsmønsteret klarede mongo sig med ca 1GB fysisk hukommelse.

Nikolaj Brinch Jørgensen
Allan Ebdrup

Bruger I en anden database til jeres ERP-system? En transaktion kan være f.eks. både debitorposter og finansposter og det vil være et problem, medmindre det er det samme dokument.


Der er måder at kommer rundt om mongo begrænsninger:
http://cookbook.mongodb.org/patterns/perform-two-phase-commits/
Men hvis man skal lave den slags tricks hver gang man opdaterer data, så bliver det et kæmpe arbejde, specielt hvis transaktionerne rører rigtigt meget data. Det vil højst sandsynligt også gå ud over mongos performance.
Det er derfor jeg taler om at mongo ikke er den rigtige database hvis man har brug for mange af den slags transaktioner.

Log ind eller opret en konto for at skrive kommentarer

IT Businesses