Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
/deps
erl_crash.dump
*.ez
/docs
/docs
/bench/snapshots
.env
17 changes: 17 additions & 0 deletions bench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Note about these benchmarks:

The `stream_process_bench.exs` script uses Meck to stub out the
`ExTwitter.API.Streaming.parse_tweet` function for benchmark isolation.

However, benchfella does not currently support teardown phases to undo this
stub after the completion of the bench.

Therefore, if you run them all via the default `mix bench` task (which loads and
runs all benchmarks in this directory), other benchmarks which depend on that
call will get unexpected results.

For now, just run each benchmark script you want individually directly, e.g.

mix bench bench/stream_parse_bench.exs

Sorry for the inconvenience!
1 change: 1 addition & 0 deletions bench/bench_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Benchfella.start()
22 changes: 22 additions & 0 deletions bench/deserialize_bench.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule ExTwitter.JSON.Bench do
use Benchfella
@mock_tweet_json File.read!("fixture/mocks/tweet.json")

bench "decode" do
ExTwitter.JSON.decode(@mock_tweet_json)
end
end

defmodule ExTwitter.Parser.Bench do
use Benchfella
@mock_tweet_json File.read!("fixture/mocks/tweet.json")

bench "parse_tweet", [tweet: decode_json()] do
ExTwitter.Parser.parse_tweet(tweet)
end

defp decode_json do
{:ok, tweet} = ExTwitter.JSON.decode(@mock_tweet_json)
tweet
end
end
16 changes: 16 additions & 0 deletions bench/stream_parse_bench.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule ExTwitter.API.Streaming.Parse.Bench do
use Benchfella
import ExTwitter.API.Streaming

@mock_tweet_json File.read!("fixture/mocks/tweet.json")
@mock_limit_json File.read!("fixture/mocks/limit.json")

bench "parse_tweet_message(tweet)" do
parse_tweet_message(@mock_tweet_json, receive_messages: true)
end

bench "parse_tweet_message(control)" do
parse_tweet_message(@mock_limit_json, receive_messages: true)
end

end
77 changes: 77 additions & 0 deletions bench/stream_process_bench.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
defmodule ExTwitter.API.Streaming.Process.Bench do
use Benchfella
import ExTwitter.API.Streaming

@mock_tweet_json File.read!("fixture/mocks/tweet.json")

bench "process stream - single chunk", [req_id: make_ref, m: stub_tweet_parsing!] do
streamProcessor = spawn_stream_processor(self, req_id)
send streamProcessor, {:http, {req_id, :stream, @mock_tweet_json}}
receive_processed_msgs()
end

bench "process stream - multi chunk", [req_id: make_ref, parts: msg_chunks, m: stub_tweet_parsing!] do
streamProcessor = spawn_stream_processor(self, req_id)
Enum.each(parts, fn part ->
send streamProcessor, {:http, {req_id, :stream, part}}
end)
receive_processed_msgs()
end

defp spawn_stream_processor(receiver, req_id) do
spawn(fn ->
process_stream(receiver, req_id, [])
end)
end

# block until we receive a message from the stream reader, raise exception
# if something unexpected happens
defp receive_processed_msgs do
receive do
:parsed -> :ok #mock parsed msg
{:stream, _} -> :ok #real parsed msg
_msg -> raise "got unexpected message back from stream processor!"
after 1000
-> raise "timed out waiting for stream processor!"
end
end

# stub out tweet parsing so it doesnt affect benchmark time
# this is a fairly bad way to do it, but benchfella doesnt support a global
# "setup" phase just yet.
#
# also note that since benchfella doesn't support "after" callbacks to unload
# stubs, this stays loaded, so we all benchmarks in this file should be
# considered as using the stub.
defp stub_tweet_parsing! do
try do
:meck.validate(ExTwitter.API.Streaming)
rescue
ErlangError ->
Mix.Shell.IO.info """
WARNING! We just stubbed ExTwitter.API.Streaming.parse_tweet_message
This stub will be effect until this process ends.

If you are running multiple benchmarks, these ones should be run
independently of others, you can specify the files to run directly via
`mix bench bench/filename_bench.exs` etc.
"""
:meck.new(ExTwitter.API.Streaming, [:passthrough, :no_history])
:meck.expect(
ExTwitter.API.Streaming, :parse_tweet_message,
fn(_,_) -> :parsed end
)
end
end

