Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MongoNYC 2012: E-Commerce at UnderArmour, Inc.

mongodb
May 29, 2012
2.3k

MongoNYC 2012: E-Commerce at UnderArmour, Inc.

MongoNYC 2012: E-Commerce at UnderArmour, Inc, Jesse Dailey, Twin Technologies. Using MongoDB at UnderArmour, in 9 months from concept to launch, we built an e-commerce platform that within weeks of launch was handling millions of dollars a day in sales; replacing a huge portion of a large MSSQL+J2EE stack, in what is considered a "home run" by the technical team, the content teams, and management. Here's how we did it using an innovative CMS we built for MongoDB.

mongodb

May 29, 2012
Tweet

Transcript

  1. The Caveat This presentation discusses a closed-source application owned by

    a publicly-traded company, so code snippets are pseudo-code and all dates, statistics, and numbers are only my best personal estimates.
  2. Goals: for 2011 1. Act quicker, and sell more. ◦

    Get developers out of the way. 2. Empower authors to control Publishing. ◦ Preview ◦ Scheduling ◦ Rollback 3. Test any thing. ◦ Deeply integrated AB Testing.
  3. The Plan • Divide the existing application into three sections:

    ◦ the "runtime", ◦ "author time", ◦ and the "service" layer. • What we are showing today ◦ The new runtime, a CMS designed for MongoDB. ◦ The new author time UI, done in Flex.
  4. Batches Publishing: • Preview • Scheduled Activation • Rollback Every

    piece of content is associated with an integer batch number, and batches are scheduled to begin and end at certain times.
  5. Batches: how-to Documents in Mongo that are batch-aware have a

    "batch" attribute, a single integer. To find the document with the highest active version: var doc = collection("pages").find({ ... real criteria first ..., $in: { batch: timeslice } }) .sort({ batch: -1 }) .limit(1); There is a more elegant way to use the timeslice that we will save for later, if someone asks about it in Q&A.
  6. Content A tree of objects, stored in BSON documents. Every

    node in the tree has a "type" attribute. var page = { type: "page", body: "Hello World" }; Each "type" maps to some handler code. reducer["page"] = function(node) { return [ "<html><head>",node.head, "</head><body>",node.body, "</body></html>" ]; } Two-phase rendering.
  7. reduce: the first phase. A central recursive function, • evaluates

    the handler for each node • output accumulates output as a (larger, but simpler) tree. This is why the previous example was: return [ "string", object, "string" ]; instead of return "string" + object + "string"; At the end of reduction, it is still "turtles all the way down".
  8. finalize: the second phase. Results in a single string. A

    second recursive function, • joins simple elements as strings. • invokes finalize on nodes that survived reduction. Next, an example of a node that survives to be finalized, • { type: "region" }
  9. Regions: out-of-order rendering. A region lets you: • label a

    position in the output. • each label gets a content buffer. • during reduction, other nodes contribute to these buffers. • during finalization, the buffer is joined. { type: page, head: ["<style>",{ type:region, name:"css" },"</style>"] body: [ "<div class='demo'>Hello World</div>", { type: region, action: "add", name: "css", content: ".demo { color: red; }" } ] }
  10. Dependencies: scripts {type: "script", src: "my-jquery-plugin.js", depends: "jquery" } {type:

    "script", src: "jquery-1.6.min.js", provides: "jquery" } • reduce builds a dependency tree. • finalize renders tree to $script (into the "js" region).
  11. Dependencies: application logic { type: "anything", require: "tax-calculated" } Here,

    "tax-calculated" is the name of a Processor. A Processor defines two methods: • pre() • post() Each method is called once in any request where it is required.
  12. AB Testing • The code "homepage-test" gets sent to the

    mPath Learning API from Conductrics. { type: "abtest", code: "homepage-test", items: { A: { ... } B: { ... } } }
  13. Programming { type: "data", key: "FOO" } { type: "each",

    of: [ ... ] as: { ... } } { type: "with", person: { ... } as: [ "Hello ", { type: "data", key: "PERSON.NAME"} ] } { type: "if-data" } { type: "case-data" }, etc...
  14. Caching { type: "cached", key: { type: "list-join", items: [

    {type:"data", key:"PARENTKEY"}, "my-key-component" ] }, content: { ... } }
  15. Event Queue w/ Blind Locking Goal: Cluster-wide publish / subscribe

    event queue. Strategy: • Capped Collection ◦ maintains insertion order cheaply. ◦ can't delete, just mark. • Readers of the Queue must use "Blind Locking"
  16. Blind Locking 1. Each document in the capped collection gets:

    ◦ { lock: 'unlocked' } 2. Each Reader is assigned/generated an 8-digit token: ◦ 'reader01' 3. To pop an event off the Queue safely under load: ◦ Update the first 'unlocked' lock to 'reader01'. ◦ Find the first document with 'reader01'.
  17. Reading from an Event Queue. 1: db.event.queue.update( 2: { lock:

    'unlocked' }, 3: { lock: 'reader01' }); 4: 5: doc = db.event.queue.findOne( 6: { lock: 'reader01' }); 7: 8: if( doc != null ) { 9: doc.status = invokeEventHandlers(doc); 10: doc.lock = 'finished'; 11: db.event.queue.save(doc); 12: }
  18. Acknowledgements (aka Wall of Email) UnderArmour, Inc - Dev Team

    Brian Massey <[email protected]> Brian "Magic" Johnston <[email protected]> Jeff "Switzerland" Switzer <[email protected]> Ken Valencik, Ron Wilson, and others. Twin Technologies, LLC Ben Elmore, CEO <[email protected]> Abraham Lloyd, Solutions Exec. <[email protected]> Conductrics, LLC Nate Weiss, CTO <[email protected]> Jesse Dailey <[email protected]>
  19. Thank You Three Talks that inspire (and aren't on TED):

    1. Bret Victor, "Inventing on Principle": ◦ youtu.be/PUv66718DII 2. Jaron Lanier, "Learning by Experience & Play": ◦ youtu.be/F9eFZpdSeRU 3. Alan Kay, "Normal considered Harmful": ◦ youtu.be/FvmTSpJU-Xc