erl-env

Github TagBuild StatuslicenseHex.pmHex.pm Downloads

Goal

eenv makes retrieving configuration parameters used by application faster and more stable then application:get_env. In a general way, the value of a configuration parameter is retrieved by calling application:get_env which caches params in the ac_tab ETS table, but the speed of reading ets table grows more and more slowly as concurrent volumes grows. eenv translates the ETS data into a dynamically constructed and loaded Erlang module. This can significantly reduce latency. PS: It is only for the situation which needs high reading concurrency and low writing needs.

Rationale

Application stores configuration in ac_tab ETS with {{env, AppName, Key}, Value}, application:get_env/2-3 must lookup ac_tab by {env, AppName, Key} every time.

  1. Configurations in sys.config.
[
{appname_1, [{ip, "127.0.0.1"}, {port, 8080}]},
{appname_2, [{log_level, debug}, {log_path, "log/debug.log"}]}
].
  1. These configurations are present in the ac_tab table as the release starts.
[{{env, appname_1, ip}, "127.0.0.1"}, {{env, appname_1, port}, 8080},
{{env, appname_2, log_level}, debug}}, {{env, appname_2, log_path}, "log/debug.log"}].
  1. eenv dynamic compiles configurations into beam by calling eenv:load(AppName). It generates two beam files:
% eenv_router.beam
-module(eenv_router).
-export([get/2]).
get(appname_1, Key) -> eenv_appname_1:get(Key);
get(appname_2, Key) -> eenv_appname_2:get(Key);
get(_, _) -> unload.
% eenv_appname_1.beam
-module(eenv_appname_1).
-export([get/1]).
get(ip) -> {ok, "127.0.0.1"};
get(port) -> {ok, 8080};
get(_) -> undefined.
% eenv_appname_2.beam
-module(eenv_appname_2).
-export([get/1]).
get(log_level) -> {ok, debug};
get(log_path) -> {ok, "log/debug.log"};
get(_) -> undefined.

we can retrieve parameters by eenv:get/2-3., such as:

{ok, IP} = eenv:get(appname_1, ip),
{ok, Port} = eenv:get(appname_1, port),
LogLevel = eenv:get(appname_2, log_level, error),
LogPath = eenv:get(appname_1, log_path, "log/error.log").

Each loaded application will generate eenv_'AppName'.beam to save own configuration information.

eenv's code is very straightforward(lines < 200) and worth a visit.

Quick Start

  1. Load eenv when your application starts by eenv:load(YourAppName).
% YourApp_app.erl
-behaviour(application).
% Application callbacks
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
eenv:load(your_app),
your_app_sup:start_link().
stop(_State) ->
eenv:unload(your_app),
ok.
  1. Replace application's function with eenv's function.
BeforeAfter
application:get/2-3eenv:get/2-3
application:set/2-4eenv:set/2-4
application:unset/2-3eenv:unset/2-3

Benchmark

Benchmark Type Specification

More detailed information see eenv_benchmark.erl.

