I Am MongoDB – And So Can You!

Preview:

Citation preview

Follow robert.rotarius@rakuten.com

AGENDAReally, really brief history of Rakuten Affiliate NetworkMoving faster with simpler processesMoving more efficiently with simpler technologySome things we learned along the way

REALLY, REALLY BRIEF HISTORY OF RAKUTENAFFILIATE NETWORK

A dinosaur in internet years.LinkShare was founded in 1996Rakuten acquired LinkShare in 2005Re-branded to Rakuten Affiliate Network in 2014

WHAT THAT GOT USTechnical DebtProcess Debt

BUT WHAT WE REALLY WANTED...Tech to feel empoweredMinimize distractionsEliminate blockers

THE CUSTOM REPORTING PROJECT

PROJECT GOALSSoftware Development

Introduce more structure to our JavascriptMore automated testsCleaner code, clearer patternsAPIs first

Operations

Move fast, efficient and iterate quicklyUse MongoDBShip Faster

THE SCHEMA { "_id" : ObjectId("5453ce5ee02222341b0000ea"), "user" : DBRef("User", "5453ce5de0945657b00000a"), "name" : "Sam Witwicky", "slug" : "sam-witwicky", "type" : "custom", "description" : "Cool human", "category" : "Humans", "updated" : ISODate("2015-02-24T03:00:57.802Z"), "created" : ISODate("2015-02-24T03:00:57.802Z"), "is_runnable" : true, "filter_groups" : [ { "_id" : ObjectId("5453ce5ee0909e9f1b0000eb"), "filters" : [ {

View on Github

THE MODEL namespace RM\Document;

use RM\Document\ReportColumn; use RM\Document\FilterGroup; use RM\Document\User;

use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

/** * @MongoDB\Document(repositoryClass="RM\Document\ReportRepository") */ class Report { /** * @MongoDB\Id

View on Github

THE CONTROLLER namespace AppBundle\Controller;

use AppBundle\Document\Report; use AppBundle\Form\ReportType;

use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;

use FOS\RestBundle\View\View, FOS\RestBundle\View\ViewHandler, FOS\RestBundle\View\RouteRedirectView;

use FOS\RestBundle\Controller\FOSRestController; use Symfony\Component\Routing\Route;

View on Github

THE HTML <div class="report"> <div class="report-id">5453ce5ee02222341b0000ea</div> <div class="user"> <div class="username">SomeUser</div> </div> <div class="name">Sam Witwicky</div> <div class="slug">sam-witwicky</div> <div class="type">custom</div> <p class="description">Cool human</p> <div class="category">Humans</div> <div class="updated">2015-02-24T03:00:57.802Z</div> <div class="created">2015-02-24T03:00:57.802Z</div> <div class="is-runnable">true</div> <div class="is-deleted">false</div> <div class="filter-groups"> <div class="filters">

THE JAVASCRIPT function Report() { var self = this;

this.name; this.description;

this.filterGroups = []; this.reportColumns = [];

this.addFilterGroup = function(filter){ self.filterGroups.push(filter); }

this.addReportColumn = function(reportColumn){ self.reportColumns.push(reportColumn); }

THE CSS .report { .name {} .description {}

.filter-groups { .filter { .operator {} .default-value {} } }

.report-columns{ .report-column { .format {} .sort-direction {} }

THE PLAYERS

SHIP IT!

Threat Level: LowJoeThe Product Manager

Automated testing helps him tooBeware of featuritis.Work with him to simplify the requirements

AUTOMATED TESTINGCreate a culture of testingImprove communication between stakeholders

BDD

FEATURE FILES

Scenario: Valid CRUD requests Given body of request: """ { "name": "My Special Report", "description": "The description of my special report" } """ When I make a POST request to "/reports/" Then the status code should be 200

When I make a GET request to "/reports/5453ce5ee02222341b0000ea/" Then the status code should be 200 Then the response should be: """

Threat Level: MediumDannyQA Guy

Long regressionsResistant to introducing code too close toreleasePush back on unrealistic test casesYou need to earn his trustGet him involved in the Feature File process

MOVE FASTER AND MORE EFFICIENTLYRelease more oftenEliminate distractionsDon't get woken up

Threat Level: HighSheilaThe System Administrator

Forced to limit her drinking for 1 week everymonth.Won't support your system withoutEVERYTHING being documented.She just wants to know how your systemworksYou are going to have to start sharingsupport duties

TAKING ON SUPPORTOne SOP to start: call the application ownerIncrementally build on existing proceduresTransition SOPs to Operations

USE MONGODBEasy to setupIntuitive query interfaceEasy to maintainJavascript friendly

No shardingLimited audience = Limited riskMongoDB was involved the entire wayConfidence was high

Threat Level: HighSamuelThe DBA

Fear introducing new thingsHates ORMsDoesn't like you designing your own schemaThinks NoSQL is a fadEnforcing data integrity at the app layerDe-normalization is okLow maintenanceAutomated backup and restore

WE MADE IT TO PRODUCTION. NOW WHAT?

THE PRIMARY NODE IS GETTING A BUNCH OF READS

READ YOUR OWN WRITE

WHAT IS IT?

IMPLEMENTATION WITH DOCTRINE ODM

namespace AppBundle\EventListener;

use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpKernel\Event\GetResponseEvent;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class MongoReadPreferredSubscriber implements EventSubscriberInterface { /** * @var Session */ protected $session;

/**

PERIODIC NETWORK PARTITIONS

WHY WE MOVED AWAY FROM ARBITERS

5 Node Replica Set (normal)

A & B cannot see each other. A1 wins an election.

Writes go to A1

A & C cannot see each other. B1 wins an election

Writes go to B1

Connectivity is restored

USING A DATA NODE INSTEAD

5 Node Replica Set (normal)

A & B cannot see each other. A1 wins an election.

Writes go to A1

A & C cannot see each other. C1 wins an election

Connectivity is restored

APP HANGS WHEN THE PRIMARY CHANGES(No candidate servers found)

PAY ATTENTION TO YOUR TIMEOUT SETTINGS

connectTimeoutMSThe timeout for establishing a connection.

socketTimeoutMSThe timeout for read-write operations, isMaster and ping requests

A PROPOSED FIXPHP-1223

Use connectTimeoutMS instead of socketTimeoutMS for isMaster and pingrequests

CONSIDERATIONSTimeouts vary driver to driverKeep connectTimeoutMS as low as possible

SINCE THEN...9 number of certified MongoDB DevelopersDBAs are currently handling production support issues for all but one cluster(no issues so far)We've built a couple sharded clustersLeverage the MongoDB support team as early as possible

WHAT'S NEXTGLOBAL EXPANSION

Tag Aware ShardingReplace homegrown backup solution with MMS Backup

Recommended