Behind the Curtain

Post on 09-May-2015

5.352 views 1 download

description

My talk at red dirt ruby conf

Transcript of Behind the Curtain

Testing This

TESTING TESTING

TESTING TESTING

Thursday, April 21, 2011

ZOMG!

Thursday, April 21, 2011

GOOD MORNING!

Thursday, April 21, 2011

Aaron Patterson

Thursday, April 21, 2011

@tenderlove

Thursday, April 21, 2011

WWFMD?Thursday, April 21, 2011

ITWFMWD?Thursday, April 21, 2011

Thursday, April 21, 2011

AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.

Thursday, April 21, 2011

Miami Vice Principal Señor Facebook

Integration Engineering Manager

Thursday, April 21, 2011

Elect

Thursday, April 21, 2011

12%

61%

24%

2%

Inspirational Entertaining Informative Wasted

Our Time Together

Thursday, April 21, 2011

Thursday, April 21, 2011

KEYNOTE

Thursday, April 21, 2011

10%

50%

20%

20%

Inspirational Entertaining Informative Wasted

Our Time Together

Thursday, April 21, 2011

Thursday, April 21, 2011

20% More Transitions

Thursday, April 21, 2011

Living Behindthe Curtain 6 subjects, less

than 10minutes on each of them

I enjoy dealing with behinds the scenes information

Thursday, April 21, 2011

DATABASE

APPLICATION

CLIENT

Thursday, April 21, 2011

DATABASE

APPLICATION

CLIENT

Thursday, April 21, 2011

DATABASE

APPLICATION

CLIENT

Thursday, April 21, 2011

DATABASE

APPLICATION

CLIENT

Thursday, April 21, 2011

Code as Data

Thursday, April 21, 2011

Politics

Thursday, April 21, 2011

"Open Sourceis a Democracy"

We don't vote on commits. It would be too inefficient.

Thursday, April 21, 2011

OSS is an Oligarchy

Thursday, April 21, 2011

Benevolent Dictator

Thursday, April 21, 2011

Tyrannical Leader

Small projects have few people, so it is efficient

Large projects have too many people so it becomes inefficient to individually consider all feedback

Thursday, April 21, 2011

Thursday, April 21, 2011

Care and Feeding of Your Tyrant

Thursday, April 21, 2011

Cheetos

Thursday, April 21, 2011

Feed them Quality Bugs

Good reproduction steps

Thursday, April 21, 2011

Feed them Patches with Tests

Thursday, April 21, 2011

leave build systems

Gemspec,JewlerHoe,etc

Thursday, April 21, 2011

leave test frameworks

Thursday, April 21, 2011

Use +1's Carefully

Thursday, April 21, 2011

Thursday, April 21, 2011

Thursday, April 21, 2011

320 comments

Thursday, April 21, 2011

210 plus one

Thursday, April 21, 2011

Thursday, April 21, 2011

FFFFFFFUUUUUUUUUUUU

Thursday, April 21, 2011

Thursday, April 21, 2011

Superficial

Thursday, April 21, 2011

Merit

Thursday, April 21, 2011

Quality

Thursday, April 21, 2011

How to fix?

We'll discuss this in closing.

Thursday, April 21, 2011

Encodingsthe silent killer

Thursday, April 21, 2011

Never a miscommunication

Data goes in, Data goes out

Thursday, April 21, 2011

Until there is

Thursday, April 21, 2011

