Fuge, an Erlang Port of GitHub's Scientist Library
来自: https://github.com/sthadka/fuge
fuge
fuge is an Erlang library for carefully refactoring critical paths. Fuge allows you to implement Branch by Abstraction in production.
Inspiration
See the excellent GitHub Scientist Post on why this pattern is useful.
Features
Current set of features supported by fuge:
- Run old code alongside one or multiple versions of new code. fuge runs all the versions of the code provided and provides results in a stable order. Only the result of the control code is returned.
- Compare the results of each version. The output of each of version executed is available for comparison.
- Get run time for each version. Run time allows us to compare and see which version of the code is faster.
- The different code versions are run in a random order. This allows us to average out the advantage/disadvantage of the code's run order.
- Subscriber model for publishing information. fuge is agnostic to how results are used afterwards and is extensible using subscribers. fuge also provides a way to maintain state for each subscriber, removing the necessity of making the subscriber a process with state.
- Allows context information for every run. Context information can allow debugging the performance difference and it is passed on to the subscribers as is.
Warning
Please keep the following in mind before using fuge in production!
- fuge is currently in alpha stage and in heavy development.
- fuge is to be run only on code without side effects and code that is idempotent.
- While fuge itself has a very low overhead (one ETS read, timing information, one gen_server cast), running multiple versions of the code in production will have a performance hit.
Usage
We try to find the sum of consecutive numbers for illustration.
% Start fuge application 1> application:start(fuge). ok % Create a new fuge 2> fuge:new(my_fuge). ok % Control code 3> Control = fun () -> lists:sum(lists:seq(1, 1000)) end. #Fun<erl_eval.20.54118792> % Candidate code 4> Candidate = fun() -> 1000 * (1000 + 1) div 2 end. #Fun<erl_eval.20.54118792> % Run the experiment 5> fuge:run(my_fuge, Control, Candidate). 500500
Output with the default logging subscriber:
=INFO REPORT==== 18-Feb-2016::07:55:22 === Fuge: {fuge,my_fuge,[fuge_subscriber_logger],[]} =INFO REPORT==== 18-Feb-2016::07:55:22 === Result: {fuge_result,undefined, {fuge_data,81,500500}, [{fuge_data,52,500500}], [0,1]}
In the "Result" portion, we can see that the first version of the code ran in 81 microseconds vs the candidate's 52 microseconds. We can also see that the value "500500" returned by both the versions is the same.
Planned Features
- Subscribers for different use cases (e.g. graphite)
Roadmap
- Tests for edge cases
- Benchmark
- Run subscribers in separate process to avoid building a queue on fuge_server.
- Maybe create an example application for clear understanding of usage.
- Add options to fine tune the run.
本文由用户 snortbit 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!