developing and deploying apps with the postgres fdw

92
My Love of Developing with the Postgres FDW ...and how production tested those feelings. Jonathan S. Katz PGConf EU 2015 - October 30, 2015

Upload: jkatz05

Post on 15-Apr-2017

1.596 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Developing and Deploying Apps with the Postgres FDW

My Love of Developing with the Postgres FDW

...and how production tested those feelings.

Jonathan S. Katz PGConf EU 2015 - October 30, 2015

Page 2: Developing and Deploying Apps with the Postgres FDW

Hi! I'm Jonathan!

2

Page 3: Developing and Deploying Apps with the Postgres FDW

A Bit About Me• @jkatz05

• Chief Technology Officer @ VenueBook

• Using Postgres since ~2004

• Been using it decently ~2010

• One day hope to use it well ;-)

• Active Postgres community member

• Co-Chair, PGConf US

• Co-organizer, NYC PostgreSQL User Group

• Director, United States PostgreSQL Association

• Have been to every PGConf.EU except Madrid :(

3

Page 4: Developing and Deploying Apps with the Postgres FDW

PGConf US 2016April 18 - 20, New York City

http://www.pgconf.us/4

Page 5: Developing and Deploying Apps with the Postgres FDW

Disclaimer

5

I loooooove PostgreSQL

Page 6: Developing and Deploying Apps with the Postgres FDW

Disclaimer #2

6

I'm some sort of weird dev / DBA / business-person hybrid

Page 7: Developing and Deploying Apps with the Postgres FDW

Okay, done with the boilerplate. !

Let's do this.

7

Page 8: Developing and Deploying Apps with the Postgres FDW

Foreign Data Wrappers in a Nutshell

• Provide a unified interface (i.e. SQL) to access different data sources

• RDBMS (like Postgres!)

• NoSQL

• APIs (HTTP, Twitter, etc.)

• Internet of things

8

Page 9: Developing and Deploying Apps with the Postgres FDW

IMHO: This is a killer feature

9

Page 10: Developing and Deploying Apps with the Postgres FDW

History of FDWs• Released in 9.2 with a few read-only interfaces

• SQL-MED

• Did not include Postgres :(

• 9.3: Writeable FDWs

• ...and did include Postgres :D

• 9.4: Considers triggers on foreign tables

• 9.5

• IMPORT FOREIGN SCHEMA

• Push Down API (WIP)

• Inheritance children

10

Page 11: Developing and Deploying Apps with the Postgres FDW

Not Going Anywhere• 9.6

• Join Push Down

• Aggregate API?

• Parallelism?

• "Hey we need some data from you, we will check back later"

11

Page 12: Developing and Deploying Apps with the Postgres FDW

So I was just waiting for a good problem to solve

with FDWs

12

Page 13: Developing and Deploying Apps with the Postgres FDW

And then a couple of them came.

13

Page 14: Developing and Deploying Apps with the Postgres FDW

Some Background

14

VenueBook is revolutionizing the way people think about event booking. Our platform lets venues and bookers plan together, creating a smarter and better-

connected experience for all. We simplify planning, so you can have more fun!

Page 15: Developing and Deploying Apps with the Postgres FDW

Translation

• We have two main products:

• A CRM platform that allows venue managers to control everything around an event.

• A marketplace that allows event planners source venues and book events.

15

Page 16: Developing and Deploying Apps with the Postgres FDW

Further Translation

16

There are a lot of moving pieces with our data.

Page 17: Developing and Deploying Apps with the Postgres FDW

So The Following Conversation Happend

17

Page 18: Developing and Deploying Apps with the Postgres FDW

18

Hey, can we build an API?

Sure, but I would want to run it as a separate application so that way we can

isolate the load from our primary database.

Okay, that makes sense.

Great. There is a feature in Postgres that makes it easy to talk between two separate Postgres databases, so it shouldn't be too difficult to build.

That sounds good. Let's do it!

There's one catch...

Page 19: Developing and Deploying Apps with the Postgres FDW

This could be a bit experimental...

19

Page 20: Developing and Deploying Apps with the Postgres FDW

I want to experiment with this thing called a "Foreign Data Wrapper" but it should make maintenance

easier overall.

20

Page 21: Developing and Deploying Apps with the Postgres FDW

"OK"

21

Page 22: Developing and Deploying Apps with the Postgres FDW
Page 23: Developing and Deploying Apps with the Postgres FDW

Assumptions

• We are running PostgreSQL 9.4

• The schema I'm working with is slightly contrived for the purposes of demonstration

23

Page 24: Developing and Deploying Apps with the Postgres FDW

So, let's build something in our development

environment

24

Page 25: Developing and Deploying Apps with the Postgres FDW

25

local:app jkatz$ createuser!Enter name of role to add: jkatz!Shall the new role be a superuser? (y/n) y

Yeah, of course I want superuser

Page 26: Developing and Deploying Apps with the Postgres FDW

26

# "local" is for Unix domain socket connections only!local all all trust

Yeah, of course I don't care about authentication settings.

(Pro-tip: "trust" means user privileges don't matter)

Page 27: Developing and Deploying Apps with the Postgres FDW

27

local:app jkatz$ createdb app

Let's pretend this is how I created the main database.

Page 28: Developing and Deploying Apps with the Postgres FDW

28

CREATE TABLE venues ( id serial PRIMARY KEY, name varchar(255) NOT NULL ); !CREATE TABLE events ( id serial PRIMARY KEY, venue_id int REFERENCES venues (id), name text NOT NULL, total int NOT NULL DEFAULT 0, guests int NOT NULL, start_time timestamptz NOT NULL, end_time timestamptz NOT NULL, created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL );

And let's pretend this is how I created the schema for it.

Page 29: Developing and Deploying Apps with the Postgres FDW

29

And this magic function to check for availability.

CREATE FUNCTION get_availability( venue_id int, start_time timestamptz, end_time timestamptz ) RETURNS bool AS $$ SELECT NOT EXISTS( SELECT 1 FROM events WHERE events.venue_id = $1 AND ($2, $3) OVERLAPS (events.start_time, events.end_time) LIMIT 1 ); $$ LANGUAGE SQL STABLE;

Page 30: Developing and Deploying Apps with the Postgres FDW

30

local:app jkatz$ createdb api

So let's make the API schema

Page 31: Developing and Deploying Apps with the Postgres FDW

31

CREATE SCHEMA api;

We are going to be a bit smarter about how we organize the code.

Page 32: Developing and Deploying Apps with the Postgres FDW

32

CREATE TABLE api.users ( id serial PRIMARY KEY, key text UNIQUE NOT NULL, name text NOT NULL ); !CREATE TABLE api.venues ( id serial PRIMARY KEY, remote_venue_id int NOT NULL ); !CREATE TABLE api.events ( id serial PRIMARY KEY, user_id int REFERENCES api.users (id) NOT NULL, venue_id int REFERENCES api.venues (id) NOT NULL, remote_bid_id text, ip_address text, data json, created_at timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL );

Our API schema

Page 33: Developing and Deploying Apps with the Postgres FDW

33

CREATE EXTENSION postgres_fdw; !CREATE SERVER app_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'app'); !CREATE USER MAPPING FOR CURRENT_USER SERVER app_server;

Our setup to pull the information from the main application

Page 34: Developing and Deploying Apps with the Postgres FDW

34

CREATE SCHEMA app; !CREATE FOREIGN TABLE app.venues ( id int, name text ) SERVER app_server OPTIONS (table_name 'venues');

We will isolate the foreign tables in their own schema

Page 35: Developing and Deploying Apps with the Postgres FDW

35

SELECT * FROM app.venues;

So that means this returns...

Page 36: Developing and Deploying Apps with the Postgres FDW

36

SELECT * FROM app.venues;

ERROR: relation "app.venues" does not exist CONTEXT: Remote SQL command: SELECT id, name FROM app.venues

Page 37: Developing and Deploying Apps with the Postgres FDW

37

...what?

Page 38: Developing and Deploying Apps with the Postgres FDW

38

CREATE FOREIGN TABLE app.venues ( id int, name text ) SERVER app_server OPTIONS ( table_name 'venues', schema_name 'public' );

If there is a schema mismatch between local and foreign table, you have to set the schema explicitly.

Page 39: Developing and Deploying Apps with the Postgres FDW

39

SELECT * FROM app.venues;

id | name ----+-------------- 1 | Venue A 2 | Restaurant B 3 | Bar C 4 | Club D

Page 40: Developing and Deploying Apps with the Postgres FDW

40

CREATE FOREIGN TABLE app.events ( id int, venue_id int, name text, total int, guests int, start_time timestamptz, end_time timestamptz ) SERVER app_server OPTIONS ( table_name 'events', schema_name 'public' );

Adding in our foreign table for events

Page 41: Developing and Deploying Apps with the Postgres FDW

41

INSERT INTO app.events ( venue_id, name, total, guests, start_time, end_time ) VALUES ( 1, 'Conference Party', 50000, 400, '2015-10-28 18:00', '2015-10-28 21:00' ) RETURNING id;

ERROR: null value in column "id" violates not-null constraint DETAIL: Failing row contains (null, 1, Conference Party, 50000, 400, 2015-10-28 22:00:00+00, 2015-10-29 01:00:00+00, 2015-10-27 22:19:10.555695+00). CONTEXT: Remote SQL command: INSERT INTO public.events(id, venue_id, name, total, guests, start_time, end_time) VALUES ($1, $2, $3, $4, $5, $6, $7)

Page 42: Developing and Deploying Apps with the Postgres FDW

42

Huh.

Page 43: Developing and Deploying Apps with the Postgres FDW

43

Two Solutions.

Page 44: Developing and Deploying Apps with the Postgres FDW

44

Solution #1

Page 45: Developing and Deploying Apps with the Postgres FDW

45

CREATE FOREIGN TABLE app.events ( id serial NOT NULL, venue_id int, name text, total int, guests int, start_time timestamptz, end_time timestamptz ) SERVER app_server OPTIONS ( table_name 'events', schema_name 'public' );

Page 46: Developing and Deploying Apps with the Postgres FDW

46

INSERT INTO app.events ( venue_id, name, total, guests, start_time, end_time ) VALUES ( 1, 'Conference Party', 50000, 400, '2015-10-28 18:00', '2015-10-28 21:00' ) RETURNING id;

id ---- 1 (1 row)

Page 47: Developing and Deploying Apps with the Postgres FDW

WARNING• This is using a sequence on the local database

• If you do not want to generate overlapping primary keys, this is not the solution for you.

• Want to use the sequence generating function on the foreign database

• But FDWs cannot access foreign functions

• However...

47

Page 48: Developing and Deploying Apps with the Postgres FDW

48

Solution #2

Page 49: Developing and Deploying Apps with the Postgres FDW

49(on the "app" database)

CREATE SCHEMA api; !CREATE VIEW api.events_id_seq_view AS SELECT nextval('public.events_id_seq') AS id;

Page 50: Developing and Deploying Apps with the Postgres FDW

50

CREATE FOREIGN TABLE app.events_id_seq_view ( id int ) SERVER app_server OPTIONS ( table_name 'events_id_seq_view', schema_name 'api' ); !CREATE FUNCTION app.events_id_seq_nextval() RETURNS int AS $$ SELECT id FROM app.events_id_seq_view $$ LANGUAGE SQL; !CREATE FOREIGN TABLE app.events ( id int DEFAULT app.events_id_seq_nextval(), venue_id int, name text, total int, guests int, start_time timestamptz, end_time timestamptz ) SERVER app_server OPTIONS ( table_name 'events', schema_name 'public' );

(on the "api" database)

Page 51: Developing and Deploying Apps with the Postgres FDW

51

INSERT INTO app.events ( venue_id, name, total, guests, start_time, end_time ) VALUES ( 1, 'Conference Party', 50000, 400, '2015-10-28 18:00', '2015-10-28 21:00' ) RETURNING id;

id ---- 4 (1 row)

Page 52: Developing and Deploying Apps with the Postgres FDW

52

Hey, can we check the availability on the api server before making an insert on the app server?

Page 53: Developing and Deploying Apps with the Postgres FDW

53

Sure, we have a function for that on "app" but... FDWs do not support foreign functions.

!And we cannot use a view.

!However...

Page 54: Developing and Deploying Apps with the Postgres FDW

dblink• Written in 2001 by Joe Conway

• Designed to make remote PostgreSQL database calls

• The docs say:

• See also postgres_fdw, which provides roughly the same functionality using a more modern and standards-compliant infrastructure.

54

Page 55: Developing and Deploying Apps with the Postgres FDW

55

-- setup the extensions (if not already done so) CREATE EXTENSION plpgsql; CREATE EXTENSION dblink; !-- create CREATE FUNCTION app.get_availability( venue_id int, start_time timestamptz, end_time timestamptz ) RETURNS bool AS $get_availability$ DECLARE is_available bool; remote_sql text; BEGIN remote_sql := format('SELECT get_availability(%L, %L, %L)', venue_id, start_time, end_time); SELECT availability.is_available INTO is_available FROM dblink('dbname=app', remote_sql) AS availability(is_available bool); RETURN is_available; EXCEPTION WHEN others THEN RETURN NULL::bool; END; $get_availability$ LANGUAGE plpgsql;

(on the "api" database)

Page 56: Developing and Deploying Apps with the Postgres FDW

56

SELECT app.get_availability(1, '2015-10-28 18:00', '2015-10-28 20:00');

get_availability ------------------ f (1 row)

get_availability ------------------ t (1 row)

SELECT app.get_availability(1, '2015-10-28 12:00', '2015-10-28 14:00');

Works great!

Page 57: Developing and Deploying Apps with the Postgres FDW

Summary So Far...• We created two separate databases with logical schemas

• We wrote some code using postgres_fdw and dblink that can

• Read data from "app" to "api"

• Insert data from "api" to the "app"

• ...with the help of the sequence trick

• Make a remote function call

57

Page 58: Developing and Deploying Apps with the Postgres FDW

Awesome! Let's Deploy

58

Page 59: Developing and Deploying Apps with the Postgres FDW

(And because we are good developers, we are going to test the deploy

configuration in a staging environment, but we can all safely assume that, right? :-)

59

Page 60: Developing and Deploying Apps with the Postgres FDW

(Note: when I say "superuser" I mean a Postgres superuser)

60

Page 61: Developing and Deploying Apps with the Postgres FDW

61

app api

db01: 10.0.0.80

api

api01: 10.0.0.20

app

app01: 10.0.0.10

Network Topography

Page 62: Developing and Deploying Apps with the Postgres FDW

62

db01:postgresql postgres$ createdb -O app app!db01:postgresql postgres$ createdb -O app api

How we are setting things up

Page 63: Developing and Deploying Apps with the Postgres FDW

63

# TYPE DATABASE USER ADDRESS METHOD # for the main user host app app 10.0.0.10/32 md5 host api api 10.0.0.20/32 md5 # for foreign table access local api app md5 local app api md5

pg_hba.conf setup

Page 64: Developing and Deploying Apps with the Postgres FDW

64

CREATE EXTENSION postgres_fdw; CREATE EXTENSION dblink;

So we already know to run these as a supuerser on "api" right? ;-)

Page 65: Developing and Deploying Apps with the Postgres FDW

65

CREATE SERVER app_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'app');

