the current state of asynchronous processing with ruby

58
The Current State of Asynchronous Processing in Ruby Mathias Meyer, Peritor GmbH

Upload: mattmatt

Post on 13-May-2015

18.028 views

Category:

Technology


2 download

DESCRIPTION

A small overview of current technologies

TRANSCRIPT

Page 1: The Current State of Asynchronous Processing With Ruby

The Current State of Asynchronous

Processing in Ruby

Mathias Meyer, Peritor GmbH

Page 2: The Current State of Asynchronous Processing With Ruby

self

• Software-Developer for Peritor GmbH in Berlin

• Code-Reviews, Scaling, Performance-Reviews and -Tuning, Refactorings

• Maintainer: acts_as_solr, run_later, heaps of stuff for Capistrano/Webistrano

Page 3: The Current State of Asynchronous Processing With Ruby

What?

• Asynchronous Processing

• Just a fancy word for...

Page 4: The Current State of Asynchronous Processing With Ruby

“Dude, this request is getting too long!”

Page 5: The Current State of Asynchronous Processing With Ruby

“Okay, let’s move stuff into the background.”

Page 6: The Current State of Asynchronous Processing With Ruby

In other words: What?

• A process puts a job into some queue

• Another process picks up the job and processes it

Page 7: The Current State of Asynchronous Processing With Ruby

Why?

• Requests are taking too long

• Image uploads to S3

• Full-text search updates

• Generating PDFs, reports, etc.

• Sending emails (newsletters, etc.)

Page 8: The Current State of Asynchronous Processing With Ruby

Why?

• Your app requires longer running tasks

• Compute daily statistics data

• Create reports

• Data crunching

Page 9: The Current State of Asynchronous Processing With Ruby

Why?

• You need to run tasks at a certain time

• Scheduled tasks like invoicing, billing

• Sending out reminder emails

Page 10: The Current State of Asynchronous Processing With Ruby

Why?

• Longer tasks

• Are harder to debug and monitor

• Block user and the application

Page 11: The Current State of Asynchronous Processing With Ruby

Example

Rails

Client

Upload to S3

Response

!

Page 12: The Current State of Asynchronous Processing With Ruby

Example - Much Better

RailsClient

Upload to S3

Response

Worker

Page 13: The Current State of Asynchronous Processing With Ruby

The simplest Thing that could possibly work

Thread.newdoAccountMailer.deliver_signup(@user)end

Page 14: The Current State of Asynchronous Processing With Ruby

Done.

Page 15: The Current State of Asynchronous Processing With Ruby

Not so fast...

• Problems

• Not easy on resources

• Unreliable

• Not reproducible

Page 16: The Current State of Asynchronous Processing With Ruby

A little better...

run_laterdoAccountMailer.deliver_signup(@user)end

Page 17: The Current State of Asynchronous Processing With Ruby

run_later

• Borrowed from Merb, available as a Rails-Plugin

• Uses worker thread and a queue

• Simple solution for simple tasks

Page 18: The Current State of Asynchronous Processing With Ruby

What you really want

• Reliable messaging

• Durability

• Scheduling

• Scalable processing

• Not necessarily all at the same time

Page 19: The Current State of Asynchronous Processing With Ruby

Options, options

• Messaging Queues

• Polling

• Schedulers

• Oh my!

Page 20: The Current State of Asynchronous Processing With Ruby

Protocols, oh my!

• AMQP

• STOMP

• JMS

• XMPP

• RestMS

Page 21: The Current State of Asynchronous Processing With Ruby

Message Queues

Page 22: The Current State of Asynchronous Processing With Ruby

Message Queues

• Publish/subscribe mechanism

• Usually require more than one new component in your infrastructure

• Broker middleware

• Subscribers, message listeners

Page 23: The Current State of Asynchronous Processing With Ruby

Message Queues

• ActiveMQ

• RabbitMQ

• ActiveMessaging

• Amazon SQS

• Usually more code involved

Page 24: The Current State of Asynchronous Processing With Ruby

Pollers

• Polling a database for new jobs

• Simple and domain-specific

• Only one new component

• Usually requires more effort to make them less prone to errors

Page 25: The Current State of Asynchronous Processing With Ruby

Pollers

• Roll Your Own w/ or w/o daemons gem

• delayed_job - adds some infrastructure

• background_job

Page 26: The Current State of Asynchronous Processing With Ruby

Pollers - RYO

create_table:jobsdo|t|t.string:klazz,:methodt.string:obj_idend

Page 27: The Current State of Asynchronous Processing With Ruby

Pollers - RYOclassJob<ActiveRecord::Basedefself.schedule(obj,method)create(:klazz=>obj.class.name,:obj_id=>obj.id,:method=>method.to_s)enddefrunobj=klazz.constantize.find(obj_id)obj.send(self[:method])endend

Job.schedule(@user,:notify_signup)

Page 28: The Current State of Asynchronous Processing With Ruby

Pollers - RYO