`encode': "\xE9" from ASCII-8BIT to UTF-8(Encoding::UndefinedConversionError)

Thursday, April 21, 2011

You can't explain that

Encoding Error?

Thursday, April 21, 2011

String Tagging

Thursday, April 21, 2011

"hello! <3".encoding # => #<Encoding:UTF-8>

Strings with unknown encoding are marked as binary.

Thursday, April 21, 2011

socket.read(4).encoding # => #<Encoding:ASCII-8BIT>

Thursday, April 21, 2011

Boundaries

To understand these bugs, we must find boundaries

Thursday, April 21, 2011

App Code

Thursday, April 21, 2011

App Code

Web Server

Database

NoSQL File System

SadnessBelt

Thursday, April 21, 2011

#<Encoding:ASCII-8BIT>is usually a bug!

Databases know the stored encoding

Post contains encoding / spec declares default

Thursday, April 21, 2011

# encoding: utf-8

name = 'たこ焼き仮面'

user = User.create!(:name => name)user.reloaduser.name + name

Thursday, April 21, 2011

# encoding: utf-8

name = 'たこ焼き仮面'

user = User.create!(:name => name)user.reloaduser.name + name

incompatible character encodings: ASCII-8BIT and UTF-8 (Encoding::CompatibilityError)

Thursday, April 21, 2011

>> name = "Aaron Patterson">> name.encoding=> #<Encoding:UTF-8>>> User.create!(:name => name)>> u = User.find :first>> u.name.encoding=> #<Encoding:ASCII-8BIT>

Thursday, April 21, 2011

>> name = "Aaron Patterson">> name.encoding=> #<Encoding:UTF-8>>> User.create!(:name => name)>> u = User.find :first>> u.name.encoding=> #<Encoding:ASCII-8BIT>

Thursday, April 21, 2011

name = 'aaron patterson'user = User.create!(:name => name)user.reloaduser.name + name

Ruby attempts to convert the binary for you

Thursday, April 21, 2011

name = 'aaron patterson'user = User.create!(:name => name)user.reloaduser.name + name

no error

Ruby attempts to convert the binary for you

Thursday, April 21, 2011

ASCII-8BIT + othershould raise

Thursday, April 21, 2011

ASCII-8BIT + othershould raise

Aaron'sOpinionCorner!

Thursday, April 21, 2011

Resources Know Encoding

Thursday, April 21, 2011

Prepared Statements

Thursday, April 21, 2011

SELECT * FROM "users" WHERE id = 1

Thursday, April 21, 2011

♥Parse the Query♥Formulate an Execution Plan♥Return results

Normal Queries

Thursday, April 21, 2011

Application

Database

Thursday, April 21, 2011

Application

Database

Result Set

Thursday, April 21, 2011

Application

Database

Thursday, April 21, 2011

Application

Database

query: 1234

Thursday, April 21, 2011

Application

Database

Thursday, April 21, 2011

Application

Database

Result Set

Thursday, April 21, 2011

♥Parse the Query♥Formulate an Execution Plan

Preparation

Thursday, April 21, 2011

Execution

♥Return Results

Thursday, April 21, 2011

Data transfer decreases

Thursday, April 21, 2011

Query Planner Improves

Thursday, April 21, 2011

Security

Thursday, April 21, 2011

SQLite3 Issues :'(

Talk about problem inserting rows with session data

Could insert session data, but could not find it

Thursday, April 21, 2011

INSERT INTO "sessions"("data", "session_id")VALUES (?, ?)

['data', 'BAh7BjoIZm9vSSIIYmF6BjoGRUY=']['session_id', 'dae0fb8a9c34e6980c9d0fae33a8fff6']

Thursday, April 21, 2011

db = SQLite3::Database.new ':memory:'db.trace { |sql| puts sql }

Thursday, April 21, 2011

INSERT INTO "sessions"("data", "session_id")

VALUES (

'BAh7BjoIZm9vSSIIYmF6BjoGRUY=',

x'3633303337623066376536613130343132343765623763626631616439303631')

Thursday, April 21, 2011

INSERT INTO "sessions"("data", "session_id")

VALUES (

'BAh7BjoIZm9vSSIIYmF6BjoGRUY=',

x'3633303337623066376536613130343132343765623763626631616439303631')

Thursday, April 21, 2011

db.trace { |sql| puts sql }

stmt = db.prepare('INSERT INTO "sessions" (session_id)VALUES (?)')

stmt.bind_param 1, 'fuu'stmt.execute

Made a sample program to reproduce the problem

Thursday, April 21, 2011

INSERT INTO "sessions"(session_id) VALUES ('fuu')

Resulting Query

Thursday, April 21, 2011

stmt.bind_param 1, 'fuu'.encode('BINARY')stmt.execute

Thursday, April 21, 2011

INSERT INTO "sessions" (session_id)VALUES (x'667575')

Thursday, April 21, 2011

INSERT INTO "sessions" (session_id)VALUES (x'667575')

Why did sqlite3 do this?

Talk about column affinity

Thursday, April 21, 2011

Why is it stored as Binary?

Thursday, April 21, 2011

Why is the session id tagged binary?

Thursday, April 21, 2011

>> x = OpenSSL::Random.random_bytes(10)=> "G\x93\xFC\xB2\xCE\xC0\xEC\xBB\xA7W">> x.encoding=> #<Encoding:ASCII-8BIT>

Thursday, April 21, 2011

>> y = x.unpack('H*')=> ["4793fcb2cec0ecbba757"]>> y.first.encoding=> #<Encoding:ASCII-8BIT>

Thursday, April 21, 2011

def generate_sid ActiveSupport::SecureRandom.hex(16)end

Thursday, April 21, 2011

def generate_sid sid = ActiveSupport::SecureRandom.hex(16) if sid.respond_to?(:encode!) sid.encode!('UTF-8') end sidend

Should hex have tagged it?

Should have generate_sid tagged it?

Thursday, April 21, 2011

Find the Source

Thursday, April 21, 2011

ARel and ActiveRecord

Thursday, April 21, 2011

Fear of SQL

Thursday, April 21, 2011

Why?

Thursday, April 21, 2011

We must learn SQL

But not specifically SQL

Working with sets

Can fetch correct data.

Thursday, April 21, 2011

We must learn to work with Sets

Thursday, April 21, 2011

Avoid SQL for:

Thursday, April 21, 2011

code reuse

Thursday, April 21, 2011

security

Thursday, April 21, 2011

independence

from databases

Thursday, April 21, 2011

What is ARel?

Thursday, April 21, 2011

AST

Represents SQL as a Tree

Represents the IDEA of a SQL statement

Thursday, April 21, 2011

SQL Compiler

Thursday, April 21, 2011

Representsan IDEA

Only until we invoke the compiler, does the AST become a query

Thursday, April 21, 2011

Relationship to ActiveRecord

Thursday, April 21, 2011

User.where('something')

Thursday, April 21, 2011

class User scope :heart where(:name => '<3')end

User.heart.select('name')

Thursday, April 21, 2011

ActiveRecord

Our Application

ARel Database

Thursday, April 21, 2011

ActiveRecord

Our Application

.to_a

ARel Database

Thursday, April 21, 2011

ActiveRecord

Our Application

ARel Database

to_sql

Thursday, April 21, 2011

ActiveRecord

Our Application

ARel Database

SELECT * ...

Thursday, April 21, 2011

ActiveRecord

Our Application

ARel Database

[{: name => '<3'}]

Thursday, April 21, 2011

ActiveRecord::Relation

Thursday, April 21, 2011

shortens to...

Thursday, April 21, 2011

ARel

Thursday, April 21, 2011

ARel != ARel

Thursday, April 21, 2011

:'(

Thursday, April 21, 2011

SQL Compiler

Future we can have optimizers, compiler cache, etc

Thursday, April 21, 2011

Interpreter?

Thursday, April 21, 2011

module Arel module Visitors class Mongo < Arel::Visitors::Visitor attr_reader :db

def initialize db; @db = db; end Query = Struct.new(:collection_name, :fields, :conditions)

private

def visit_Arel_Nodes_SelectStatement o o.cores.map { |c| visit_Arel_Nodes_SelectCore c }.map { |query| collection = db.collection query.collection_name fields = query.fields selector = Hash[query.conditions] opts = {} opts[:fields] = fields unless fields.empty? opts[:limit] = o.limit.expr.to_i if o.limit collection.find(selector, opts).to_a }.flatten end

def visit_Arel_Nodes_SelectCore o fields = o.projections.map { |proj| visit(proj) }.compact conditions = o.wheres.map { |condition| visit(condition) }.flatten(1) Query.new(visit(o.source), fields, conditions) end

def visit_Arel_Nodes_And o o.children.map { |child| visit child } end

def visit_Arel_Nodes_Equality o [visit(o.left), visit(o.right)] end

def visit_Arel_Attributes_Attribute o return if o.name == '*'; o.name end

def visit_Arel_Nodes_JoinSource o visit o.left end

def visit_Arel_Table o o.name end def literal o; o; end alias :visit_String :literal alias :visit_Fixnum :literal end endend

Thursday, April 21, 2011

module Arel module Visitors class Mongo < Arel::Visitors::Visitor attr_reader :db

def initialize db; @db = db; end Query = Struct.new(:collection_name, :fields, :conditions)

private

def visit_Arel_Nodes_SelectStatement o o.cores.map { |c| visit_Arel_Nodes_SelectCore c }.map { |query| collection = db.collection query.collection_name fields = query.fields selector = Hash[query.conditions] opts = {} opts[:fields] = fields unless fields.empty? opts[:limit] = o.limit.expr.to_i if o.limit collection.find(selector, opts).to_a }.flatten end

def visit_Arel_Nodes_SelectCore o fields = o.projections.map { |proj| visit(proj) }.compact conditions = o.wheres.map { |condition| visit(condition) }.flatten(1) Query.new(visit(o.source), fields, conditions) end

def visit_Arel_Nodes_And o o.children.map { |child| visit child } end

def visit_Arel_Nodes_Equality o [visit(o.left), visit(o.right)] end

def visit_Arel_Attributes_Attribute o return if o.name == '*'; o.name end

def visit_Arel_Nodes_JoinSource o visit o.left end

def visit_Arel_Table o o.name end def literal o; o; end alias :visit_String :literal alias :visit_Fixnum :literal end endend

Mongo Interpreter64 loc

Thursday, April 21, 2011

Thursday, April 21, 2011

gist.github.com/845782

Thursday, April 21, 2011

Custom DSL

Thursday, April 21, 2011

class Select < Struct.new(:columns) def self.* other other.select = new(Arel.sql('*')) other endend

Responds to *

Thursday, April 21, 2011

Responds to WHERE

class From < Struct.new(:table, :conditions) def WHERE conditions self.conditions = conditions Where.new(self) endend

Thursday, April 21, 2011

Responds to to_s

class Where < Struct.new(:from, :select) def to_s Arel::Table.engine = Arel::Sql::Engine.new( FakeRecord::Base.new) table = Arel::Table.new from.table table.project(select.columns).where( from.conditions.map { |k,v| table[k].eq v }).to_sql endend

Thursday, April 21, 2011

Bootstrap Methods

SELECT = Selectdef FROM table From.new tableend

Thursday, April 21, 2011

x = SELECT * FROM("users") .WHERE(:id => 10)puts x

Thursday, April 21, 2011

Thursday, April 21, 2011

Write SQL Rubyto avoid SQL

Thursday, April 21, 2011

Streaming

Talked about data from the back end, talk a bit about data to the front end

Thursday, April 21, 2011

Twitter Streaming API

Thursday, April 21, 2011

Chunked Responses

Needed for persistent connections

Thursday, April 21, 2011

Normal TimelineBuffers data, then sends a response

Thursday, April 21, 2011

Processing ERb

Normal TimelineBuffers data, then sends a response

Thursday, April 21, 2011

Processing ERb

Download Asset

Download Asset

Normal TimelineBuffers data, then sends a response

Thursday, April 21, 2011

Chunked TimelineSend data as soon as it is available

Thursday, April 21, 2011

Processing ERb

Chunked TimelineSend data as soon as it is available

Thursday, April 21, 2011

Processing ERb

Download Asset

Download Asset

Chunked TimelineSend data as soon as it is available

Thursday, April 21, 2011

Implementation

Thursday, April 21, 2011

Rack API

Thursday, April 21, 2011

status, headers, body = app.call(env)

Thursday, April 21, 2011

status.to_i

Thursday, April 21, 2011

headers.is_a?(Hash)

Thursday, April 21, 2011

body.responds_to?(:each)

Thursday, April 21, 2011

Inside Rack

body.each do |chunk| output(chunk)endbody.close rescue nil

Calls each,

possibly calls close

Thursday, April 21, 2011

Sample Applicationclass MyApp def call(env) # some computation body = 'hello' # more computation body << ' world'

[200, { 'X-Hello' => 'World' }, [body]] endend

Thursday, April 21, 2011

Delay work until eachclass FooBody def each yield "hello " sleep(10) # simulate work yield "world!" endend

Thursday, April 21, 2011

Problems ☹

Database connections were lost

Thursday, April 21, 2011

Middleware

Database connections are managed in middleware

Thursday, April 21, 2011

"There are two hard problems in CS: cache invalidation

and naming things"-- Phil Karlton

Thursday, April 21, 2011

class DbCache < Struct.new(:app) def call(env) # init cache status, headers, body = app.call(env) # clear cache [status, headers, body] endend

Thursday, April 21, 2011

class BodyProxy < Struct.new(:delegate) def each delegate.each { |x| yield x } end

def close delegate.close rescue nil # clear cache endend

Thursday, April 21, 2011

Update Middleware, Database Works! ☺

Thursday, April 21, 2011

But Query Cache was broken. ☹

Same problem in query cache as the database handle cache.

Thursday, April 21, 2011

Middleware is coupled to the callstack

Thursday, April 21, 2011

~ 24 middleware

Thursday, April 21, 2011

~ 5 required a proxy

Thursday, April 21, 2011

Types of Middleware

♥Generators♥Filters♥Lifecycle Handlers

Rack jams these to one API.

Approach seems naive when examining usage

Tied to callstack

Thursday, April 21, 2011

Solutions!

Thursday, April 21, 2011

Embrace The Differences!

Thursday, April 21, 2011

class Listener def created(event) end

def destroyed(event) endend

Lifecycle Listeners

Thursday, April 21, 2011

Generatorsclass Resource def service(request, response) 10.times { |i| response.body.write "hello #{i}" } response.body.close endend

Synchronous or Asynchronous

Return value is ignored

Thursday, April 21, 2011

Filters

class Filter def filter(request, response, chain) chain.filter(request, response) endend

Thursday, April 21, 2011

♥Synchronous

♥Asynchronous

♥Freedom from the Callstack

♥Thread Safety

Thursday, April 21, 2011

I stole this API.

Thursday, April 21, 2011

TEE HEE!!!!!

Thursday, April 21, 2011

fixing our tyrants

Thursday, April 21, 2011

Facebook + Github

Thursday, April 21, 2011

Get Involved

Thursday, April 21, 2011

Gain Trust

Only fix the immediate problem

Thursday, April 21, 2011

Then Persuade

Thursday, April 21, 2011

Thursday, April 21, 2011

Thursday, April 21, 2011

Questions?

Thursday, April 21, 2011