ERROR: permission denied for foreign server app_server

But if we log in as the "api" user and try to run this...

Page 66: Developing and Deploying Apps with the Postgres FDW

66

As a superuser, grant permission

GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO api;

Page 67: Developing and Deploying Apps with the Postgres FDW

67

CREATE SERVER app_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'app'); !CREATE FOREIGN TABLE app.venues ( id int, name text ) SERVER app_server OPTIONS ( table_name 'venues', schema_name 'public' );

Now this works! Let's run a query...

Page 68: Developing and Deploying Apps with the Postgres FDW

68

SELECT * FROM app.venues;

ERROR: user mapping not found for "api"

Page 69: Developing and Deploying Apps with the Postgres FDW

69

CREATE USER MAPPING FOR api SERVER app_server OPTIONS ( user 'api', password 'test' );

So we create the user mapping and...

Page 70: Developing and Deploying Apps with the Postgres FDW

70

SELECT * FROM app.venues;

ERROR: permission denied for relation venues CONTEXT: Remote SQL command: SELECT id, name FROM public.venues

You've got to be kidding me...

Page 71: Developing and Deploying Apps with the Postgres FDW

71Go to "app" and as a superuser run this

GRANT SELECT ON venues TO api; GRANT SELECT, INSERT, UPDATE ON events TO api;