task:pollerdoJobPoller.new.runend

classJobPollerdefrunloopdojob=Job.find(:first,:lock=>true)nextunlessjobjob.runjob.deleteendendend

Page 29: The Current State of Asynchronous Processing With Ruby

Pollers - delayed_job

User.find(params[:id]).send_later(:notify_signup)

rakejobs:work

Page 30: The Current State of Asynchronous Processing With Ruby

Pollers - delayed_job

classSignupNotifier<Struct.new(:name)defperformuser=User.find_by_name(name)user.notify_signupendend

Delayed::Job.enqueueSignupNotifier.new("david")

Page 31: The Current State of Asynchronous Processing With Ruby

Message Queues, Pollers

• Usually don’t directly support scheduling

Page 32: The Current State of Asynchronous Processing With Ruby

Schedulers

Page 33: The Current State of Asynchronous Processing With Ruby

Schedulers

• cron, cron, cron (oh, and rake)

• rufus-scheduler

• BackgrounDRb

• Quartz with JRuby (if you’re into that sort of thing)

• Roll Your Own

Page 34: The Current State of Asynchronous Processing With Ruby

rufus-schedulerscheduler=Rufus::Scheduler.start_new

scheduler.cron'022**1‐5'doJob.new.runend

scheduler.in'20m'doputs"Getaflatwhite"end

Page 35: The Current State of Asynchronous Processing With Ruby

Playing with the Big Guys

• Nanite - Jack of all trades

• Uses RabbitMQ, Erlang-based messaging server, AMQP implementation

• Distributes work across a network of workers

Page 36: The Current State of Asynchronous Processing With Ruby

Nanite

• A self-assembling fabric of Ruby daemons

• Uses mappers and agents

• Mappers route message requests

• Agents handle messages

Page 37: The Current State of Asynchronous Processing With Ruby

Nanite

Agent

Agent

Agent

Agent

RabbitMQ

Mapper

Mapper

Page 38: The Current State of Asynchronous Processing With Ruby

Nanite

classReactorincludeNanite::Actorexpose:reactdefreact(payload)"reactingtomessagewithpayload:#{payload}"endend

Page 39: The Current State of Asynchronous Processing With Ruby

Nanite

Nanite.request('/reactor/react','goodactingisreacting')do|r|prend

Page 40: The Current State of Asynchronous Processing With Ruby

Nanite

• Agents register themselves, constantly pinging the mappers

• Mappers remove timed-out agents

• Work distributed based on agent load

• Unfortunately: Poor documentation

Page 41: The Current State of Asynchronous Processing With Ruby

Other Stuff• BackgrounDRb

• AP4R

• job_fu

• spawn

• Starling/Workling

• Daemons

• beanstalkd

Page 42: The Current State of Asynchronous Processing With Ruby

Careful now! Pitfalls

Page 43: The Current State of Asynchronous Processing With Ruby

Race Conditions

Page 44: The Current State of Asynchronous Processing With Ruby
Page 45: The Current State of Asynchronous Processing With Ruby

Race Conditions

• Workers try accessing the same data

• Workers update data that is updated heavily from other layers

Page 46: The Current State of Asynchronous Processing With Ruby

Race Conditions

• Make jobs repeatable

• Reduce proneness to errors coming from the database

• Let your broker take care of that

Page 47: The Current State of Asynchronous Processing With Ruby

Queue Congestion

Page 48: The Current State of Asynchronous Processing With Ruby

Queue Congestion

http://www.flickr.com/photos/lasgalletas/263909727/

Page 49: The Current State of Asynchronous Processing With Ruby

Queue Congestion

• Workers can’t keep up with queue

• More work coming in than being processed

Page 50: The Current State of Asynchronous Processing With Ruby

Queue Congestion

• Make it easy to add more workers

• Ensure they don’t steal each other’s work or do it twice (row-level locking, lock on fields)

Page 51: The Current State of Asynchronous Processing With Ruby

Stalled Workers

Page 53: The Current State of Asynchronous Processing With Ruby

Stalled Workers

• Workers stopped processing jobs

• Got stuck on an a particular task

• Choked on an error

Page 54: The Current State of Asynchronous Processing With Ruby

Stalled Workers

• Monitor your workers and queues

• Build in error reporting, report exceptions like in the rest of your code

• Make jobs repeatable

• Use Nanite (self-healing)

Page 55: The Current State of Asynchronous Processing With Ruby

Recommendations

• Simple jobs

• Roll Your Own

• delayed_job

Page 56: The Current State of Asynchronous Processing With Ruby

Recommendations

• Distributed

• Amazon SQS w/ ActiveMessaging or custom worker

• Works best on EC2

Page 57: The Current State of Asynchronous Processing With Ruby

Recommendations

• Heavy load, scalable

• Nanite/RabbitMQ

Page 58: The Current State of Asynchronous Processing With Ruby

The End

• Questions?

• @roidrage

• http://www.paperplanes.de

• http://github.com/mattmatt