SlideShare una empresa de Scribd logo
1 de 70
Descargar para leer sin conexión
Integrating PostgreSQL
Reuven M. Lerner • reuven@lerner.co.il
Rails Israel Conference 2013
October 9th, 2013 • Tel Aviv
1
Who am I?
• Web developer since 1993
• Linux Journal columnist since 1996
• PostgreSQL user since at least 1999
• Ruby/Rails developer, teacher since 2005
• PhD candidate in learning sciences at
Northwestern University
2
But before all of that...
I was a kid.
3
(Yes, a geeky kid.)
But before all of that...
I was a kid.
3
4
5
6
7
Databases?!?
Who needs them?
8
Er, I did. And do.
• Whadaya know? Databases are useful!
• They offer an abstraction layer for our data
• I don’t care how it is stored, so long as the
data is safe, and I can get it back
9
What is a database?
Database
Store data
confidently
Retrieve data
flexibly
10
Relational databases
• Client-server
• All data is stored in two-dimensional tables
• Client-server communication is in SQL
• SQL is standard. Each database implements
a superset of a subset of that standard.
11
“Impedance mismatch”
• You basically couldn’t find two languages
that are more different from one another
than Ruby and SQL:
• Ruby — object oriented, imperative
• SQL — table oriented, declarative
• Mixing them is ugly, as we all know
12
ActiveRecord!
• ActiveRecord is the default ORM (object-
relational manager) in Rails
• ActiveRecord translates our Ruby method
calls to SQL queries
• Query results are turned into Ruby objects
and collections
13
The good news...
• ActiveRecord papers over the differences
between databases!
14
... and the bad news
• ActiveRecord papers over the differences
between databases!
15
The really bad news
• This means we’re:
• Writing extra code
• Slowing down our apps
• Putting logic in the wrong place
• Database logic shouldn’t be in your app
(just like SQL shouldn’t be in your HTML)
16
PostgreSQL
17
PostgreSQL
17
PostgreSQL
17
PostgreSQL
17
PostgreSQL
17
PostgreSQL
17
PostgreSQL
• Very fast, very scalable.
• Amazingly flexible, easily extensible.
• Rock-solid — no crashes, corruption,
major security issues for years
• Ridiculously easy administration
• It also happens to be free (MIT/BSD)
18
Brief history
• Ingres (Stonebreaker, Berkeley)
• Postgres (Stonebreaker, Berkeley)
• PostgreSQL project = Postgres + SQL
• About one major release per year
• Latest and greatest is 9.3
19
Features!
• MVCC
• Custom data types
• CTEs
• Functional, partial indexes
• Full-text search
• Server-side functions
20
Rails 4 to the rescue!
• As of Rails 4, many PostgreSQL features are
supported, out of the box, by ActiveRecord
• The best of both worlds:Amazing database
features, and we don’t have to use SQL!
21
But wait!
• What if I want to switch to another
database?
• Won’t I lose the platform independence?
• Yes, you will.
• Fortunately, this will almost certainly not
happen.
• And if it does, it’ll be super-painful anyway.
22
Wait again!
• This means that I can’t use SQLite in
development, and PostgreSQL on the
server.
• Yes, that’s right.
• You should use the same database in
development and production. Please.
23
Data types
• Databases are like programming languages:
The right choice of data type can have huge
consequences in speed, size, and flexibility
• PostgreSQL has some special ones that can
really come in handy!
24
Network types
• PostgreSQL supports INET, CIDR, and
MACADDR types
• If you’re ever storing network addresses,
you should use these, not a text type!
• Savings in space (and time), and faster
comparisons (numeric vs. textual)
25
CREATE TABLE Logins (
id SERIAL PRIMARY KEY,
local_ip INET NOT NULL,
remote_ip INET NOT NULL);
INSERT INTO Logins (local_ip, remote_ip) values
('192.168.1.1', '10.1.2.3');
reuven=# INSERT INTO Logins (local_ip, remote_ip) values
('999.999.999.1000', '10.1.2.3');
ERROR: invalid input syntax for type inet:
"999.999.999.1000"
LINE 1: INSERT INTO Logins (local_ip, remote_ip) values
('999.999.99...
26
Rails 4 migrations
$ rails g model login local_ip:inet remote_ip:inet
class CreateLogins < ActiveRecord::Migration
def change
create_table :logins do |t|
t.inet :local_ip, nulls: :false
t.inet :remote_ip, nulls: :false
t.timestamps
end
end
end
27
Rails 4 app
Login.create!(local_ip:'192.168.1.1', remote_ip:'10.1.2.3')
Login.create!(local_ip:'192.168.1.1000', remote_ip:'10.1.2.3')
IPAddr::InvalidAddressError: invalid address
i = Login.first.local_ip
i.class
=> IPAddr
i.ipv4?
=> true
i.ipv6?
=> false
28
Managing a network?
• We also have CIDR and MAC addresses!
Netconfig.create!(net: '192.168.1.1/255.255.255.0')
29
UUIDs
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE evil_companies (
id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v1(),
name TEXT);
INSERT INTO evil_companies (name) VALUES ('HOT Internet');
INSERT INTO evil_companies (name) VALUES ('HOT TV');
INSERT INTO evil_companies (name) VALUES ('HOT Telephone');
INSERT INTO evil_companies (name) VALUES ('Voldemort, Ltd.');
30
Wait... extensions?
• Yes, PostgreSQL has extensions!
• Download and install them from PGXN
• (Think of them as gems for PostgreSQL)
• Some, like uuid-ossp, come by default
• Activate them with CREATE EXTENSION
31
32
With this in place...
SELECT * FROM evil_companies ;
┌──────────────────────────────────────┬─────────────────┐
│ id │ name │
├──────────────────────────────────────┼─────────────────┤
│ da7a333a-3069-11e3-b4fd-28cfe91f81e7 │ HOT Internet │
│ dc480be2-3069-11e3-a0f4-28cfe91f81e7 │ HOT TV │
│ ddadabfe-3069-11e3-af1d-28cfe91f81e7 │ HOT Telephone │
│ 4c33a128-306a-11e3-8000-28cfe91f81e7 │ Voldemort, Ltd. │
└──────────────────────────────────────┴─────────────────┘
33
Migrations
# Requires some manual tinkering
class CreateEvilCompanies < ActiveRecord::Migration
def change
enable_extension "uuid-ossp"
create_table :evil_companies, id: :uuid do |t|
t.text :name
t.timestamps
end
end
end
34
Migrations
# Requires some manual tinkering
class CreateEvilCompanies < ActiveRecord::Migration
def change
enable_extension "uuid-ossp"
create_table :evil_companies, id: :uuid do |t|
t.text :name
t.timestamps
end
end
end
34
In the app
EvilCompany.create!(name: 'HOT Internet')
EvilCompany.first.id
=> "07d3f537-c008-4006-9f2b-0592c7df3ebf"
35
JSON
• Remember the “impedance mismatch”
between objects and databases?
• Now we have another one — we get data
in JSON, turn it into database objects, and
then turn it back into JSON
• But now PostgreSQL can store, retrieve,
and query JSON!
36
CREATE TABLE bad_service (
id SERIAL,
info JSON
);
INSERT INTO bad_service (info)
values ('{"company":"HOT Internet", "badness":9}');
INSERT INTO bad_service (info) VALUES ('la la la');
ERROR: invalid input syntax for type json
LINE 1: insert into bad_service (info) values ('la la la');
^
DETAIL: Token "la" is invalid.
CONTEXT: JSON data, line 1: la...
37
JSON operators!
• As of PostgreSQL 9.3, we get operators
• Work with JSON, much like XML:
SELECT info#>'{company}' from bad_service;
┌────────────────┐
│ ?column? │
├────────────────┤
│ "HOT Internet" │
└────────────────┘
38
Migration
$ rails g model bad_service info:json
class CreateBadServices < ActiveRecord::Migration
def change
create_table :bad_services do |t|
t.json :info
t.timestamps
end
end
end
39
App
BadService.create!(info: '{"company":"HOT Internet"}')
b = BadService.first
b.info.class
=> Hash
b.info['company']
=> "HOT Internet"
40
Ranges
CREATE TABLE forecast (
forecast_for DATE,
hi_lo int4range);
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('8-
oct-2013', '[19,29]');
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('9-
oct-2013', '[20,32]');
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('10-
oct-2013', '[19,28]');
41
SELECT * FROM forecast ;
┌──────────────┬─────────┐
│ forecast_for │ hi_lo │
├──────────────┼─────────┤
│ 2013-10-08 │ [19,30) │
│ 2013-10-09 │ [20,33) │
│ 2013-10-10 │ [19,29) │
└──────────────┴─────────┘
SELECT * FROM forecast WHERE hi_lo @> 19;
┌──────────────┬─────────┐
│ forecast_for │ hi_lo │
├──────────────┼─────────┤
│ 2013-10-08 │ [19,30) │
│ 2013-10-10 │ [19,29) │
└──────────────┴─────────┘
42
Migration
$ model forecast forecast_for:date hi_lo:int4range
class CreateForecasts < ActiveRecord::Migration
def change
create_table :forecasts do |t|
t.date :forecast_for
t.int4range :hi_lo
t.timestamps
end
end
end
43
Ranges in apps
Forecast.create!(forecast_for: Time.now, hi_lo: 20..30)
Forecast.first.hi_lo
=> 20...31
Forecast.first.hi_lo.class
Range
44
Arrays!
CREATE TABLE posts (
body text,
tags text[]
);
INSERT INTO posts (body, tags) VALUES ('Hello, world',
'{intro, hello}');
INSERT INTO posts (body, tags) VALUES ('Hello again',
'{DRY, boring, hello}');
45
SELECT * FROM posts;
┌──────────────┬────────────────────┐
│ body │ tags │
├──────────────┼────────────────────┤
│ Hello, world │ {intro,hello} │
│ Hello again │ {DRY,boring,hello} │
└──────────────┴────────────────────┘
SELECT * from posts where 'intro' = ANY(tags);
┌──────────────┬───────────────┐
│ body │ tags │
├──────────────┼───────────────┤
│ Hello, world │ {intro,hello} │
└──────────────┴───────────────┘
46
Arrays in migrations
$ rails g model post body:text tags:text
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :body
t.text :tags, array: true
t.timestamps
end
end
end
47
Arrays in your app
Post.create!(body: 'First post!', tags: %w(first second
third))
Post.first.tags
=> ["first", "second", "third"]
48
Normalization = DRY
• Everyone loves to talk about tagging as a
great example of PostgreSQL arrays
• Um... don’t forget about normalization, the
DRY of database design
• I know, it’s not cool to talk about it in the
Age of NoSQL. But it really does work.
49
Premature
denormalization is the
root of all database evil.
50
Hstore
• A key-value storage system in PostgreSQL!
• Any column can be defined as hstore
• Then you can query it — and you get a
hash back!
• It’s sort of like Redis or memcached, but
integrated into (and backed by) Postgres!
51
PostgreSQL
CREATE EXTENSION hstore;
CREATE TABLE posts2 (
body text,
tags hstore
);
INSERT INTO posts2 (body, tags)
VALUES ('Hello, there', '"hello" => 2, "boring" => 10');
INSERT INTO posts2 (body, tags)
values ('Hello again', '"DRY" => 5,
"hello" => 2, "boring" => 3');
52
SELECT * from posts2 where defined(tags, 'hello');
┌──────────────┬─────────────────────────────────────────┐
│ body │ tags │
├──────────────┼─────────────────────────────────────────┤
│ Hello, there │ "hello"=>"2", "boring"=>"10" │
│ Hello again │ "DRY"=>"5", "hello"=>"2", "boring"=>"3" │
└──────────────┴─────────────────────────────────────────┘
(2 rows)
SELECT tags -> 'boring' from posts2 ;
┌──────────┐
│ ?column? │
├──────────┤
│ 10 │
│ 3 │
└──────────┘
(2 rows)
53
And also...
SELECT * FROM posts2
WHERE (tags -> 'boring')::integer > 5;
┌──────────────┬──────────────────────────────┐
│ body │ tags │
├──────────────┼──────────────────────────────┤
│ Hello, there │ "hello"=>"2", "boring"=>"10" │
└──────────────┴──────────────────────────────┘
54
Migration
$ rails g model newpost body:text tags:hstore
class CreateNewposts < ActiveRecord::Migration
def change
enable_extension "hstore"
create_table :newposts do |t|
t.text :body
t.hstore :tags
t.timestamps
end
end
end
55
In your app
Newpost.create!(:body => 'foo', :tags => {a:1, b:2})
Newpost.first.tags
=> {"a"=>"1", "b"=>"2"} # hash!
Newpost.first.tags['a']
=> "1" # now a string!
Newpost.first.tags[:a]
=> nil # not WithIndifferentAccess!
56
Indexes
• PostgreSQL offers different index types
• Specify these in your migrations
• Partial indexes (with a WHERE clause)
• Functional indexes (apply a function!)
• Full-text indexes (many languages)
• Geospatial indexes (install PostGIS!)
57
Some things
are still missing
• “The Active Record way claims that
intelligence belongs in your models, not in
the database.As such, features such as
triggers or foreign key constraints, which
push some of that intelligence back into the
database, are not heavily used.”
• — Rails Guide,ActiveRecord Migrations
58
Sigh.
59
60
Your data are your
crown jewels!
61
Summary
• Rails good! PostgreSQL good!
• Rails + PostgreSQL = Doubleplus good!
• Rails 4 + PostgreSQL = Secure storage,
flexible retrieval, and a lot of geeky DB fun!
62
Thanks!
(Any questions?)
reuven@lerner.co.il
http://www.lerner.co.il/
@reuvenmlerner
054-496-8405
“reuvenlerner” on Skype
63

Más contenido relacionado

La actualidad más candente

Scaling Twitter with Cassandra
Scaling Twitter with CassandraScaling Twitter with Cassandra
Scaling Twitter with Cassandra
Ryan King
 

La actualidad más candente (20)

Scalable Streaming Data Pipelines with Redis
Scalable Streaming Data Pipelines with RedisScalable Streaming Data Pipelines with Redis
Scalable Streaming Data Pipelines with Redis
 
Functional Programming in JavaScript
Functional Programming in JavaScriptFunctional Programming in JavaScript
Functional Programming in JavaScript
 
Distributed computing with spark
Distributed computing with sparkDistributed computing with spark
Distributed computing with spark
 
Cassandra Summit 2014: Cassandra at Instagram 2014
Cassandra Summit 2014: Cassandra at Instagram 2014Cassandra Summit 2014: Cassandra at Instagram 2014
Cassandra Summit 2014: Cassandra at Instagram 2014
 
Large volume data analysis on the Typesafe Reactive Platform
Large volume data analysis on the Typesafe Reactive PlatformLarge volume data analysis on the Typesafe Reactive Platform
Large volume data analysis on the Typesafe Reactive Platform
 
Introduction to Spark
Introduction to SparkIntroduction to Spark
Introduction to Spark
 
MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...
MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...
MongoDB SoCal 2020: From Pharmacist to Analyst: Leveraging MongoDB for Real-T...
 
Masterless Distributed Computing with Riak Core - EUC 2010
Masterless Distributed Computing with Riak Core - EUC 2010Masterless Distributed Computing with Riak Core - EUC 2010
Masterless Distributed Computing with Riak Core - EUC 2010
 
Elasticsearch, Logstash, Kibana. Cool search, analytics, data mining and more...
Elasticsearch, Logstash, Kibana. Cool search, analytics, data mining and more...Elasticsearch, Logstash, Kibana. Cool search, analytics, data mining and more...
Elasticsearch, Logstash, Kibana. Cool search, analytics, data mining and more...
 
Big data 101 for beginners devoxxpl
Big data 101 for beginners devoxxplBig data 101 for beginners devoxxpl
Big data 101 for beginners devoxxpl
 
Scaling Twitter with Cassandra
Scaling Twitter with CassandraScaling Twitter with Cassandra
Scaling Twitter with Cassandra
 
Tuning and Debugging in Apache Spark
Tuning and Debugging in Apache SparkTuning and Debugging in Apache Spark
Tuning and Debugging in Apache Spark
 
Spark cassandra connector.API, Best Practices and Use-Cases
Spark cassandra connector.API, Best Practices and Use-CasesSpark cassandra connector.API, Best Practices and Use-Cases
Spark cassandra connector.API, Best Practices and Use-Cases
 
Enter the Snake Pit for Fast and Easy Spark
Enter the Snake Pit for Fast and Easy SparkEnter the Snake Pit for Fast and Easy Spark
Enter the Snake Pit for Fast and Easy Spark
 
Experiences in ELK with D3.js for Large Log Analysis and Visualization
Experiences in ELK with D3.js  for Large Log Analysis  and VisualizationExperiences in ELK with D3.js  for Large Log Analysis  and Visualization
Experiences in ELK with D3.js for Large Log Analysis and Visualization
 
CockroachDB: Architecture of a Geo-Distributed SQL Database
CockroachDB: Architecture of a Geo-Distributed SQL DatabaseCockroachDB: Architecture of a Geo-Distributed SQL Database
CockroachDB: Architecture of a Geo-Distributed SQL Database
 
Algebird : Abstract Algebra for big data analytics. Devoxx 2014
Algebird : Abstract Algebra for big data analytics. Devoxx 2014Algebird : Abstract Algebra for big data analytics. Devoxx 2014
Algebird : Abstract Algebra for big data analytics. Devoxx 2014
 
C* Summit 2013: Cassandra at Instagram by Rick Branson
C* Summit 2013: Cassandra at Instagram by Rick BransonC* Summit 2013: Cassandra at Instagram by Rick Branson
C* Summit 2013: Cassandra at Instagram by Rick Branson
 
Node collaboration - sharing information between your systems
Node collaboration - sharing information between your systemsNode collaboration - sharing information between your systems
Node collaboration - sharing information between your systems
 
S3, Cassandra or Outer Space? Dumping Time Series Data using Spark - Demi Ben...
S3, Cassandra or Outer Space? Dumping Time Series Data using Spark - Demi Ben...S3, Cassandra or Outer Space? Dumping Time Series Data using Spark - Demi Ben...
S3, Cassandra or Outer Space? Dumping Time Series Data using Spark - Demi Ben...
 

Destacado

Summer School Scale Cloud Across the Enterprise
Summer School   Scale Cloud Across the EnterpriseSummer School   Scale Cloud Across the Enterprise
Summer School Scale Cloud Across the Enterprise
WSO2
 
Simplifying The Cloud Top 10 Questions By SMBs
Simplifying The Cloud Top 10 Questions By SMBsSimplifying The Cloud Top 10 Questions By SMBs
Simplifying The Cloud Top 10 Questions By SMBs
Sun Digital, Inc.
 
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud ComputingLinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
Mark Hinkle
 
Linthicum what is-the-true-future-of-cloud-computing
Linthicum what is-the-true-future-of-cloud-computingLinthicum what is-the-true-future-of-cloud-computing
Linthicum what is-the-true-future-of-cloud-computing
David Linthicum
 
Best Practices for Architecting in the Cloud - Jeff Barr
Best Practices for Architecting in the Cloud - Jeff BarrBest Practices for Architecting in the Cloud - Jeff Barr
Best Practices for Architecting in the Cloud - Jeff Barr
Amazon Web Services
 

Destacado (20)

Big Data — Your new best friend
Big Data — Your new best friendBig Data — Your new best friend
Big Data — Your new best friend
 
Functional Python Webinar from October 22nd, 2014
Functional Python Webinar from October 22nd, 2014Functional Python Webinar from October 22nd, 2014
Functional Python Webinar from October 22nd, 2014
 
What can Ruby learn from Python (and vice versa)?
What can Ruby learn from Python (and vice versa)?What can Ruby learn from Python (and vice versa)?
What can Ruby learn from Python (and vice versa)?
 
PostgreSQL, your NoSQL database
PostgreSQL, your NoSQL databasePostgreSQL, your NoSQL database
PostgreSQL, your NoSQL database
 
Python's magic methods
Python's magic methodsPython's magic methods
Python's magic methods
 
Technical training business talk.key
Technical training business talk.keyTechnical training business talk.key
Technical training business talk.key
 
2013 State of Cloud Survey SMB Results
2013 State of Cloud Survey SMB Results2013 State of Cloud Survey SMB Results
2013 State of Cloud Survey SMB Results
 
Breaking through the Clouds
Breaking through the CloudsBreaking through the Clouds
Breaking through the Clouds
 
2013 Future of Cloud Computing - 3rd Annual Survey Results
2013 Future of Cloud Computing - 3rd Annual Survey Results2013 Future of Cloud Computing - 3rd Annual Survey Results
2013 Future of Cloud Computing - 3rd Annual Survey Results
 
Intro to cloud computing — MegaCOMM 2013, Jerusalem
Intro to cloud computing — MegaCOMM 2013, JerusalemIntro to cloud computing — MegaCOMM 2013, Jerusalem
Intro to cloud computing — MegaCOMM 2013, Jerusalem
 
Can we hack open source #cloud platforms to help reduce emissions?
Can we hack open source #cloud platforms to help reduce emissions?Can we hack open source #cloud platforms to help reduce emissions?
Can we hack open source #cloud platforms to help reduce emissions?
 
Summer School Scale Cloud Across the Enterprise
Summer School   Scale Cloud Across the EnterpriseSummer School   Scale Cloud Across the Enterprise
Summer School Scale Cloud Across the Enterprise
 
Simplifying The Cloud Top 10 Questions By SMBs
Simplifying The Cloud Top 10 Questions By SMBsSimplifying The Cloud Top 10 Questions By SMBs
Simplifying The Cloud Top 10 Questions By SMBs
 
Penetrating the Cloud: Opportunities & Challenges for Businesses
Penetrating the Cloud: Opportunities & Challenges for BusinessesPenetrating the Cloud: Opportunities & Challenges for Businesses
Penetrating the Cloud: Opportunities & Challenges for Businesses
 
The Inevitable Cloud Outage
The Inevitable Cloud OutageThe Inevitable Cloud Outage
The Inevitable Cloud Outage
 
Avoiding Cloud Outage
Avoiding Cloud OutageAvoiding Cloud Outage
Avoiding Cloud Outage
 
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud ComputingLinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
LinuxFest NW 2013: Hitchhiker's Guide to Open Source Cloud Computing
 
Delivering IaaS with Open Source Software
Delivering IaaS with Open Source SoftwareDelivering IaaS with Open Source Software
Delivering IaaS with Open Source Software
 
Linthicum what is-the-true-future-of-cloud-computing
Linthicum what is-the-true-future-of-cloud-computingLinthicum what is-the-true-future-of-cloud-computing
Linthicum what is-the-true-future-of-cloud-computing
 
Best Practices for Architecting in the Cloud - Jeff Barr
Best Practices for Architecting in the Cloud - Jeff BarrBest Practices for Architecting in the Cloud - Jeff Barr
Best Practices for Architecting in the Cloud - Jeff Barr
 

Similar a Rails israel 2013

Lessons learned while building Omroep.nl
Lessons learned while building Omroep.nlLessons learned while building Omroep.nl
Lessons learned while building Omroep.nl
bartzon
 
mloc.js 2014 - JavaScript and the browser as a platform for game development
mloc.js 2014 - JavaScript and the browser as a platform for game developmentmloc.js 2014 - JavaScript and the browser as a platform for game development
mloc.js 2014 - JavaScript and the browser as a platform for game development
David Galeano
 

Similar a Rails israel 2013 (20)

Rails Tips and Best Practices
Rails Tips and Best PracticesRails Tips and Best Practices
Rails Tips and Best Practices
 
Metarhia: Node.js Macht Frei
Metarhia: Node.js Macht FreiMetarhia: Node.js Macht Frei
Metarhia: Node.js Macht Frei
 
Building a Complex, Real-Time Data Management Application
Building a Complex, Real-Time Data Management ApplicationBuilding a Complex, Real-Time Data Management Application
Building a Complex, Real-Time Data Management Application
 
Intro to Databases
Intro to DatabasesIntro to Databases
Intro to Databases
 
Asynchronous single page applications without a line of HTML or Javascript, o...
Asynchronous single page applications without a line of HTML or Javascript, o...Asynchronous single page applications without a line of HTML or Javascript, o...
Asynchronous single page applications without a line of HTML or Javascript, o...
 
Postgres Vienna DB Meetup 2014
Postgres Vienna DB Meetup 2014Postgres Vienna DB Meetup 2014
Postgres Vienna DB Meetup 2014
 
Lessons learned while building Omroep.nl
Lessons learned while building Omroep.nlLessons learned while building Omroep.nl
Lessons learned while building Omroep.nl
 
Lessons learned while building Omroep.nl
Lessons learned while building Omroep.nlLessons learned while building Omroep.nl
Lessons learned while building Omroep.nl
 
Data herding
Data herdingData herding
Data herding
 
Data herding
Data herdingData herding
Data herding
 
Supersized PostgreSQL: Postgres-XL for Scale-Out OLTP and Big Data Analytics
Supersized PostgreSQL: Postgres-XL for Scale-Out OLTP and Big Data AnalyticsSupersized PostgreSQL: Postgres-XL for Scale-Out OLTP and Big Data Analytics
Supersized PostgreSQL: Postgres-XL for Scale-Out OLTP and Big Data Analytics
 
Scaling tappsi
Scaling tappsiScaling tappsi
Scaling tappsi
 
MongoDB Days Silicon Valley: Winning the Dreamforce Hackathon with MongoDB
MongoDB Days Silicon Valley: Winning the Dreamforce Hackathon with MongoDBMongoDB Days Silicon Valley: Winning the Dreamforce Hackathon with MongoDB
MongoDB Days Silicon Valley: Winning the Dreamforce Hackathon with MongoDB
 
CDC to the Max!
CDC to the Max!CDC to the Max!
CDC to the Max!
 
MapReduce on Zero VM
MapReduce on Zero VM MapReduce on Zero VM
MapReduce on Zero VM
 
Migrating To PostgreSQL
Migrating To PostgreSQLMigrating To PostgreSQL
Migrating To PostgreSQL
 
Cassandra Day SV 2014: Scaling Hulu’s Video Progress Tracking Service with Ap...
Cassandra Day SV 2014: Scaling Hulu’s Video Progress Tracking Service with Ap...Cassandra Day SV 2014: Scaling Hulu’s Video Progress Tracking Service with Ap...
Cassandra Day SV 2014: Scaling Hulu’s Video Progress Tracking Service with Ap...
 
Michael Hall [InfluxData] | Become an InfluxDB Pro in 20 Minutes | InfluxDays...
Michael Hall [InfluxData] | Become an InfluxDB Pro in 20 Minutes | InfluxDays...Michael Hall [InfluxData] | Become an InfluxDB Pro in 20 Minutes | InfluxDays...
Michael Hall [InfluxData] | Become an InfluxDB Pro in 20 Minutes | InfluxDays...
 
mloc.js 2014 - JavaScript and the browser as a platform for game development
mloc.js 2014 - JavaScript and the browser as a platform for game developmentmloc.js 2014 - JavaScript and the browser as a platform for game development
mloc.js 2014 - JavaScript and the browser as a platform for game development
 
Introduction to Elixir
Introduction to ElixirIntroduction to Elixir
Introduction to Elixir
 

Más de Reuven Lerner

Más de Reuven Lerner (14)

Web APIs: The future of software
Web APIs: The future of softwareWeb APIs: The future of software
Web APIs: The future of software
 
PostgreSQL
PostgreSQLPostgreSQL
PostgreSQL
 
Rails traps
Rails trapsRails traps
Rails traps
 
Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...
Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...
Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...
 
Rails development environment talk
Rails development environment talkRails development environment talk
Rails development environment talk
 
Git talk from Open 2011 conference in Israel
Git talk from Open 2011 conference in IsraelGit talk from Open 2011 conference in Israel
Git talk from Open 2011 conference in Israel
 
Dynamic languages, for software craftmanship group
Dynamic languages, for software craftmanship groupDynamic languages, for software craftmanship group
Dynamic languages, for software craftmanship group
 
Modern Web Technologies — Jerusalem Web Professionals, January 2011
Modern Web Technologies — Jerusalem Web Professionals, January 2011Modern Web Technologies — Jerusalem Web Professionals, January 2011
Modern Web Technologies — Jerusalem Web Professionals, January 2011
 
PostgreSQL talk, Database 2011 conference
PostgreSQL talk, Database 2011 conferencePostgreSQL talk, Database 2011 conference
PostgreSQL talk, Database 2011 conference
 
ActiveRecord 2.3
ActiveRecord 2.3ActiveRecord 2.3
ActiveRecord 2.3
 
Ruby objects
Ruby objectsRuby objects
Ruby objects
 
Rails console
Rails consoleRails console
Rails console
 
Rails tools
Rails toolsRails tools
Rails tools
 
Why ruby and rails
Why ruby and railsWhy ruby and rails
Why ruby and rails
 

Último

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Último (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 

Rails israel 2013

  • 1. Integrating PostgreSQL Reuven M. Lerner • reuven@lerner.co.il Rails Israel Conference 2013 October 9th, 2013 • Tel Aviv 1
  • 2. Who am I? • Web developer since 1993 • Linux Journal columnist since 1996 • PostgreSQL user since at least 1999 • Ruby/Rails developer, teacher since 2005 • PhD candidate in learning sciences at Northwestern University 2
  • 3. But before all of that... I was a kid. 3
  • 4. (Yes, a geeky kid.) But before all of that... I was a kid. 3
  • 5. 4
  • 6. 5
  • 7. 6
  • 8. 7
  • 10. Er, I did. And do. • Whadaya know? Databases are useful! • They offer an abstraction layer for our data • I don’t care how it is stored, so long as the data is safe, and I can get it back 9
  • 11. What is a database? Database Store data confidently Retrieve data flexibly 10
  • 12. Relational databases • Client-server • All data is stored in two-dimensional tables • Client-server communication is in SQL • SQL is standard. Each database implements a superset of a subset of that standard. 11
  • 13. “Impedance mismatch” • You basically couldn’t find two languages that are more different from one another than Ruby and SQL: • Ruby — object oriented, imperative • SQL — table oriented, declarative • Mixing them is ugly, as we all know 12
  • 14. ActiveRecord! • ActiveRecord is the default ORM (object- relational manager) in Rails • ActiveRecord translates our Ruby method calls to SQL queries • Query results are turned into Ruby objects and collections 13
  • 15. The good news... • ActiveRecord papers over the differences between databases! 14
  • 16. ... and the bad news • ActiveRecord papers over the differences between databases! 15
  • 17. The really bad news • This means we’re: • Writing extra code • Slowing down our apps • Putting logic in the wrong place • Database logic shouldn’t be in your app (just like SQL shouldn’t be in your HTML) 16
  • 24. PostgreSQL • Very fast, very scalable. • Amazingly flexible, easily extensible. • Rock-solid — no crashes, corruption, major security issues for years • Ridiculously easy administration • It also happens to be free (MIT/BSD) 18
  • 25. Brief history • Ingres (Stonebreaker, Berkeley) • Postgres (Stonebreaker, Berkeley) • PostgreSQL project = Postgres + SQL • About one major release per year • Latest and greatest is 9.3 19
  • 26. Features! • MVCC • Custom data types • CTEs • Functional, partial indexes • Full-text search • Server-side functions 20
  • 27. Rails 4 to the rescue! • As of Rails 4, many PostgreSQL features are supported, out of the box, by ActiveRecord • The best of both worlds:Amazing database features, and we don’t have to use SQL! 21
  • 28. But wait! • What if I want to switch to another database? • Won’t I lose the platform independence? • Yes, you will. • Fortunately, this will almost certainly not happen. • And if it does, it’ll be super-painful anyway. 22
  • 29. Wait again! • This means that I can’t use SQLite in development, and PostgreSQL on the server. • Yes, that’s right. • You should use the same database in development and production. Please. 23
  • 30. Data types • Databases are like programming languages: The right choice of data type can have huge consequences in speed, size, and flexibility • PostgreSQL has some special ones that can really come in handy! 24
  • 31. Network types • PostgreSQL supports INET, CIDR, and MACADDR types • If you’re ever storing network addresses, you should use these, not a text type! • Savings in space (and time), and faster comparisons (numeric vs. textual) 25
  • 32. CREATE TABLE Logins ( id SERIAL PRIMARY KEY, local_ip INET NOT NULL, remote_ip INET NOT NULL); INSERT INTO Logins (local_ip, remote_ip) values ('192.168.1.1', '10.1.2.3'); reuven=# INSERT INTO Logins (local_ip, remote_ip) values ('999.999.999.1000', '10.1.2.3'); ERROR: invalid input syntax for type inet: "999.999.999.1000" LINE 1: INSERT INTO Logins (local_ip, remote_ip) values ('999.999.99... 26
  • 33. Rails 4 migrations $ rails g model login local_ip:inet remote_ip:inet class CreateLogins < ActiveRecord::Migration def change create_table :logins do |t| t.inet :local_ip, nulls: :false t.inet :remote_ip, nulls: :false t.timestamps end end end 27
  • 34. Rails 4 app Login.create!(local_ip:'192.168.1.1', remote_ip:'10.1.2.3') Login.create!(local_ip:'192.168.1.1000', remote_ip:'10.1.2.3') IPAddr::InvalidAddressError: invalid address i = Login.first.local_ip i.class => IPAddr i.ipv4? => true i.ipv6? => false 28
  • 35. Managing a network? • We also have CIDR and MAC addresses! Netconfig.create!(net: '192.168.1.1/255.255.255.0') 29
  • 36. UUIDs CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE TABLE evil_companies ( id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v1(), name TEXT); INSERT INTO evil_companies (name) VALUES ('HOT Internet'); INSERT INTO evil_companies (name) VALUES ('HOT TV'); INSERT INTO evil_companies (name) VALUES ('HOT Telephone'); INSERT INTO evil_companies (name) VALUES ('Voldemort, Ltd.'); 30
  • 37. Wait... extensions? • Yes, PostgreSQL has extensions! • Download and install them from PGXN • (Think of them as gems for PostgreSQL) • Some, like uuid-ossp, come by default • Activate them with CREATE EXTENSION 31
  • 38. 32
  • 39. With this in place... SELECT * FROM evil_companies ; ┌──────────────────────────────────────┬─────────────────┐ │ id │ name │ ├──────────────────────────────────────┼─────────────────┤ │ da7a333a-3069-11e3-b4fd-28cfe91f81e7 │ HOT Internet │ │ dc480be2-3069-11e3-a0f4-28cfe91f81e7 │ HOT TV │ │ ddadabfe-3069-11e3-af1d-28cfe91f81e7 │ HOT Telephone │ │ 4c33a128-306a-11e3-8000-28cfe91f81e7 │ Voldemort, Ltd. │ └──────────────────────────────────────┴─────────────────┘ 33
  • 40. Migrations # Requires some manual tinkering class CreateEvilCompanies < ActiveRecord::Migration def change enable_extension "uuid-ossp" create_table :evil_companies, id: :uuid do |t| t.text :name t.timestamps end end end 34
  • 41. Migrations # Requires some manual tinkering class CreateEvilCompanies < ActiveRecord::Migration def change enable_extension "uuid-ossp" create_table :evil_companies, id: :uuid do |t| t.text :name t.timestamps end end end 34
  • 42. In the app EvilCompany.create!(name: 'HOT Internet') EvilCompany.first.id => "07d3f537-c008-4006-9f2b-0592c7df3ebf" 35
  • 43. JSON • Remember the “impedance mismatch” between objects and databases? • Now we have another one — we get data in JSON, turn it into database objects, and then turn it back into JSON • But now PostgreSQL can store, retrieve, and query JSON! 36
  • 44. CREATE TABLE bad_service ( id SERIAL, info JSON ); INSERT INTO bad_service (info) values ('{"company":"HOT Internet", "badness":9}'); INSERT INTO bad_service (info) VALUES ('la la la'); ERROR: invalid input syntax for type json LINE 1: insert into bad_service (info) values ('la la la'); ^ DETAIL: Token "la" is invalid. CONTEXT: JSON data, line 1: la... 37
  • 45. JSON operators! • As of PostgreSQL 9.3, we get operators • Work with JSON, much like XML: SELECT info#>'{company}' from bad_service; ┌────────────────┐ │ ?column? │ ├────────────────┤ │ "HOT Internet" │ └────────────────┘ 38
  • 46. Migration $ rails g model bad_service info:json class CreateBadServices < ActiveRecord::Migration def change create_table :bad_services do |t| t.json :info t.timestamps end end end 39
  • 47. App BadService.create!(info: '{"company":"HOT Internet"}') b = BadService.first b.info.class => Hash b.info['company'] => "HOT Internet" 40
  • 48. Ranges CREATE TABLE forecast ( forecast_for DATE, hi_lo int4range); INSERT INTO forecast (forecast_for, hi_lo) VALUES ('8- oct-2013', '[19,29]'); INSERT INTO forecast (forecast_for, hi_lo) VALUES ('9- oct-2013', '[20,32]'); INSERT INTO forecast (forecast_for, hi_lo) VALUES ('10- oct-2013', '[19,28]'); 41
  • 49. SELECT * FROM forecast ; ┌──────────────┬─────────┐ │ forecast_for │ hi_lo │ ├──────────────┼─────────┤ │ 2013-10-08 │ [19,30) │ │ 2013-10-09 │ [20,33) │ │ 2013-10-10 │ [19,29) │ └──────────────┴─────────┘ SELECT * FROM forecast WHERE hi_lo @> 19; ┌──────────────┬─────────┐ │ forecast_for │ hi_lo │ ├──────────────┼─────────┤ │ 2013-10-08 │ [19,30) │ │ 2013-10-10 │ [19,29) │ └──────────────┴─────────┘ 42
  • 50. Migration $ model forecast forecast_for:date hi_lo:int4range class CreateForecasts < ActiveRecord::Migration def change create_table :forecasts do |t| t.date :forecast_for t.int4range :hi_lo t.timestamps end end end 43
  • 51. Ranges in apps Forecast.create!(forecast_for: Time.now, hi_lo: 20..30) Forecast.first.hi_lo => 20...31 Forecast.first.hi_lo.class Range 44
  • 52. Arrays! CREATE TABLE posts ( body text, tags text[] ); INSERT INTO posts (body, tags) VALUES ('Hello, world', '{intro, hello}'); INSERT INTO posts (body, tags) VALUES ('Hello again', '{DRY, boring, hello}'); 45
  • 53. SELECT * FROM posts; ┌──────────────┬────────────────────┐ │ body │ tags │ ├──────────────┼────────────────────┤ │ Hello, world │ {intro,hello} │ │ Hello again │ {DRY,boring,hello} │ └──────────────┴────────────────────┘ SELECT * from posts where 'intro' = ANY(tags); ┌──────────────┬───────────────┐ │ body │ tags │ ├──────────────┼───────────────┤ │ Hello, world │ {intro,hello} │ └──────────────┴───────────────┘ 46
  • 54. Arrays in migrations $ rails g model post body:text tags:text class CreatePosts < ActiveRecord::Migration def change create_table :posts do |t| t.text :body t.text :tags, array: true t.timestamps end end end 47
  • 55. Arrays in your app Post.create!(body: 'First post!', tags: %w(first second third)) Post.first.tags => ["first", "second", "third"] 48
  • 56. Normalization = DRY • Everyone loves to talk about tagging as a great example of PostgreSQL arrays • Um... don’t forget about normalization, the DRY of database design • I know, it’s not cool to talk about it in the Age of NoSQL. But it really does work. 49
  • 57. Premature denormalization is the root of all database evil. 50
  • 58. Hstore • A key-value storage system in PostgreSQL! • Any column can be defined as hstore • Then you can query it — and you get a hash back! • It’s sort of like Redis or memcached, but integrated into (and backed by) Postgres! 51
  • 59. PostgreSQL CREATE EXTENSION hstore; CREATE TABLE posts2 ( body text, tags hstore ); INSERT INTO posts2 (body, tags) VALUES ('Hello, there', '"hello" => 2, "boring" => 10'); INSERT INTO posts2 (body, tags) values ('Hello again', '"DRY" => 5, "hello" => 2, "boring" => 3'); 52
  • 60. SELECT * from posts2 where defined(tags, 'hello'); ┌──────────────┬─────────────────────────────────────────┐ │ body │ tags │ ├──────────────┼─────────────────────────────────────────┤ │ Hello, there │ "hello"=>"2", "boring"=>"10" │ │ Hello again │ "DRY"=>"5", "hello"=>"2", "boring"=>"3" │ └──────────────┴─────────────────────────────────────────┘ (2 rows) SELECT tags -> 'boring' from posts2 ; ┌──────────┐ │ ?column? │ ├──────────┤ │ 10 │ │ 3 │ └──────────┘ (2 rows) 53
  • 61. And also... SELECT * FROM posts2 WHERE (tags -> 'boring')::integer > 5; ┌──────────────┬──────────────────────────────┐ │ body │ tags │ ├──────────────┼──────────────────────────────┤ │ Hello, there │ "hello"=>"2", "boring"=>"10" │ └──────────────┴──────────────────────────────┘ 54
  • 62. Migration $ rails g model newpost body:text tags:hstore class CreateNewposts < ActiveRecord::Migration def change enable_extension "hstore" create_table :newposts do |t| t.text :body t.hstore :tags t.timestamps end end end 55
  • 63. In your app Newpost.create!(:body => 'foo', :tags => {a:1, b:2}) Newpost.first.tags => {"a"=>"1", "b"=>"2"} # hash! Newpost.first.tags['a'] => "1" # now a string! Newpost.first.tags[:a] => nil # not WithIndifferentAccess! 56
  • 64. Indexes • PostgreSQL offers different index types • Specify these in your migrations • Partial indexes (with a WHERE clause) • Functional indexes (apply a function!) • Full-text indexes (many languages) • Geospatial indexes (install PostGIS!) 57
  • 65. Some things are still missing • “The Active Record way claims that intelligence belongs in your models, not in the database.As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.” • — Rails Guide,ActiveRecord Migrations 58
  • 67. 60
  • 68. Your data are your crown jewels! 61
  • 69. Summary • Rails good! PostgreSQL good! • Rails + PostgreSQL = Doubleplus good! • Rails 4 + PostgreSQL = Secure storage, flexible retrieval, and a lot of geeky DB fun! 62