# fake chunked messages to emulate chunked network traffic
defp msg_chunks, do: chunkify(@mock_tweet_json, 20)
defp chunkify(msg, n) do
chars = String.graphemes(msg)
chunksize = trunc(length(chars)/n)+1

Enum.chunk(chars, chunksize, chunksize, [])
|> Enum.map(&Enum.join/1)
end

end
1 change: 1 addition & 0 deletions fixture/mocks/deleted_tweet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"delete":{"status":{"id":1234,"id_str":"1234","user_id":3,"user_id_str":"3"}}}
1 change: 1 addition & 0 deletions fixture/mocks/limit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"limit":{"timestamp_ms":"1415022747749","track":542}}
1 change: 1 addition & 0 deletions fixture/mocks/stall_warning.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"warning":{"code":"FALLING_BEHIND","message":"Your connection is falling behind and messages are being queued for delivery to you. Your queue is now over 60% full. You will be disconnected when the queue is full.","percent_full":60}}
1 change: 1 addition & 0 deletions fixture/mocks/tweet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"created_at":"Wed Mar 19 16:53:03 +0000 2014","id":446328507694845952,"id_str":"446328507694845952","text":"sample tweet text","source":"web","truncated":false,"in_reply_to_status_id":446225539796594688,"in_reply_to_status_id_str":"446225539796594688","in_reply_to_user_id":86202207,"in_reply_to_user_id_str":"86202207","in_reply_to_screen_name":"suranyami","user":{"id":507309896,"id_str":"507309896","name":"Elixir Lang","screen_name":"elixirlang","location":"","description":"The Elixir programming language that runs on the Erlang VM","url":"http:\/\/t.co\/xGmHND9luN","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/xGmHND9luN","expanded_url":"http:\/\/elixir-lang.org","display_url":"elixir-lang.org","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":2408,"friends_count":6,"listed_count":68,"created_at":"Tue Feb 28 12:31:32 +0000 2012","favourites_count":31,"utc_offset":3600,"time_zone":"Amsterdam","geo_enabled":false,"verified":false,"statuses_count":370,"lang":"en","contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"131516","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme14\/bg.gif","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme14\/bg.gif","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/1859982753\/drop_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/1859982753\/drop_normal.png","profile_link_color":"009999","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[],"symbols":[],"urls":[],"user_mentions":[{"screen_name":"suranyami","name":"Ravey Day-V Gravy","id":86202207,"id_str":"86202207","indices":[0,10]}]},"favorited":false,"retweeted":false,"lang":"en"}
8 changes: 5 additions & 3 deletions lib/extwitter/api/streaming.ex
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ defmodule ExTwitter.API.Streaming do
end
end

defp process_stream(processor, request_id, configs, acc \\ []) do
@doc false
def process_stream(processor, request_id, configs, acc \\ []) do
receive do
{:http, {request_id, :stream_start, headers}} ->
send processor, {:header, headers}
Expand All @@ -102,7 +103,7 @@ defmodule ExTwitter.API.Streaming do
is_end_of_message(part) ->
message = Enum.reverse([part|acc])
|> Enum.join("")
|> parse_tweet_message(configs)
|> __MODULE__.parse_tweet_message(configs)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note this change, which was required to get Meck working for this.

if message do
send processor, message
end
Expand All @@ -127,7 +128,8 @@ defmodule ExTwitter.API.Streaming do
defp is_empty_message(part), do: part == "\r\n"
defp is_end_of_message(part), do: part =~ ~r/\r\n$/

defp parse_tweet_message(json, configs) do
@doc false
def parse_tweet_message(json, configs) do
try do
case ExTwitter.JSON.decode(json) do
{:ok, tweet} ->
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ defmodule ExTwitter.Mixfile do
{:mock, github: "parroty/mock", only: [:dev, :test], branch: "fix"},
{:ex_doc, "~> 0.6", only: :docs},
{:earmark, "~> 0.1", only: :docs},
{:inch_ex, "~> 0.2", only: :docs}
{:inch_ex, "~> 0.2", only: :docs},
{:benchfella, github: "alco/benchfella", only: :dev}
]
end

