Building Real Time Systems on MongoDB Using the Oplog at Stripe

86
MongoDB and the Oplog EVAN BRODER @ebroder

description

 

Transcript of Building Real Time Systems on MongoDB Using the Oplog at Stripe

Page 1: Building Real Time Systems on MongoDB Using the Oplog at Stripe

MongoDB and the OplogEVAN BRODER @ebroder

Page 2: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 3: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 4: Building Real Time Systems on MongoDB Using the Oplog at Stripe

MongoDB and the OplogEVAN BRODER @ebroder

Page 5: Building Real Time Systems on MongoDB Using the Oplog at Stripe

AGENDAINTRO TO THE OPLOGEXAMPLE APPLICATIONS

Page 6: Building Real Time Systems on MongoDB Using the Oplog at Stripe

INTROTO THE OPLOG

Page 7: Building Real Time Systems on MongoDB Using the Oplog at Stripe

PRIMARY

SECONDARIES

APPLICATION

Page 8: Building Real Time Systems on MongoDB Using the Oplog at Stripe

APPLICATION

save{_id: 1, a: 2}

Page 9: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

- save {_id: 1, a: 2}

Page 10: Building Real Time Systems on MongoDB Using the Oplog at Stripe

APPLICATION

update where{a: 2},{$set: {a: 3}}

Page 11: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

- save {_id: 1, a: 2}- update {_id: 1}, {$set: {a: 3}}

Page 12: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

- save {_id: 1, a: 2}- update {_id: 1}, {$set: {a: 3}}- insert…- delete…- delete…- save…- update…

Page 13: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

save…

THINGS I’VE DONE:

Page 14: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

save…

THINGS I’VE DONE:

save…

Page 15: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 16: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 17: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 18: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 19: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 20: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 21: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 22: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 23: Building Real Time Systems on MongoDB Using the Oplog at Stripe

TRIGGERS

Page 24: Building Real Time Systems on MongoDB Using the Oplog at Stripe

GOAL:EVENT PROCESSING

Page 25: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 26: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 27: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 28: Building Real Time Systems on MongoDB Using the Oplog at Stripe

GOAL:DETECT INSERTIONS

Page 29: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 30: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog = mongo_connection['local']['oplog.rs']

ns = 'eventdb.events'

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.each do |op|

puts op['o']['_id']

end

end

Page 31: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog = mongo_connection['local']['oplog.rs']

ns = 'eventdb.events'

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.each do |op|

puts op['o']['_id']

end

end

Page 32: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog = mongo_connection['local']['oplog.rs']

ns = 'eventdb.events'

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.each do |op|

puts op['o']['_id']

end

end

Page 33: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog = mongo_connection['local']['oplog.rs']

ns = 'eventdb.events'

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.each do |op|

puts op['o']['_id']

end

end

Page 34: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 35: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 36: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 37: Building Real Time Systems on MongoDB Using the Oplog at Stripe

oplog.find({'op' => 'i', 'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 38: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 39: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 40: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 41: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 42: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 43: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 44: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start},

'op' => 'i',

'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 45: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start},

'op' => 'i',

'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY)

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 46: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start},

'op' => 'i',

'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY)

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 47: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start},

'op' => 'i',

'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY)

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 48: Building Real Time Systems on MongoDB Using the Oplog at Stripe

DATA TRANSFORMATIONS

Page 49: Building Real Time Systems on MongoDB Using the Oplog at Stripe

GOAL:MONGODB TO POSTGRESQL

Page 50: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start},

'op' => 'i',

'ns' => ns}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY)

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 51: Building Real Time Systems on MongoDB Using the Oplog at Stripe

start_entry = oplog.find_one({},

{:sort => {'$natural' => -1}})

start = start_entry['ts']

oplog.find({'ts' => {'$gt' => start}}) do |cursor|

cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY)

cursor.add_option(Mongo::Constants::OP_QUERY_TAILABLE)

cursor.add_option(Mongo::Constants::OP_QUERY_AWAIT_DATA)

loop do

cursor.each do |op|

puts op['o']['_id']

end

end

end

Page 52: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

puts op['o']['_id']

end

Page 53: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

case op['op']

when 'i'

puts op['o']['_id']

else

# ¯\_(ツ)_/¯

end

end

Page 54: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

case op['op']

when 'i'

query = "INSERT INTO #{op['ns']} (" +

op['o'].keys.join(', ') +

') VALUES (' +

op['o'].values.map(&:inspect).join(', ')

else

# ¯\_(ツ)_/¯

end

end

Page 55: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 56: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 57: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 58: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

case op['op']

when 'i'

query = "INSERT INTO #{op['ns']} (" +

op['o'].keys.join(', ') +

') VALUES (' +

op['o'].values.map(&:inspect).join(', ')

when 'd'

query = "DELETE FROM #{op['ns']} WHERE _id=" +

op['o']['_id'].inspect

else

# ¯\_(ツ)_/¯

end

end

Page 59: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 60: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 61: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 62: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 63: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 64: Building Real Time Systems on MongoDB Using the Oplog at Stripe

query = "DELETE FROM #{op['ns']} WHERE _id=" +

op['o']['_id'].inspect

when 'u'

query = "UPDATE #{op['ns']} SET"

updates = op['o']['$set'] ? op['o']['$set'] : op['o']

updates.each do |k, v|

query += " #{k}=#{v.inspect}"

end

query += " WHERE _id="

query += op['o2']['_id'].inspect

else

# ¯\_(ツ)_/¯

end

end

Page 65: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

case op['op']

when 'i'