Page 72: Developing and Deploying Apps with the Postgres FDW

72

SELECT * FROM app.venues;

id | name ----+-------------- 1 | Venue A 2 | Restaurant B 3 | Bar C 4 | Club D

Meanwhile, back on "api"

Page 73: Developing and Deploying Apps with the Postgres FDW

Time to make the events work.

73

Page 74: Developing and Deploying Apps with the Postgres FDW

74

CREATE SCHEMA api; !CREATE VIEW api.events_id_seq_view AS SELECT nextval('public.events_id_seq') AS id;

Get things started on the "app" database

Page 75: Developing and Deploying Apps with the Postgres FDW

75

-- setup the sequence functionality CREATE FOREIGN TABLE app.events_id_seq_view ( id int ) SERVER app_server OPTIONS ( table_name 'events_id_seq_view', schema_name 'api' ); !CREATE FUNCTION app.events_id_seq_nextval() RETURNS int AS $$ SELECT id FROM app.events_id_seq_view $$ LANGUAGE SQL;

Back on the "api" database

Page 76: Developing and Deploying Apps with the Postgres FDW

And when we test the sequence function...

76

Page 77: Developing and Deploying Apps with the Postgres FDW

77

SELECT app.events_id_seq_nextval();

ERROR: permission denied for schema api CONTEXT: Remote SQL command: SELECT id FROM api.events_id_seq_view SQL function "events_id_seq_nextval" statement 1