zsh$ make benchmark
===> Verifying dependencies...
===> Compiling eenv
Starting benchmark...
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.0 (abort with ^G)
1> Run ProcNum=1 processes Count=10000 count/process. EmptyLoop: 8.000ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 24.500| 245.000| 1.00x rec | 1.00 x rec = rec |
dict| 50.800| 508.000| 2.07x rec | 2.07 x rec = dict |
eenv| 73.600| 736.000| 3.00x rec | 1.45 x dict = eenv |
map| 78.400| 784.000| 3.20x rec | 1.07 x eenv = map |
code| 187.600| 1876.000| 7.66x rec | 2.39 x map = code |
app| 449.200| 4492.000| 18.33x rec | 2.39 x code = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=10 processes Count=10000 count/process. EmptyLoop: 6.200ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 21.650| 216.500| 1.00x rec | 1.00 x rec = rec |
dict| 40.780| 407.800| 1.88x rec | 1.88 x rec = dict |
map| 65.500| 655.000| 3.03x rec | 1.61 x dict = map |
code| 176.260| 1762.600| 8.14x rec | 2.69 x map = code |
eenv| 198.180| 1981.800| 9.15x rec | 1.12 x code = eenv |
app| 990.120| 9901.200| 45.73x rec | 5.00 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=20 processes Count=10000 count/process. EmptyLoop: 11.600ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 21.600| 216.000| 1.00x rec | 1.00 x rec = rec |
dict| 37.530| 375.300| 1.74x rec | 1.74 x rec = dict |
map| 72.705| 727.050| 3.37x rec | 1.94 x dict = map |
code| 243.830| 2438.300| 11.29x rec | 3.35 x map = code |
eenv| 289.220| 2892.200| 13.39x rec | 1.19 x code = eenv |
app| 1288.025| 12880.250| 59.63x rec | 4.45 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=30 processes Count=10000 count/process. EmptyLoop: 8.800ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 27.173| 271.733| 1.00x rec | 1.00 x rec = rec |
dict| 56.157| 561.567| 2.07x rec | 2.07 x rec = dict |
map| 76.130| 761.300| 2.80x rec | 1.36 x dict = map |
code| 274.173| 2741.733| 10.09x rec | 3.60 x map = code |
eenv| 342.797| 3427.967| 12.62x rec | 1.25 x code = eenv |
app| 1800.640| 18006.400| 66.26x rec | 5.25 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=40 processes Count=10000 count/process. EmptyLoop: 10.050ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 28.973| 289.725| 1.00x rec | 1.00 x rec = rec |
dict| 52.775| 527.750| 1.82x rec | 1.82 x rec = dict |
map| 73.282| 732.825| 2.53x rec | 1.39 x dict = map |
code| 336.410| 3364.100| 11.61x rec | 4.59 x map = code |
eenv| 399.125| 3991.250| 13.78x rec | 1.19 x code = eenv |
app| 2241.085| 22410.850| 77.35x rec | 5.61 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=50 processes Count=10000 count/process. EmptyLoop: 10.400ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 30.304| 303.040| 1.00x rec | 1.00 x rec = rec |
dict| 51.718| 517.180| 1.71x rec | 1.71 x rec = dict |
map| 70.316| 703.160| 2.32x rec | 1.36 x dict = map |
code| 384.044| 3840.440| 12.67x rec | 5.46 x map = code |
eenv| 477.252| 4772.520| 15.75x rec | 1.24 x code = eenv |
app| 2726.554| 27265.540| 89.97x rec | 5.71 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=1000 processes Count=10000 count/process. EmptyLoop: 10.956ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.751| 187.506| 1.00x rec | 1.00 x rec = rec |
dict| 38.307| 383.066| 2.04x rec | 2.04 x rec = dict |
map| 60.377| 603.768| 3.22x rec | 1.58 x dict = map |
code| 8852.114| 88521.138| 472.10x rec | 146.61 x map = code |
eenv| 9247.929| 92479.286| 493.21x rec | 1.04 x code = eenv |
app| 66608.656| 666086.560| 3552.35x rec | 7.20 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=2000 processes Count=10000 count/process. EmptyLoop: 10.706ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.498| 184.981| 1.00x rec | 1.00 x rec = rec |
dict| 37.629| 376.288| 2.03x rec | 2.03 x rec = dict |
map| 59.790| 597.904| 3.23x rec | 1.59 x dict = map |
code| 15319.678| 153196.775| 828.18x rec | 256.22 x map = code |
eenv| 19704.738| 197047.378| 1065.23x rec | 1.29 x code = eenv |
app| 165826.738| 1658267.384| 8964.53x rec | 8.42 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=3000 processes Count=10000 count/process. EmptyLoop: 12.050ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.703| 187.035| 1.00x rec | 1.00 x rec = rec |
dict| 40.587| 405.870| 2.17x rec | 2.17 x rec = dict |
map| 59.714| 597.141| 3.19x rec | 1.47 x dict = map |
code| 24968.905| 249689.051| 1334.99x rec | 418.14 x map = code |
eenv| 35843.095| 358430.950| 1916.38x rec | 1.44 x code = eenv |
app| 233007.906| 2330079.058| 12457.98x rec | 6.50 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=5000 processes Count=10000 count/process. EmptyLoop: 11.276ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.808| 188.077| 1.00x rec | 1.00 x rec = rec |
dict| 41.237| 412.371| 2.19x rec | 2.19 x rec = dict |
map| 58.939| 589.391| 3.13x rec | 1.43 x dict = map |
code| 46437.121| 464371.206| 2469.05x rec | 787.88 x map = code |
eenv| 59476.405| 594764.053| 3162.35x rec | 1.28 x code = eenv |
app| 473787.975| 4737879.751| 25191.22x rec | 7.97 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=10000 processes Count=10000 count/process. EmptyLoop: 10.829ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.884| 188.843| 1.00x rec | 1.00 x rec = rec |
dict| 39.479| 394.787| 2.09x rec | 2.09 x rec = dict |
map| 60.383| 603.827| 3.20x rec | 1.53 x dict = map |
code| 90985.045| 909850.445| 4818.04x rec | 1506.81 x map = code |
eenv| 106457.887| 1064578.867| 5637.39x rec | 1.17 x code = eenv |
app| 875741.508| 8757415.083| 46374.15x rec | 8.23 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=15000 processes Count=10000 count/process. EmptyLoop: 10.773ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 19.000| 189.998| 1.00x rec | 1.00 x rec = rec |
dict| 42.839| 428.392| 2.25x rec | 2.25 x rec = dict |
map| 65.579| 655.790| 3.45x rec | 1.53 x dict = map |
code| 110231.265| 1102312.651| 5801.70x rec | 1680.89 x map = code |
eenv| 154514.285| 1545142.848| 8132.41x rec | 1.40 x code = eenv |
app| 1103348.055| 11033480.554| 58071.52x rec | 7.14 x eenv = app |
--------------------------------------------------------------------------------------------------------
Run ProcNum=20000 processes Count=10000 count/process. EmptyLoop: 10.800ns/loop
--------------------------------------------------------------------------------------------------------
Type| Time/(ProcNum*Count) ns| Time/ProcNum us| Time/MinTime| Time/PrevMinTime|
rec| 18.891| 188.906| 1.00x rec | 1.00 x rec = rec |
dict| 39.378| 393.776| 2.08x rec | 2.08 x rec = dict |
map| 61.373| 613.734| 3.25x rec | 1.56 x dict = map |
code| 145870.193| 1458701.932| 7721.84x rec | 2376.77 x map = code |
eenv| 206175.821| 2061758.207| 10914.20x rec | 1.41 x code = eenv |
app| 1379935.745| 13799357.453| 73048.79x rec | 6.69 x eenv = app |
--------------------------------------------------------------------------------------------------------

Conclusion

Confused

ChangeLog

License

See the LICENSE file for license rights and limitations (MIT).