SlideShare a Scribd company logo
Mongo or Die!
How MongoDB powers Doodle or Die




        Aaron Silverman
             (@Zugwalt)
Doodle or Die




@DoodleOrDie
What is Doodle or Die?
Telephone
Phrase     I'd like some beer!

Phrase     I'd like some deer

Phrase     I'd like some deer

Phrase     I'd like some deer
Doodle or Die

Doodle


Phrase         Shining Apple


Doodle

Phrase      Eat your fruit or DIE!
MongoDC 2012: How MongoDB Powers Doodle or Die
MongoDC 2012: How MongoDB Powers Doodle or Die
MongoDC 2012: How MongoDB Powers Doodle or Die
MongoDC 2012: How MongoDB Powers Doodle or Die
What Powers Doodle or Die?
MongoDC 2012: How MongoDB Powers Doodle or Die
Started very, very small


            4 Cores
          128MB RAM
            Node Server

           MongoDB Server
Got serious about our servers

                     Small - 2GB

     8 Cores      MongoDB Server
   256MB RAM
    Node Server
Called in some Reinforcements


                      Large - 5GB

    12 Cores        MongoDB Server
    1GB RAM
     Node Server
In the last 30 Days:

• 2,500,000 page views

• 100,000 uniques

• 35,000 active player accounts

• 2,000,000 new doodles and descriptions
Data Information
Mongo DB            • Player Info
                    • Chain Info (minus
3.5 GB data size
                      doodles)
0.5 GB index size   • Group Info
~10 queries/sec     • Game State
                    • Logs
Amazon
170 GB data size    • Doodles!
                    • Static Content
8 GB in/month       • Compressed
200 GB out/month      Database Backups
MongoDB - $65 / month




Daily backups to S3: $16/mo
Amazon S3 - $70 / month
Node - $62/ month
Total Cost To Host: $197/month
     MongoDB     $65
     Amazon S3   $70
     Node        $62
     Total       $197
PAAS Provides Easy Upgrade Path
General Principles
Custom _id
Partially random string generated using
ShortId node module
                        ObjectId
        ObjectId("4fd02d5d78315a502d15cdde")
        ObjectId("4fd02d5a78315a502d15cddd")
        ObjectId("4fd02d5878315a502d15cddc")

                      ShortId
                "8rOIwh2VD"
                "1qyY61Lu1"
                "5GQnbx-1"
• Shorter, less cumbersome in code /
  queries
db.players.findOne({_id: ‘58mwYlTKV’});

db.chains.update({_id: ‘58mwYlTKV’},
          {$set: activePlayer_id: ‘88ueYaL6V’});


• Randomness will be better for sharding
  and makes it harder to cheat
http://doodleordie.com/c/5ONtvvSGH


<span class="doodle" data-jsonp="http://doodles.s3.amazonaws.com/d2/Eh8-
Po2R5/1Em5kj3LY.js">
Question Oriented Subdocuments

  What chain is this player working on right now?




  What are this player’s stats?




  Which players are not eligible to be assigned this chain?
Goal is for most “Questions” to be able to
be answered in one query from one sub
document

db.players.findOne({_id: ‘58mwYlTKV’},
             {‘game.recentSkips’: 1});



Related “Questions” will share common
ancestors

db.players.findOne({_id: ‘58mwYlTKV’},
             {game: 1});
Indexes are designed to make answering
questions easy!

What chain is this player working on right now?
db.players.ensureIndex({‘game.activeChain_id’: 1});