Here we go again...

Page 78: Developing and Deploying Apps with the Postgres FDW

78

GRANT USAGE ON SCHEMA api TO api;

On the "app" database

Page 79: Developing and Deploying Apps with the Postgres FDW

79

SELECT app.events_id_seq_nextval();

ERROR: permission denied for relation events_id_seq_view CONTEXT: Remote SQL command: SELECT id FROM api.events_id_seq_view SQL function "events_id_seq_nextval" statement 1

On "api" - ARGH...

Page 80: Developing and Deploying Apps with the Postgres FDW

80

GRANT SELECT ON api.events_id_seq_view TO api;

On the "app" database

Page 81: Developing and Deploying Apps with the Postgres FDW

81

SELECT app.events_id_seq_nextval();

ERROR: permission denied for sequence events_id_seq CONTEXT: Remote SQL command: SELECT id FROM api.events_id_seq_view SQL function "events_id_seq_nextval" statement 1

On "api" - STILL?!?!?!?!

Page 82: Developing and Deploying Apps with the Postgres FDW

82

GRANT USAGE ON SEQUENCE events_id_seq TO api;

On the "app" database

Page 83: Developing and Deploying Apps with the Postgres FDW

83

SELECT app.events_id_seq_nextval();

And on "api" - YES!

events_id_seq_nextval ----------------------- 1

