afeld/mongoid-locker

View on GitHub
demo/showoff.md

Summary

Maintainability
Test Coverage
!SLIDE

# Mongoid-Locker

[github.com/afeld/mongoid-locker](https://github.com/afeld/mongoid-locker)

## Aidan Feldman, [Jux.com](https://jux.com)

!SLIDE

![Instagram diagram](instagram.png)

!SLIDE

# Jux needed a queue.

* Distributed, but synchronized
* No add'l DB
* No add'l hassle (managing workers, etc.)

!SLIDE

* Thread
  - \+ Distributed
  - – Not synchronized
* Many workers
  - \+ Distributed
  - – Not synchronized
* One worker
  - \+ Synchronized
  - – Not distributed
  - – Single POF

!SLIDE

    @@@ ruby
    require 'rubygems'
    require 'mongoid-locker'
    Mongoid.load!('config/mongoid.yml', :development)


    class User
      include Mongoid::Document
      # include Mongoid::Locker

      field :balance, type: Float
    end

    # cleanup
    User.destroy_all

    bob = User.create!(balance: 100.00)

!SLIDE

# Atomic Operations

    @@@ ruby
    class User
      def purchase(amount)
        if amount > self.balance
          raise "Can't have negative balance!" 
        else
          # deduct *atomically*
          self.inc(:balance, -1 * amount)
          # a.k.a.
          # db.users.update({_id: ...}, {$inc: {balance: ...}})

          puts "cha-ching!"
        end
      end
    end

    bob.purchase(5.10) #=> "cha-ching!"
    bob.purchase(110.53) #=> "Can't have negative balance!"

!SLIDE

# All fine, right?

!SLIDE

    @@@ ruby
    class User
      def purchase(amount)
        if amount > self.balance
          raise "Can't have negative balance!" 
        else
          # artificial delay
          print 'has enough money...waiting for ENTER > '
          gets

          self.inc(:balance, -1 * amount)
          puts "cha-ching!"
        end
      end
    end

!SLIDE

    @@@ ruby
    # shell 1
    bob.purchase(10.00)

    # shell 2
    also_bob = User.first
    also_bob.purchase(95.00)

!SLIDE

# oops.

!SLIDE

    @@@ ruby
    class User
      # add doc-level locking
      include Mongoid::Locker
      timeout_lock_after 20

      def purchase(amount)
        # only one at a time
        self.with_lock(wait: true) do
          # after the `wait`, will have updated `balance`

          if amount > self.balance
            raise "Can't have negative balance!" 
          else
            print 'has enough money...waiting for ENTER > '
            gets

            self.inc(:balance, -1 * amount)
            puts "cha-ching!"
          end
        end
      end
    end

!SLIDE

# Summary

* Easy document-level locking
* Useful for queueing or pseudo-transactions
* No additional dependencies

!SLIDE

# Fin.

[afeld/mongoid-locker](https://github.com/afeld/mongoid-locker)

----------------

## Aidan Feldman, [Jux.com](https://jux.com)

[@aidanfeldman](https://twitter.com/aidanfeldman)

[afeld.me](http://afeld.me)