What chains are recently awaiting a new doodle?

 db.chains.ensureIndex({inUse: -1,
              activeState: 1
              lastModified" : -1});
Doodle or Die Collections
MongoDC 2012: How MongoDB Powers Doodle or Die
players


Primary   chains

          groups

          sessions
Support
            log
players
  game            Often


chainHistory

   stats         Query
               Frequency
  account
   login
                  Rarely
    info
players
        game               •   activeState
                           •   activeChain_id
     chainHistory          •   activeStepIndex
                           •   recentSkips
         stats
       account

Answerslogin    question:
“What is this player working on right now?”
                info
 db.players.findOne({_id: ‘58mwYlTKV’}, {game: 1});
players                             Chain1
            game                            • datePlayed
                                            • dateViewed
       chainHistory

              stats
           account                                    ChainN

      login
Answers the question:
“What has the player worked on?”
       info
db.players.findOne({_id: ‘58mwYlTKV’}, {chainHistory: 1});
players                            •       totalSteps
           game                               •       drawSteps
                                              •       phraseSteps
      chainHistory                            •       numSkips
                                              •       numLikes
             stats
          account
      login
Answers the question:
“What has the player worked on?”
       info
db.players.findOne({_id: ‘58mwYlTKV’}, {stats: 1});
chains
•   activePlayer_id
•   activeState
•   numSteps
•   lastModified
               steps
       ineligiblePlayer_ids
chains are searched (not as many questions)
db.chains.findOne({inUse: false,
            activeState : player.game.activeState,
            ineligiblePlayer_ids: {$ne: player._id},
            lastModified: { $gte: timeRange}});
chains           [player_id1
                            player_id2,
•   activePlayer_id         player_id3
•   activeState                   …
•   numSteps                player_idN]
•   lastModified

              *
    ineligiblePlayer_ids

Note: $addToSet and $pull work great in
          steps
maintaining this array
chains           [

•   activePlayer_id               Step1
•   activeState                • player_id
•   numSteps                   • state
•   lastModified               • date
                                 content
              *
    ineligiblePlayer_ids
                                  StepN
           steps
                           ]
Description Step Content:
  • phrase



Doodle Step Content:
  •   url (points to S3)
  •   time
  •   numStrokes
  •   numDistinctColors
How Does it all Work?
MongoDC 2012: How MongoDB Powers Doodle or Die
players queried for users who are associated
with the authenticated twitter account

db.players.find({‘login.twitter.uid’: ‘XXXXXXX’},
                      {game: 1, chainHistory: 1});



chain history used to load
thumbnails, complete active and previous
chain loaded

db.chains.find({_id: {$in: [player.game.activeChain_id,
                  player.game.lastChain_id]}});
MongoDC 2012: How MongoDB Powers Doodle or Die
Chains collection searched and updated for
unclaimed eligible chain to be given to player

 db.chains.update({inUse: false,
            activeState : player.game.activeState,
            ineligiblePlayer_ids: {$ne: player._id},
            lastModified: { $gte: timeRange}},
           {$set: {inUse: true,
                  activePlayer_id: player._id,
            $addToSet:
               {ineligiblePlayer_ids: player._id}});




 db.chains.find({activePlayer_id: player._id});
MongoDC 2012: How MongoDB Powers Doodle or Die
Content saved to chain

db.chains.update({_id: chain._id,
           activePlayer_id: player._id}},
          {$inc: {numSteps: 1},
           $set: inUse: false,
               lastModified: datePlayed}
           $unset: {activePlayer_id: 1},
           $push: {steps: {
                   player_id: player._id,
                   state: chain.activeState,
                   content: content,
                   date: datePlayed}}});
Player updated and given new state


db.players.update({_id: player._id,
            'game.activeChain_id': chain._id},
           {$inc: {‘stats.totalSteps’: 1},
            $set: {'game.activeState': nextState,
                                   'game.lastChain_id’: chain._id},
            $unset: {'game.activeChain_id': 1,
                  'game.activeStepIndex': 1}});




Active chain and chain history reloaded
MongoDC 2012: How MongoDB Powers Doodle or Die
Doodle strokes will be saved to S3
(but url to S3 and metadata saved to chain)
{"color":"#000000","size":15,"path":[307,66,308,66,308,68,308,69,308,70,308,71,308,7
2,308,73,306,76,305,79,305,81,302,83,302,84,302,85,302,86,301,87,300,89,300,90,300
,91,300,92,300,95,300,97,300,102,300,103,300,104,300,108,300,109,300,110,301,114,
303,116,304,116,305,119,305,121,307,124,309,127,310,130,310,131,313,132,314,133,
316,137,317,138,320,140,321,140,323,143,326,143,328,143,333,144,337,144,341,144,
343,144,347,143,352,143,354,141,357,140,358,140,359,139,362,138,363,138,365,137,
368,135,369,132,370,131,371,130,374,128,375,128,376,125,378,125,379,124,380,123,
381,123,382,120,385,119,386,118,388,115,391,112,394,109,394,107,394,106,397,104,
397,103,397,102,397,101,397,100,397,98,397,96,397,94,397,93,397,91,397,90,397,88,
397,87,397,86,397,83,395,80,395,79,395,77,394,74,393,72,393,70,393,67,392,65,391,6
3,389,62,388,59,386,57,383,54,383,52,381,49,379,48,378,47,376,46,375,44,374,43,372
,43,370,42,369,42,368,42,364,41,362,41,359,41,355,41,351,42,349,42,347,42,346,44,3
43,44,342,45,339,46,337,47,336,48,333,49,332,51,330,51,328,51,327,52,326,53,323,53
,322,53,321,54,320,55,317,55,316,56,314,58,309,59,306,61,306,62,305,62,304,63]}
MongoDC 2012: How MongoDB Powers Doodle or Die
Player’s chainHistory retrieved and
filtered/sorted on server; applicable chains
retrieved

db.players.find({‘login.urlSafe’: urlSafeUid},
          {chainHistory: 1});


db.chains.find({$in: chain_idArr});




Retrieved chains ordered based on pre-
determined sorting.
MongoDC 2012: How MongoDB Powers Doodle or Die
Some Additional Doodle or Die Tricks
Build assumptions into queries
Alice and Bob are in an awesome group

groups:
{
  _id: ‘8rOIwh2VD’,
  members: [
     {name: ‘Alice’, dateJoined: ‘2012-05-24’},
     {name: ‘Bob’, dateJoined: ‘2012-05-25’}
  ]
  banned: [
     {name: ‘Eve’, dateBanned: ‘2012-05-25’}
    ]
}




Eve tries to join despite being banned!
MongoDC 2012: How MongoDB Powers Doodle or Die
Bugs in our code fail to detect Eve’s trickery
and the update query is run!

groups.update(
  {_id: ‘8rOIwh2VD’},
  {$push:
    {members:
      {name: ‘Eve’,
       dateJoined: new Date()}
      },
  },
  function(err) {
          if (err) throw err;
          callback();
  });
groups:
{
  _id: ‘8rOIwh2VD’,
  members: [
     {name: ‘Alice’, dateJoined: ‘2012-05-24’},
     {name: ‘Bob’, dateJoined: ‘2012-05-25’},
     {name: ‘Eve’, dateJoined: ‘2012-05-26’}
  ]
  banned: [
     {name: ‘Eve’, dateRequested: ‘2012-05-25’}
    ]
}




Blast! She got through! How can we help
prevent this?
Bake in our assumptions!

groups.update(
  {_id: ‘8rOIwh2VD’,
   ‘members.name’: {$ne: ‘Eve’},
   ‘banned.name’: {$ne: ‘Eve’}},
  {$push:
    {members:
      {name: ‘Eve’,
       dateJoined: new Date()}
        },
  },
  function(err, updateCount) {
      if (err) throw err;
      if (updateCount !== 1) throw new Error(‘bugs!’);
      callback(updateCount === 1);
  });
Always Specify Fields
To assign a player a new chain, we only need
their _id and game information

                   players

                    game
                 chainHistory
                    stats
                   account
                    login
                     info
To load a player’s profile content, we just
need their history, stats, and profile info

                   players

                   game
                chainHistory
                    stats
                  account
                    login
                    info
We need to assign Bob a new chain, lets
figure out what state he needs next

players.find({name: ‘Bob’});




This will fetch and send back the ENTIRE
player object!
Lets specify that we only want our “game”
subdocument
players.find({name: ‘Bob’}, {fields: [‘game’]});




Hooray! Much less to send over the series
of tubes.
Some Pain Points
Less obvious database structure


mysql> DESC players;
+----------------+---------+------+-----+---------+-------+
| Field         | Type | Null | Key | Default | Extra |
+----------------+---------+------+-----+---------+-------+
| id          | int(11) | NO | PRI | NULL |             |
| activeStateId | int(11) | YES | | NULL |                  |
| activeStepId | int(11) | YES | | NULL |                   |
+----------------+---------+------+-----+---------+-------+
Data integrity up to application


Mysql> ALTER TABLE players
    ADD CONSTRAINT fk_players_chains
     FOREIGN KEY (activeChainId) REFERENCES chains(id);




/* Oh no! This is going to mess things up! */

players.update({_id: ‘c58D4’},
         {$set: {‘game.activeChain_id’: ‘bad_id’}});
No mature GUI query development tools
If we could start over would we still use MongoDB?
Absolutely!
• NoSQL in general is great for rapid prototyping

• Fantastic performance

• Intuitive language keeps it easy to run one-off
  queries

• Excellent (and now officially supported) Node
  driver
Questions?




@Zugwalt     @DoodleOrDie

More Related Content

PPTX
Mongo or Die: How MongoDB Powers Doodle or Die
PDF
Heroku Postgres Cloud Database Webinar
KEY
Thoughts on MongoDB Analytics
PPTX
Mythbusting: Understanding How We Measure the Performance of MongoDB
PPTX
Ops Jumpstart: MongoDB Administration 101
PPTX
Mongo db mug_2012-02-07
PDF
The Ring programming language version 1.3 book - Part 87 of 88
PDF
Optimizing Slow Queries with Indexes and Creativity
Mongo or Die: How MongoDB Powers Doodle or Die
Heroku Postgres Cloud Database Webinar
Thoughts on MongoDB Analytics
Mythbusting: Understanding How We Measure the Performance of MongoDB
Ops Jumpstart: MongoDB Administration 101
Mongo db mug_2012-02-07
The Ring programming language version 1.3 book - Part 87 of 88
Optimizing Slow Queries with Indexes and Creativity

What's hot (20)

PDF
MongoDB dessi-codemotion
PDF
The Testing Games: Mocking, yay!
PPTX
MySQL Rises with JSON Support
PDF
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
PDF
MongoDB .local Munich 2019: Aggregation Pipeline Power++: How MongoDB 4.2 Pip...
PDF
2018 PyCon Korea - Ring
PPTX
Internet of things
PDF
The Ring programming language version 1.2 book - Part 82 of 84
KEY
Springest Dev Lunch: MongoDB Introduction
PDF
The Ring programming language version 1.2 book - Part 83 of 84
PDF
MongoDB @ Frankfurt NoSql User Group
PDF
Jan Lehnardt Couch Db In A Real World Setting
PDF
DEF CON 23 - amit ashbel and maty siman - game of hacks
PPTX
Letgo Data Platform: A global overview
PDF
Easy Scaling with Open Source Data Structures, by Talip Ozturk
PDF
The Ring programming language version 1.2 book - Part 84 of 84
PDF
The Ring programming language version 1.5.1 book - Part 178 of 180
PDF
MongoDB .local Munich 2019: New Encryption Capabilities in MongoDB 4.2: A Dee...
PDF
MongoDB .local Houston 2019: Using Client Side Encryption in MongoDB 4.2
PDF
Hostingultraso nevada
MongoDB dessi-codemotion
The Testing Games: Mocking, yay!
MySQL Rises with JSON Support
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
MongoDB .local Munich 2019: Aggregation Pipeline Power++: How MongoDB 4.2 Pip...
2018 PyCon Korea - Ring
Internet of things
The Ring programming language version 1.2 book - Part 82 of 84
Springest Dev Lunch: MongoDB Introduction
The Ring programming language version 1.2 book - Part 83 of 84
MongoDB @ Frankfurt NoSql User Group
Jan Lehnardt Couch Db In A Real World Setting
DEF CON 23 - amit ashbel and maty siman - game of hacks
Letgo Data Platform: A global overview
Easy Scaling with Open Source Data Structures, by Talip Ozturk
The Ring programming language version 1.2 book - Part 84 of 84
The Ring programming language version 1.5.1 book - Part 178 of 180
MongoDB .local Munich 2019: New Encryption Capabilities in MongoDB 4.2: A Dee...
MongoDB .local Houston 2019: Using Client Side Encryption in MongoDB 4.2
Hostingultraso nevada
Ad

Similar to MongoDC 2012: How MongoDB Powers Doodle or Die (20)

PDF
Building a Social Network with MongoDB
KEY
MongoDB Best Practices in AWS
PPTX
MongoDB San Francisco 2013: Storing eBay's Media Metadata on MongoDB present...
PPTX
Storing eBay's Media Metadata on MongoDB, by Yuri Finkelstein, Architect, eBay
PPT
MongoDB Basic Concepts
PPTX
MongoSF 2011 - Using MongoDB for IGN's Social Platform
PDF
All Your Base
PPTX
Introduction to NoSQL
KEY
Building your first application w/mongoDB MongoSV2011
PPTX
GDC 2013 - Ditching the Server: Making Client-side Only Social Games
PDF
Rails Loves MongoDB
PDF
Ensuring High Availability for Real-time Analytics featuring Boxed Ice / Serv...
KEY
Mongo scaling
KEY
NOSQL101, Or: How I Learned To Stop Worrying And Love The Mongo!
PPTX
MongoDB Live Hacking
PDF
MongoDB Tokyo - Monitoring and Queueing
PDF
Game post
PDF
From mysql to MongoDB(MongoDB2011北京交流会)
PDF
MongoDB for Game Analytics
PPTX
Webinar: Building Your First Application with MongoDB
Building a Social Network with MongoDB
MongoDB Best Practices in AWS
MongoDB San Francisco 2013: Storing eBay's Media Metadata on MongoDB present...
Storing eBay's Media Metadata on MongoDB, by Yuri Finkelstein, Architect, eBay
MongoDB Basic Concepts
MongoSF 2011 - Using MongoDB for IGN's Social Platform
All Your Base
Introduction to NoSQL
Building your first application w/mongoDB MongoSV2011
GDC 2013 - Ditching the Server: Making Client-side Only Social Games
Rails Loves MongoDB
Ensuring High Availability for Real-time Analytics featuring Boxed Ice / Serv...
Mongo scaling
NOSQL101, Or: How I Learned To Stop Worrying And Love The Mongo!
MongoDB Live Hacking
MongoDB Tokyo - Monitoring and Queueing
Game post
From mysql to MongoDB(MongoDB2011北京交流会)
MongoDB for Game Analytics
Webinar: Building Your First Application with MongoDB
Ad

More from MongoDB (20)

PDF
MongoDB SoCal 2020: Migrate Anything* to MongoDB Atlas
PDF
MongoDB SoCal 2020: Go on a Data Safari with MongoDB Charts!
PDF
MongoDB SoCal 2020: Using MongoDB Services in Kubernetes: Any Platform, Devel...
PDF
MongoDB SoCal 2020: A Complete Methodology of Data Modeling for MongoDB
PDF
MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...
PDF
MongoDB SoCal 2020: Best Practices for Working with IoT and Time-series Data
PDF
MongoDB SoCal 2020: MongoDB Atlas Jump Start
PDF
MongoDB .local San Francisco 2020: Powering the new age data demands [Infosys]
PDF
MongoDB .local San Francisco 2020: Using Client Side Encryption in MongoDB 4.2
PDF
MongoDB .local San Francisco 2020: Using MongoDB Services in Kubernetes: any ...
PDF
MongoDB .local San Francisco 2020: Go on a Data Safari with MongoDB Charts!
PDF
MongoDB .local San Francisco 2020: From SQL to NoSQL -- Changing Your Mindset
PDF
MongoDB .local San Francisco 2020: MongoDB Atlas Jumpstart
PDF
MongoDB .local San Francisco 2020: Tips and Tricks++ for Querying and Indexin...
PDF
MongoDB .local San Francisco 2020: Aggregation Pipeline Power++
PDF
MongoDB .local San Francisco 2020: A Complete Methodology of Data Modeling fo...
PDF
MongoDB .local San Francisco 2020: MongoDB Atlas Data Lake Technical Deep Dive
PDF
MongoDB .local San Francisco 2020: Developing Alexa Skills with MongoDB & Golang
PDF
MongoDB .local Paris 2020: Realm : l'ingrédient secret pour de meilleures app...
PDF
MongoDB .local Paris 2020: Upply @MongoDB : Upply : Quand le Machine Learning...
MongoDB SoCal 2020: Migrate Anything* to MongoDB Atlas
MongoDB SoCal 2020: Go on a Data Safari with MongoDB Charts!
MongoDB SoCal 2020: Using MongoDB Services in Kubernetes: Any Platform, Devel...
MongoDB SoCal 2020: A Complete Methodology of Data Modeling for MongoDB
MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...
MongoDB SoCal 2020: Best Practices for Working with IoT and Time-series Data
MongoDB SoCal 2020: MongoDB Atlas Jump Start
MongoDB .local San Francisco 2020: Powering the new age data demands [Infosys]
MongoDB .local San Francisco 2020: Using Client Side Encryption in MongoDB 4.2
MongoDB .local San Francisco 2020: Using MongoDB Services in Kubernetes: any ...
MongoDB .local San Francisco 2020: Go on a Data Safari with MongoDB Charts!
MongoDB .local San Francisco 2020: From SQL to NoSQL -- Changing Your Mindset
MongoDB .local San Francisco 2020: MongoDB Atlas Jumpstart
MongoDB .local San Francisco 2020: Tips and Tricks++ for Querying and Indexin...
MongoDB .local San Francisco 2020: Aggregation Pipeline Power++
MongoDB .local San Francisco 2020: A Complete Methodology of Data Modeling fo...
MongoDB .local San Francisco 2020: MongoDB Atlas Data Lake Technical Deep Dive
MongoDB .local San Francisco 2020: Developing Alexa Skills with MongoDB & Golang
MongoDB .local Paris 2020: Realm : l'ingrédient secret pour de meilleures app...
MongoDB .local Paris 2020: Upply @MongoDB : Upply : Quand le Machine Learning...

Recently uploaded (20)

PDF
Download FL Studio Crack Latest version 2025
DOCX
Elisabeth de Pot, the Witch of Flanders .
PPTX
Kulipari: Army of Frogs Movie - OVFX Story Internship 2023
PPTX
AEN 302 - Brinjal, Bhendi, Tomato Pests - PPT 1 - Agri Junction.pptx.pptx
PPTX
Squares64 Quiz, A chessboard of questions, crafted with care by @mahi_anmol_ ...
PDF
WKA #29: "FALLING FOR CUPID" TRANSCRIPT.pdf
PDF
Lucky_MangA chapter 2. Story and Art by Enaji Studio
PDF
Hip Hop Culture – More Than Just Music & Style
PPTX
Safety_Pharmacology_Tier2_Edibbbbbbbbbbbbbbbable.pptx
PDF
Western Pop Music: From Classics to Chart-Toppers
PPTX
understanding the Human DNA components database design
PDF
WKA #29: "FALLING FOR CUPID" TRANSCRIPT.pdf
PDF
Module-3-Week005-to-Week006-PPT.pdf hahahgs
PDF
CityofHorror_v1.1.pdf manual en español i
PPTX
GILGIT BALTISTAN HISTORY ,ADMINISTRATIVE , CONSTITUTUINAL STATUS , GEOGRAPMY ...
PPTX
Introduction to NGO’s098765789709876.pptx
PDF
What is Rotoscoping Best Software for Rotoscoping in 2025.pdf
PDF
WKA? #29.5: "HELLO NURSE" TRANSCRIPT.pdf
PPTX
continuous_steps_relay.pptx. Another activity
PDF
Watch Eddington (2025) – A Town Torn in Two
Download FL Studio Crack Latest version 2025
Elisabeth de Pot, the Witch of Flanders .
Kulipari: Army of Frogs Movie - OVFX Story Internship 2023
AEN 302 - Brinjal, Bhendi, Tomato Pests - PPT 1 - Agri Junction.pptx.pptx
Squares64 Quiz, A chessboard of questions, crafted with care by @mahi_anmol_ ...
WKA #29: "FALLING FOR CUPID" TRANSCRIPT.pdf
Lucky_MangA chapter 2. Story and Art by Enaji Studio
Hip Hop Culture – More Than Just Music & Style
Safety_Pharmacology_Tier2_Edibbbbbbbbbbbbbbbable.pptx
Western Pop Music: From Classics to Chart-Toppers
understanding the Human DNA components database design
WKA #29: "FALLING FOR CUPID" TRANSCRIPT.pdf
Module-3-Week005-to-Week006-PPT.pdf hahahgs
CityofHorror_v1.1.pdf manual en español i
GILGIT BALTISTAN HISTORY ,ADMINISTRATIVE , CONSTITUTUINAL STATUS , GEOGRAPMY ...
Introduction to NGO’s098765789709876.pptx
What is Rotoscoping Best Software for Rotoscoping in 2025.pdf
WKA? #29.5: "HELLO NURSE" TRANSCRIPT.pdf
continuous_steps_relay.pptx. Another activity
Watch Eddington (2025) – A Town Torn in Two

MongoDC 2012: How MongoDB Powers Doodle or Die

  • 1. Mongo or Die! How MongoDB powers Doodle or Die Aaron Silverman (@Zugwalt)
  • 3. What is Doodle or Die?
  • 4. Telephone Phrase I'd like some beer! Phrase I'd like some deer Phrase I'd like some deer Phrase I'd like some deer
  • 5. Doodle or Die Doodle Phrase Shining Apple Doodle Phrase Eat your fruit or DIE!
  • 12. Started very, very small 4 Cores 128MB RAM Node Server MongoDB Server
  • 13. Got serious about our servers Small - 2GB 8 Cores MongoDB Server 256MB RAM Node Server
  • 14. Called in some Reinforcements Large - 5GB 12 Cores MongoDB Server 1GB RAM Node Server
  • 15. In the last 30 Days: • 2,500,000 page views • 100,000 uniques • 35,000 active player accounts • 2,000,000 new doodles and descriptions
  • 16. Data Information Mongo DB • Player Info • Chain Info (minus 3.5 GB data size doodles) 0.5 GB index size • Group Info ~10 queries/sec • Game State • Logs Amazon 170 GB data size • Doodles! • Static Content 8 GB in/month • Compressed 200 GB out/month Database Backups
  • 17. MongoDB - $65 / month Daily backups to S3: $16/mo
  • 18. Amazon S3 - $70 / month
  • 19. Node - $62/ month
  • 20. Total Cost To Host: $197/month MongoDB $65 Amazon S3 $70 Node $62 Total $197
  • 21. PAAS Provides Easy Upgrade Path
  • 23. Custom _id Partially random string generated using ShortId node module ObjectId ObjectId("4fd02d5d78315a502d15cdde") ObjectId("4fd02d5a78315a502d15cddd") ObjectId("4fd02d5878315a502d15cddc") ShortId "8rOIwh2VD" "1qyY61Lu1" "5GQnbx-1"
  • 24. • Shorter, less cumbersome in code / queries db.players.findOne({_id: ‘58mwYlTKV’}); db.chains.update({_id: ‘58mwYlTKV’}, {$set: activePlayer_id: ‘88ueYaL6V’}); • Randomness will be better for sharding and makes it harder to cheat http://doodleordie.com/c/5ONtvvSGH <span class="doodle" data-jsonp="http://doodles.s3.amazonaws.com/d2/Eh8- Po2R5/1Em5kj3LY.js">
  • 25. Question Oriented Subdocuments What chain is this player working on right now? What are this player’s stats? Which players are not eligible to be assigned this chain?
  • 26. Goal is for most “Questions” to be able to be answered in one query from one sub document db.players.findOne({_id: ‘58mwYlTKV’}, {‘game.recentSkips’: 1}); Related “Questions” will share common ancestors db.players.findOne({_id: ‘58mwYlTKV’}, {game: 1});
  • 27. Indexes are designed to make answering questions easy! What chain is this player working on right now? db.players.ensureIndex({‘game.activeChain_id’: 1}); What chains are recently awaiting a new doodle? db.chains.ensureIndex({inUse: -1, activeState: 1 lastModified" : -1});
  • 28. Doodle or Die Collections
  • 30. players Primary chains groups sessions Support log
  • 31. players game Often chainHistory stats Query Frequency account login Rarely info
  • 32. players game • activeState • activeChain_id chainHistory • activeStepIndex • recentSkips stats account Answerslogin question: “What is this player working on right now?” info db.players.findOne({_id: ‘58mwYlTKV’}, {game: 1});
  • 33. players Chain1 game • datePlayed • dateViewed chainHistory stats account ChainN login Answers the question: “What has the player worked on?” info db.players.findOne({_id: ‘58mwYlTKV’}, {chainHistory: 1});
  • 34. players • totalSteps game • drawSteps • phraseSteps chainHistory • numSkips • numLikes stats account login Answers the question: “What has the player worked on?” info db.players.findOne({_id: ‘58mwYlTKV’}, {stats: 1});
  • 35. chains • activePlayer_id • activeState • numSteps • lastModified steps ineligiblePlayer_ids chains are searched (not as many questions) db.chains.findOne({inUse: false, activeState : player.game.activeState, ineligiblePlayer_ids: {$ne: player._id}, lastModified: { $gte: timeRange}});
  • 36. chains [player_id1 player_id2, • activePlayer_id player_id3 • activeState … • numSteps player_idN] • lastModified * ineligiblePlayer_ids Note: $addToSet and $pull work great in steps maintaining this array
  • 37. chains [ • activePlayer_id Step1 • activeState • player_id • numSteps • state • lastModified • date content * ineligiblePlayer_ids StepN steps ]
  • 38. Description Step Content: • phrase Doodle Step Content: • url (points to S3) • time • numStrokes • numDistinctColors
  • 39. How Does it all Work?
  • 41. players queried for users who are associated with the authenticated twitter account db.players.find({‘login.twitter.uid’: ‘XXXXXXX’}, {game: 1, chainHistory: 1}); chain history used to load thumbnails, complete active and previous chain loaded db.chains.find({_id: {$in: [player.game.activeChain_id, player.game.lastChain_id]}});
  • 43. Chains collection searched and updated for unclaimed eligible chain to be given to player db.chains.update({inUse: false, activeState : player.game.activeState, ineligiblePlayer_ids: {$ne: player._id}, lastModified: { $gte: timeRange}}, {$set: {inUse: true, activePlayer_id: player._id, $addToSet: {ineligiblePlayer_ids: player._id}}); db.chains.find({activePlayer_id: player._id});
  • 45. Content saved to chain db.chains.update({_id: chain._id, activePlayer_id: player._id}}, {$inc: {numSteps: 1}, $set: inUse: false, lastModified: datePlayed} $unset: {activePlayer_id: 1}, $push: {steps: { player_id: player._id, state: chain.activeState, content: content, date: datePlayed}}});
  • 46. Player updated and given new state db.players.update({_id: player._id, 'game.activeChain_id': chain._id}, {$inc: {‘stats.totalSteps’: 1}, $set: {'game.activeState': nextState, 'game.lastChain_id’: chain._id}, $unset: {'game.activeChain_id': 1, 'game.activeStepIndex': 1}}); Active chain and chain history reloaded
  • 48. Doodle strokes will be saved to S3 (but url to S3 and metadata saved to chain) {"color":"#000000","size":15,"path":[307,66,308,66,308,68,308,69,308,70,308,71,308,7 2,308,73,306,76,305,79,305,81,302,83,302,84,302,85,302,86,301,87,300,89,300,90,300 ,91,300,92,300,95,300,97,300,102,300,103,300,104,300,108,300,109,300,110,301,114, 303,116,304,116,305,119,305,121,307,124,309,127,310,130,310,131,313,132,314,133, 316,137,317,138,320,140,321,140,323,143,326,143,328,143,333,144,337,144,341,144, 343,144,347,143,352,143,354,141,357,140,358,140,359,139,362,138,363,138,365,137, 368,135,369,132,370,131,371,130,374,128,375,128,376,125,378,125,379,124,380,123, 381,123,382,120,385,119,386,118,388,115,391,112,394,109,394,107,394,106,397,104, 397,103,397,102,397,101,397,100,397,98,397,96,397,94,397,93,397,91,397,90,397,88, 397,87,397,86,397,83,395,80,395,79,395,77,394,74,393,72,393,70,393,67,392,65,391,6 3,389,62,388,59,386,57,383,54,383,52,381,49,379,48,378,47,376,46,375,44,374,43,372 ,43,370,42,369,42,368,42,364,41,362,41,359,41,355,41,351,42,349,42,347,42,346,44,3 43,44,342,45,339,46,337,47,336,48,333,49,332,51,330,51,328,51,327,52,326,53,323,53 ,322,53,321,54,320,55,317,55,316,56,314,58,309,59,306,61,306,62,305,62,304,63]}
  • 50. Player’s chainHistory retrieved and filtered/sorted on server; applicable chains retrieved db.players.find({‘login.urlSafe’: urlSafeUid}, {chainHistory: 1}); db.chains.find({$in: chain_idArr}); Retrieved chains ordered based on pre- determined sorting.
  • 52. Some Additional Doodle or Die Tricks
  • 54. Alice and Bob are in an awesome group groups: { _id: ‘8rOIwh2VD’, members: [ {name: ‘Alice’, dateJoined: ‘2012-05-24’}, {name: ‘Bob’, dateJoined: ‘2012-05-25’} ] banned: [ {name: ‘Eve’, dateBanned: ‘2012-05-25’} ] } Eve tries to join despite being banned!
  • 56. Bugs in our code fail to detect Eve’s trickery and the update query is run! groups.update( {_id: ‘8rOIwh2VD’}, {$push: {members: {name: ‘Eve’, dateJoined: new Date()} }, }, function(err) { if (err) throw err; callback(); });
  • 57. groups: { _id: ‘8rOIwh2VD’, members: [ {name: ‘Alice’, dateJoined: ‘2012-05-24’}, {name: ‘Bob’, dateJoined: ‘2012-05-25’}, {name: ‘Eve’, dateJoined: ‘2012-05-26’} ] banned: [ {name: ‘Eve’, dateRequested: ‘2012-05-25’} ] } Blast! She got through! How can we help prevent this?
  • 58. Bake in our assumptions! groups.update( {_id: ‘8rOIwh2VD’, ‘members.name’: {$ne: ‘Eve’}, ‘banned.name’: {$ne: ‘Eve’}}, {$push: {members: {name: ‘Eve’, dateJoined: new Date()} }, }, function(err, updateCount) { if (err) throw err; if (updateCount !== 1) throw new Error(‘bugs!’); callback(updateCount === 1); });
  • 60. To assign a player a new chain, we only need their _id and game information players game chainHistory stats account login info
  • 61. To load a player’s profile content, we just need their history, stats, and profile info players game chainHistory stats account login info
  • 62. We need to assign Bob a new chain, lets figure out what state he needs next players.find({name: ‘Bob’}); This will fetch and send back the ENTIRE player object!
  • 63. Lets specify that we only want our “game” subdocument players.find({name: ‘Bob’}, {fields: [‘game’]}); Hooray! Much less to send over the series of tubes.
  • 65. Less obvious database structure mysql> DESC players; +----------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------+---------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | activeStateId | int(11) | YES | | NULL | | | activeStepId | int(11) | YES | | NULL | | +----------------+---------+------+-----+---------+-------+
  • 66. Data integrity up to application Mysql> ALTER TABLE players ADD CONSTRAINT fk_players_chains FOREIGN KEY (activeChainId) REFERENCES chains(id); /* Oh no! This is going to mess things up! */ players.update({_id: ‘c58D4’}, {$set: {‘game.activeChain_id’: ‘bad_id’}});
  • 67. No mature GUI query development tools
  • 68. If we could start over would we still use MongoDB?
  • 69. Absolutely! • NoSQL in general is great for rapid prototyping • Fantastic performance • Intuitive language keeps it easy to run one-off queries • Excellent (and now officially supported) Node driver
  • 70. Questions? @Zugwalt @DoodleOrDie

Editor's Notes

  • #29: Disclaimer: schema was originally created during Node Knockout Weekend Hackathon