Page 84: Developing and Deploying Apps with the Postgres FDW

84

CREATE FOREIGN TABLE app.events ( id int DEFAULT app.events_id_seq_nextval(), venue_id int, name text, total int, guests int, start_time timestamptz, end_time timestamptz ) SERVER app_server OPTIONS ( table_name 'events', schema_name 'public' );

We can now create the foreign table and test the INSERT...

Page 85: Developing and Deploying Apps with the Postgres FDW

85

INSERT INTO app.events ( venue_id, name, total, guests, start_time, end_time ) VALUES ( 1, 'Conference Party', 50000, 400, '2015-10-28 18:00', '2015-10-28 21:00' ) RETURNING id;

id ---- 2

Yup...we ran "GRANT SELECT, INSERT, UPDATE ON events TO api;" on "app" earlier!

Page 86: Developing and Deploying Apps with the Postgres FDW

86

CREATE FUNCTION app.get_availability( venue_id int, start_time timestamptz, end_time timestamptz ) RETURNS bool AS $get_availability$ DECLARE is_available bool; remote_sql text; BEGIN remote_sql := format('SELECT get_availability(%L, %L, %L)', venue_id, start_time, end_time); SELECT availability.is_available INTO is_available FROM dblink('dbname=app user=api password=test', remote_sql) AS availability(is_available bool); RETURN is_available; EXCEPTION WHEN others THEN RETURN NULL::bool; END; $get_availability$ LANGUAGE plpgsql;

And install our availability function...

Page 87: Developing and Deploying Apps with the Postgres FDW

87

SELECT app.get_availability(1, '2015-10-28 18:00', '2015-10-28 20:00'); ! get_availability ------------------ f (1 row) !!SELECT app.get_availability(1, '2015-10-28 13:00', '2015-10-28 17:00'); ! get_availability ------------------ t (1 row)

...and wow.

Page 88: Developing and Deploying Apps with the Postgres FDW

WE DID IT!!!

88

Page 89: Developing and Deploying Apps with the Postgres FDW

What did we learn?

89

Page 90: Developing and Deploying Apps with the Postgres FDW

We Learned That...• PostgreSQL has a robust permission system

• http://www.postgresql.org/docs/current/static/sql-grant.html

• ...there is much more we could have done too.

• Double the databases, double the problems

• Always have a testing environment that can mimic your production environment

• ...when it all works, it is so sweet.

90

Page 91: Developing and Deploying Apps with the Postgres FDW

Conclusion• Foreign data wrappers are incredible

• The postgres_fdw is incredible

• ...and it is still a work in progress

• Make sure you understand its limitations

• Research what is required to properly install in production

91

Page 92: Developing and Deploying Apps with the Postgres FDW

Questions?

• @jkatz05

92