Expand Down
3 changes: 2 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%{"earmark": {:hex, :earmark, "0.1.12"},
%{"benchfella": {:git, "git://github.com/alco/benchfella.git", "0fd092f2b20bd1b80fda95eb347ae37919f66ec2", []},
"earmark": {:hex, :earmark, "0.1.12"},
"ex_doc": {:hex, :ex_doc, "0.6.2"},
"exactor": {:hex, :exactor, "0.7.0"},
"excoveralls": {:hex, :excoveralls, "0.3.6"},
Expand Down
9 changes: 4 additions & 5 deletions test/extwitter_stream_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ defmodule ExTwitterStreamTest do
use ExUnit.Case, async: false
import Mock

@mock_tweet_json "{\"created_at\":\"Wed Mar 19 16:53:03 +0000 2014\",\"id\":446328507694845952,\"id_str\":\"446328507694845952\",\"text\":\"sample tweet text\",\"source\":\"web\",\"truncated\":false,\"in_reply_to_status_id\":446225539796594688,\"in_reply_to_status_id_str\":\"446225539796594688\",\"in_reply_to_user_id\":86202207,\"in_reply_to_user_id_str\":\"86202207\",\"in_reply_to_screen_name\":\"suranyami\",\"user\":{\"id\":507309896,\"id_str\":\"507309896\",\"name\":\"Elixir Lang\",\"screen_name\":\"elixirlang\",\"location\":\"\",\"description\":\"The Elixir programming language that runs on the Erlang VM\",\"url\":\"http:\\/\\/t.co\\/xGmHND9luN\",\"entities\":{\"url\":{\"urls\":[{\"url\":\"http:\\/\\/t.co\\/xGmHND9luN\",\"expanded_url\":\"http:\\/\\/elixir-lang.org\",\"display_url\":\"elixir-lang.org\",\"indices\":[0,22]}]},\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":2408,\"friends_count\":6,\"listed_count\":68,\"created_at\":\"Tue Feb 28 12:31:32 +0000 2012\",\"favourites_count\":31,\"utc_offset\":3600,\"time_zone\":\"Amsterdam\",\"geo_enabled\":false,\"verified\":false,\"statuses_count\":370,\"lang\":\"en\",\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"131516\",\"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme14\\/bg.gif\",\"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme14\\/bg.gif\",\"profile_background_tile\":true,\"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1859982753\\/drop_normal.png\",\"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1859982753\\/drop_normal.png\",\"profile_link_color\":\"009999\",\"profile_sidebar_border_color\":\"EEEEEE\",\"profile_sidebar_fill_color\":\"EFEFEF\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"default_profile\":false,\"default_profile_image\":false,\"following\":false,\"follow_request_sent\":false,\"notifications\":false},\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"retweet_count\":0,\"favorite_count\":0,\"entities\":{\"hashtags\":[],\"symbols\":[],\"urls\":[],\"user_mentions\":[{\"screen_name\":\"suranyami\",\"name\":\"Ravey Day-V Gravy\",\"id\":86202207,\"id_str\":\"86202207\",\"indices\":[0,10]}]},\"favorited\":false,\"retweeted\":false,\"lang\":\"en\"}\r\n"
@mock_limit_json "{\"limit\":{\"timestamp_ms\":\"1415022747749\",\"track\":542}}\r\n"
@mock_deleted_tweet "{\"delete\":{\"status\":{\"id\":1234,\"id_str\":\"1234\",\"user_id\":3,\"user_id_str\":\"3\"}}}\r\n"
@mock_stall_warning "{\"warning\":{\"code\":\"FALLING_BEHIND\",\"message\":\"Your connection is falling behind and messages are being queued for delivery to you. Your queue is now over 60% full. You will be disconnected when the queue is full.\",\"percent_full\":60}}\r\n"
@mock_tweet_json File.read!("fixture/mocks/tweet.json")
@mock_limit_json File.read!("fixture/mocks/limit.json")
@mock_deleted_tweet File.read!("fixture/mocks/deleted_tweet.json")
@mock_stall_warning File.read!("fixture/mocks/stall_warning.json")

setup_all do
ExVCR.Config.filter_url_params(true)
Expand Down Expand Up @@ -115,4 +115,3 @@ defmodule ExTwitterStreamTest do
send pid, {:http, {request_id, :stream, json}}
end
end