query = "INSERT INTO #{op['ns']} (" +

op['o'].keys.join(', ') + ') VALUES (' +

op['o'].values.map(&:inspect).join(', ')

when 'd'

query = "DELETE FROM #{op['ns']} WHERE _id=" +

op['o']['_id'].inspect

when 'u'

query = "UPDATE #{op['ns']} SET"

updates = op['o']['$set'] ? op['o']['$set'] : op['o']

updates.each do |k, v|

query += " #{k}=#{v.inspect}"

end

query += " WHERE _id=" + op['o2']['_id'].inspect

else

# ¯\_(ツ)_/¯

end

end

Page 66: Building Real Time Systems on MongoDB Using the Oplog at Stripe

github.com/stripe/mosql

Page 67: Building Real Time Systems on MongoDB Using the Oplog at Stripe

github.com/stripe/zerowing

Page 68: Building Real Time Systems on MongoDB Using the Oplog at Stripe

cursor.each do |op|

case op['op']

when 'i'

query = "INSERT INTO #{op['ns']} (" +

op['o'].keys.join(', ') + ') VALUES (' +

op['o'].values.map(&:inspect).join(', ')

when 'd'

query = "DELETE FROM #{op['ns']} WHERE _id=" +

op['o']['_id'].inspect

when 'u'

query = "UPDATE #{op['ns']} SET"

updates = op['o']['$set'] ? op['o']['$set'] : op['o']

updates.each do |k, v|

query += " #{k}=#{v.inspect}"

end

query += " WHERE _id=" + op['o2']['_id'].inspect

else

# ¯\_(ツ)_/¯

end

end

Page 69: Building Real Time Systems on MongoDB Using the Oplog at Stripe

DISASTER RECOVERY

Page 70: Building Real Time Systems on MongoDB Using the Oplog at Stripe

task = collection.find_one({'finished' => nil}

# do something with task…

collection.update({'_id' => task.id},

{'$set' => {'finished' => Time.now.to_i}})

Page 71: Building Real Time Systems on MongoDB Using the Oplog at Stripe

loop do

collection.remove(

{'finished' => {'$lt' => Time.now.to_i - 30}})

sleep(10)

end

Page 72: Building Real Time Systems on MongoDB Using the Oplog at Stripe

evan@caron:~$ mongo

MongoDB shell version: 2.4.10

connecting to: test

normal:PRIMARY> null < (Date.now() / 1000) - 30

true

Page 73: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 74: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 75: Building Real Time Systems on MongoDB Using the Oplog at Stripe

> db.getReplicationInfo()

{

"logSizeMB" : 48964.3541015625,

"usedMB" : 46116.4,

"timeDiff" : 316550,

"timeDiffHours" : 87.93,

"tFirst" : "Thu Apr 11 2013 07:24:29 GMT+0000 (UTC)",

"tLast" : "Sun Apr 14 2013 23:20:19 GMT+0000 (UTC)",

"now" : "Sat May 24 2014 07:52:35 GMT+0000 (UTC)"

}

Page 76: Building Real Time Systems on MongoDB Using the Oplog at Stripe

> db.getReplicationInfo()

{

"logSizeMB" : 48964.3541015625,

"usedMB" : 46116.4,

"timeDiff" : 316550,

"timeDiffHours" : 87.93,

"tFirst" : "Thu Apr 11 2013 07:24:29 GMT+0000 (UTC)",

"tLast" : "Sun Apr 14 2013 23:20:19 GMT+0000 (UTC)",

"now" : "Sat May 24 2014 07:52:35 GMT+0000 (UTC)"

}

Page 77: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

insertdelete…

THINGS I’VE DONE:

Page 78: Building Real Time Systems on MongoDB Using the Oplog at Stripe

new_oplog.find({'ts' => {'$gt' => start}}) do |cursor| cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY) cursor.each do |op| if op['op'] == 'd' && op['ns'] == 'monsterdb.tasks' old_task = old_tasks.find_one({'_id' => op['o']['_id']}) if old_task['finished'] == nil # found one! # save old_task to a file, and we'll re-queue it later end end

old_connection['admin'].command({'applyOps' => [op]}) endend

Page 79: Building Real Time Systems on MongoDB Using the Oplog at Stripe

new_oplog.find({'ts' => {'$gt' => start}}) do |cursor| cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY) cursor.each do |op| if op['op'] == 'd' && op['ns'] == 'monsterdb.tasks' old_task = old_tasks.find_one({'_id' => op['o']['_id']}) if old_task['finished'] == false # found one! # save old_task to a file, and we'll re-queue it later end end

old_connection['admin'].command({'applyOps' => [op]}) endend

Page 80: Building Real Time Systems on MongoDB Using the Oplog at Stripe

new_oplog.find({'ts' => {'$gt' => start}}) do |cursor| cursor.add_option(Mongo::Constants::OP_QUERY_OPLOG_REPLAY) cursor.each do |op| if op['op'] == 'd' && op['ns'] == 'monsterdb.tasks' old_task = old_tasks.find_one({'_id' => op['o']['_id']}) if old_task['finished'] == false # found one! # save old_task to a file, and we'll re-queue it later end end

old_connection['admin'].command({'applyOps' => [op]}) endend

Page 81: Building Real Time Systems on MongoDB Using the Oplog at Stripe

THINGS I’VE DONE:

save…

THINGS I’VE DONE:

save…

Page 82: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 83: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 84: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 85: Building Real Time Systems on MongoDB Using the Oplog at Stripe
Page 86: Building Real Time Systems on MongoDB Using the Oplog at Stripe

QUESTIONS?