reactive programming with elixir and rethinkdb › static › upload › media › ...reactive...

26
Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016

Upload: others

Post on 27-Jun-2020

20 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Reactive Programming with Elixir and RethinkDBPeter Hamilton

Erlang Factory 2016

Page 2: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Let’s Build Something!

Page 3: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Which character do people tweet most?

The first tweet had 6 uses of ’t'

Page 4: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Data Transformations

Raw Tweet Analyzed Tweet

Dashboard DataDashboard

Analyzer

Aggregator

Publisher

Page 5: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Elixir Stream

tweet_stream      |>  Stream.map(&analyze_tweet/1)      |>  Stream.scan(initial_dashboard,  &update_dashboard/2)      |>  Stream.each(&broadcast/1)      |>  Stream.run

Problems?

Page 6: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

RethinkDBThe open-source database

for the realtime web

Page 7: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

ReQLtable(“people”)      |>  filter(%{name:  “John”})  

table(“people”)      |>  filter(lambda  fn  (person)  -­‐>          person[:age]  >  count(person[:name])      end)  

table(“people”)      |>  merge(lambda  fn  (person)  -­‐>          if  (person[:age]  <  13)  do              %{name:  “PRIVATE”}          else              %{}          end      end)

Page 8: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

More ReQL#  Get  all  posts,  include  author  

table(“posts”)      |>  eq_join(“author_id”,  table(“people”))      |>  zip    

#  Get  people  and  any  posts  they  might  have  written  

table(“people”)      |>  merge(lambda  fn  (person)  -­‐>          posts  =  table(“posts”)              |>  filter(%{author_id:  person[:id]})              |>  coerce_to(:array)          %{posts:  posts}      end)  

Page 9: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Even More ReQL#  Count  posts  per  author  

table(“posts”)      |>  group(“author_id”)      |>  count    

#  Word  count  per  author  

table(“posts”)      |>  group(“author_id”)      |>  map(lambda  &(count(&1[:post]))      |>  sum  

Page 10: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

The Most ReQL#  Get  counts  of  frequent/occasional  authors  (including  staff)  

table(“people”)      |>  group(lambda  fn  (person)  -­‐>          post_count  =  table(“posts”)              |>  filter(%{author_id:  person[:id]})              |>  count          (post_count  <  20)              |>  coerce_to(:string)      end)      |>  map(lambda  fn  (author)  -­‐>          1  +  count(author[:staff])      end)      |>  sum      |>  ungroup      |>  map(&values(&1))      |>  coerce_to(:object)      |>  map(lambda  fn  (obj)  -­‐>          %{frequent:  obj[“true”],  occasional:  obj[“false”]}      end)

Page 11: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

LimitationsACID?

CAP?

Test results: aphyr.com and Jepson tests

Page 12: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Let’s Use RethinkDB!

Page 13: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Elixir Stream

tweet_stream      |>  Stream.map(&analyze_tweet/1)      |>  Stream.scan(initial_dashboard,  &update_dashboard/2)      |>  Stream.each(&broadcast/1)      |>  Stream.run

Our Original

Page 14: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Restructure with RethinkDBRethinkDB

Tweet Streamer Tweet Analyzer Tweet Aggregator

Dashboard Publisher

Page 15: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

RethinkDB.ChangefeedOTP Behaviour

use RethinkDB.Changefeed

Define the query, database and initial state

init(opts) :: {:subscribe, query, db, state} | {:stop, reason}

Process updates

handle_update(update, state) :: {:next, state} | {:stop, reason, state}

Plus GenServer callbacks

Page 16: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Tweet Streamer

def  start_link(topic)  do        ExTwitter.stream_filter(track:  topic)            |>  Stream.each(fn  el  -­‐>                table("tweets")                    |>  insert(%{text:  el.text,  state:  “RAW"})                    |>  run        end)  |>  StreamRunner.start_linkend

Page 17: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Tweet Analyzer (Part 1)

def  init(_opts)  do        query  =  table("tweets")            |>  filter(%{state:  "RAW"})            |>  changes(include_initial:  true)        {:subscribe,  query,  DB,  nil}end

Page 18: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Tweet Analyzer (Part 2)def  handle_update(data,  _state)  do        Enum.each(fn                %{“new_val”  =>  el,  “old_val”  =>  nil}  -­‐>                        most_used_char  =  get_most_used_char(el)                        table(“tweets”)                            |>  get(el[“id”])                            |>  update(lambda  fn  (tweet)  -­‐>                                if  (tweet[“state”]  ==  “RAW”)  do                                    %{state:  “PROCESSED”,  most_used_char:  most_used_char}                                else                                    %{}                                end                            end)  |>  run        end)        {:next,  nil}end

Page 19: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Tweet Aggregator (Part 1)

def  init(_opts)  do        query  =  table("tweets")            |>  filter(%{state:  "PROCESSED"})            |>  changes(include_initial:  true)        {:subscribe,  query,  DB,  nil}end

Page 20: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Tweet Aggregator (Part 2)

def  handle_update(data,  _state)  do        Enum.each(data,  fn                %{“new_val”  =>  el,  “old_val”  =>  nil}  -­‐>                        update_tweet_state(“PROCESSED”,  “RECORDED”)                        char  =  el[“most_used_char”]                        table(“dashboard”)                            |>  get(“character_dashboard”)                            |>  update(lambda  fn  (dashboard)  -­‐>                                %{char  =>  default(dashboard[char],  0)  +1}                        end)  |>  run        end)        {:next,  nil}end

Page 21: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Dashboard Publisher (Part 1)

def  init(_opts)  do        query  =  table("dashboards")            |>  get(“character_dashboard”)            |>  changes(include_initial:  true,  squash:  true)        {:subscribe,  query,  DB,  nil}end

Page 22: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Dashboard Publisher (Part 2)

def  handle_update(%{"new_val"  =>  val},  _state)  do        process_dashboard(val)        |>  publish_dashboard        {:next,  nil}end

Page 23: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Is it better?

Pros

Page 24: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Is it better?

Cons

Page 25: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Rethinking Your Designs

Page 26: Reactive Programming with Elixir and RethinkDB › static › upload › media › ...Reactive Programming with Elixir and RethinkDB Peter Hamilton Erlang Factory 2016 Let’s Build

Thank you!

Please reach out with any questions!

github: hamiltop/rethinkdb-elixir

slack: #rethinkdb on elixir-lang

email: [email protected]

twitter: hamiltop

Special thanks to Annie Ruygt at RethinkDB for the artwork!