Wouldn't you like to forget about the database?

Community

Tenderlove being awesome

Shoehorn

ID Name Birthdate Email
1 Jesse Cooke 9/28/1981 [email protected]
2 Jon Doe 6/23/1972 [email protected]

Objects!


Widget.find_by_name("foo")
# or
User.create({ email: "[email protected]" })
  

Some Query Language :(


select * from widgets where name = 'foo';
# or
insert into users...
  

Object-relational impedance mismatch

Wikipedia

Agile Data

Cunningham & Cunningham

Coding Horror

PORO

Plain Old Ruby Object


class Post < ActiveRecord::Base

end
  

The Image

plasma lamp

Time traveller

The future of Ruby is here, it’s just that we have it over in the Smalltalk world and you guys should come and get it.
Avi Bryant

Avi Bryant

GemStone/S is the state-of-the-art platform for developing, deploying, and managing scalable, high-performance, multi-tier applications based on business objects.

GemStone/S

A wise man once asked...

How much do you think about saving your data instead of using it?
Dale Henrichs

Dale Henrichs

Re-Introducing MagLev

MagLev logo

What we want to build

Ideal design

What we end up building

Actual design

Aha!

What we could build with MagLev

MagLev design

Decouple no more

MagLev becomes your API

Transparent Object Persistence

I've often wished that I could just do something where I just change a bunch of models and at the end of the request cycle something magical goes through and collects up all the models that have changed and persists them, and I haven't really gotten to that level yet. Sometimes I feel that ActiveRecord is holding me back from that.

Avdi Grimm

The Punchline

You can just switch to Java, or .NET.

David Laribee

Waa waaah

Transparent Object Persistence


Maglev::PERSISTENT_ROOT
  

Simple Example


# VM #1
Maglev.abort_transaction
j = Rubyist.new("Jesse")
j.object_id
# => 1234567
Maglev::PERSISTENT_ROOT[:jesse] = j
Maglev.commit_transaction
  

# VM #2
Maglev.abort_transaction
Maglev::PERSISTENT_ROOT[:jesse].object_id
# => 1234567
  

What is happening?


Maglev.abort_transaction
    

Maglev.commit_transaction
    

Persistence by Reachability


# VM #1
Maglev.abort_transaction

cats = CatCollection.new
pierre = Cat.new("Pierre")
cats << pierre
Maglev::PERSISTENT_ROOT[:cats] = cats

Maglev.commit_transaction
pierre.object_id
# => 9876543
  

# VM #2
Maglev.abort_transaction
persisted_cats = Maglev::PERSISTENT_ROOT[:cats]

persisted_cats.first.object_id
# => 9876543
  

Persistence by Reachability


# VM #3
Maglev.abort_transaction

pierre = Maglev::PERSISTENT_ROOT[:cats].first
pierre.favorite_toy = BallOfYarn.new

Maglev.commit_transaction
  

# VM #4
Maglev.abort_transaction

Maglev::PERSISTENT_ROOT[:cats].first.favorite_toy
# => #<BallOfYarn:123987>
  

What can you (easily) persist today?

Arrays

Sets

Hashes

Counters

Sorted Sets

Something more... exotic

k-D Tree
Bloom Filter
Judy Array

Leftist Tree

(Almost) All the objects!

Proc
Binding
IO

A blog I whipped up this morning


class MaglevBlogApp < Sinatra::Application
  set :root, File.dirname(__FILE__)

  before do
    @blog = Maglev::PERSISTENT_ROOT[:maglev_blogs].first
    @posts = @blog.posts
  end

  get "/" do
    erb :index
  end
end
  

class MaglevBlogApp < Sinatra::Application
  # ...
  get "/posts/:id" do
    @post = @posts.detect { |post|
      post.object_id == params[:id].to_i
    }
    erb :post
  end
end
          

class MaglevBlogApp < Sinatra::Application
  # ...

  post "/post" do
    post = @posts.class.new(params[:title], params[:body])
    @blog.posts << post
    redirect "/"
  end
end
  

class MagLevTransactionWrapper
  def initialize(app); @app = app; end

  def call(env)
    begin
      Maglev.abort_transaction
      status, headers, body = @app.call env
      [status, headers, body]
    ensure
      if committable? status
        Maglev.commit_transaction
      else
        Maglev.abort_transaction
      end
    end
  end
end
  

class MaglevBlog
  attr_accessor :posts, :name, :author
  def initialize(name, author_name)
    @posts = []
    @name = name
    @author = Author.new(author_name)
  end
  # ...
end
  

class MaglevBlog
  # ...

  class Post
    attr_accessor :title, :body
    def initialize(title, body)
      @title = title
      @body = body
    end
  end

  # ...
end
  

class MaglevBlog
  # ...

  class Author
    attr_accessor :name
    def initialize(name)
      @name = name
    end
  end
end
  

# bootstrap.rb

Maglev.persistent do
  load "maglev_blog.rb"
  blog = MaglevBlog.new("Cascadia Ruby!", "jc00ke")
  Maglev::PERSISTENT_ROOT[:maglev_blogs] = [blog]
end
  

maglev-ruby -Mcommit bootstrap.rb
  

Background Jobs

Sidekiq

Resque

Queue Classic

RabbitMQ

etc...

World's worst (but easiest) queue


# pseudo
jobs = []
jobs << Proc.new { puts "doing work" }
jobs.shift.call
  

Slightly-better producer


Maglev.abort_transaction
Maglev::PERSISTENT_ROOT[:q] ||= []

while true do
  Maglev::PERSISTENT_ROOT[:q] << Proc.new { puts "doing work" }
  Maglev.commit_transaction
end
  

Slightly-better worker


while true do
  Maglev.abort_transaction
  work = Maglev::PERSISTENT_ROOT[:q].shift
  Maglev.commit_transaction
  work.call if work
end
  

Issues?

  1. What happens if a commit fails?
  2. Array isn't a concurrent data structure
  3. others...

Slightly-better producer


Maglev.abort_transaction
Maglev::PERSISTENT_ROOT[:q] ||= []

while true do
  Maglev::PERSISTENT_ROOT[:q] << Proc.new { puts "doing work" }
  Maglev.commit_transaction
rescue Maglev::CommitFailedException
  puts "Couldn't add the job. Retrying..."
  redo
end
  

Slightly-better worker


while true do
  Maglev.abort_transaction
  work = Maglev::PERSISTENT_ROOT[:q].shift
  Maglev.commit_transaction
  work.call if work
rescue Maglev::CommitFailedException
  puts "Someone grabbed this job before me. Go grab another."
  redo
end
  

Issues?

  1. What happens if a commit fails?
  2. Array isn't a concurrent data structure
  3. others...

Reduced Conflict Classes

RCHash

RCQueue

RCCounter*

RCIdentityBag*

Write RCHash diagram
Read RCHash diagram

Slightly-better producer


require 'maglev/rcqueue'

Maglev.abort_transaction
Maglev::PERSISTENT_ROOT[:q] ||= RCQueue.new

while true do
  Maglev::PERSISTENT_ROOT[:q] << Proc.new { puts "doing work" }
  Maglev.commit_transaction
rescue Maglev::CommitFailedException
  puts "Couldn't add the job. Retrying..."
  redo
end
  

Issues?

  1. What happens if a commit fails?
  2. Array isn't a concurrent data structure
  3. others...

MagLev-Q

https://github.com/jc00ke/maglev-q

What's the point?

Totals grouped by language (dominant language first):
ruby:            34 (100.00%)

Total Physical Source Lines of Code (SLOC)                = 34
Development Effort Estimate, Person-Months                = 0.07
Schedule Estimate, Months                                 = 0.90
Estimated Average Number of Developers (Effort/Schedule)  = 0.08
Total Estimated Cost to Develop                           = $ 776
 (average salary = $56,286/year, overhead = 2.40).
  

Generated using David A. Wheeler's 'SLOCCount'

Like all implementations

MagLev needs help

RubySpec - catch up to 1.8.7
Other C-ext
RSpec's described_class, etc
IO

Things we depend on

<nokogiri>

bcrypt

{ json }

The future of MagLev

GemStone/S 3.1.0.1

HPI 1.9/Rails 4

Your cool projects


rvm install maglev
ruby-build install maglev-1.1.0-dev
./install.sh
  

Thanks!

Jesse Cooke

github/jc00ke

@jc00ke

jesse [at] jc00ke.com

Reveal.js

HTML Presentations Made Easy

Created by Hakim El Hattab / @hakimel

Themes

Reveal.js comes with a few themes built in:
Solarized

* Theme demos are loaded after the presentation which leads to flicker. In production you should load your theme in the <head> using a <link>.

Pretty Code


function linkify( selector ) {
  if( supports3DTransforms ) {

    var nodes = document.querySelectorAll( selector );

    for( var i = 0, len = nodes.length; i < len; i++ ) {
      var node = nodes[i];

      if( !node.className ) {
        node.className += ' roll';
      }
    }
  }
}
					

Courtesy of highlight.js.