Fix pins unrandomizing (#134)

* Add extra test to pins

* Fix pins unrandomizing

* Fix pins finally

* Add test and docs
This commit is contained in:
ndiezel0 2024-05-30 12:06:37 +05:00 committed by GitHub
parent ef25bdc20a
commit 5accc10b24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 118 additions and 39 deletions

View File

@ -349,20 +349,31 @@ select_better_route({LeftScore, _} = Left, {RightScore, _} = Right) ->
RightPin = RightScore#domain_PaymentRouteScores.route_pin, RightPin = RightScore#domain_PaymentRouteScores.route_pin,
Res = Res =
case {LeftPin, RightPin} of case {LeftPin, RightPin} of
_ when LeftPin /= ?ZERO, RightPin /= ?ZERO, LeftPin == RightPin -> _ when LeftPin /= ?ZERO, RightPin /= ?ZERO, RightPin == LeftPin ->
select_better_pinned_route(Left, Right); select_better_pinned_route(Left, Right);
_ -> _ ->
select_better_regular_route(Left, Right) select_better_regular_route(Left, Right)
end, end,
Res. Res.
select_better_pinned_route({LeftScore0, _Route1} = Left, {RightScore0, _Route2} = Right) -> select_better_pinned_route({LeftScore0, LeftRoute} = Left, {RightScore0, RightRoute} = Right) ->
LeftScore1 = LeftScore0#domain_PaymentRouteScores{ LeftScore1 = LeftScore0#domain_PaymentRouteScores{
random_condition = 0 random_condition = 0,
route_pin = erlang:phash2({
LeftScore0#domain_PaymentRouteScores.route_pin,
hg_route:provider_ref(LeftRoute),
hg_route:terminal_ref(LeftRoute)
})
}, },
RightScore1 = RightScore0#domain_PaymentRouteScores{ RightScore1 = RightScore0#domain_PaymentRouteScores{
random_condition = 0 random_condition = 0,
route_pin = erlang:phash2({
RightScore0#domain_PaymentRouteScores.route_pin,
hg_route:provider_ref(RightRoute),
hg_route:terminal_ref(RightRoute)
})
}, },
case max(LeftScore1, RightScore1) of case max(LeftScore1, RightScore1) of
LeftScore1 -> LeftScore1 ->
Left; Left;
@ -868,58 +879,93 @@ pin_random_test() ->
Route2 = {hg_route:new(?prv(2), ?trm(2), 50, 1, Pin), Scores}, Route2 = {hg_route:new(?prv(2), ?trm(2), 50, 1, Pin), Scores},
lists:foldl( lists:foldl(
fun(_I, Acc) -> fun(_I, Acc) ->
BalancedRoutes = balance_routes([Route1, Route2]), {ST, _} = ShuffledRoute = shuffle_routes([Route1, Route2]),
ScoredRoutes = score_routes(BalancedRoutes),
{{_, ChosenScoredRoute}, _IdealRoute} = find_best_routes(ScoredRoutes),
case Acc of case Acc of
undefined -> undefined ->
ChosenScoredRoute; ShuffledRoute;
{ST, _} ->
ShuffledRoute;
_ -> _ ->
ChosenTerminal = hg_route:terminal_ref(ChosenScoredRoute), error({ShuffledRoute, Acc})
AccTerminal = hg_route:terminal_ref(Acc),
_ = ?assertEqual(ChosenTerminal, AccTerminal),
ChosenScoredRoute
end end
end, end,
undefined, undefined,
lists:seq(0, 1000) lists:seq(0, 1000)
). ).
-spec diff_pin_test() -> _.
diff_pin_test() ->
Pin = #{
email => <<"example@mail.com">>
},
Scores = {{alive, 0.0}, {normal, 0.0}},
Route1 = {hg_route:new(?prv(1), ?trm(1), 50, 33, Pin), Scores},
Route2 = {hg_route:new(?prv(1), ?trm(2), 50, 33, Pin), Scores},
Route3 = {hg_route:new(?prv(1), ?trm(3), 50, 33, Pin#{client_ip => <<"IP">>}), Scores},
{I1, I2, I3} = lists:foldl(
fun(_I, {Iter1, Iter2, Iter3}) ->
{ST, _} = shuffle_routes([Route1, Route2, Route3]),
case ST of
?trm(1) ->
{Iter1 + 1, Iter2, Iter3};
?trm(2) ->
{Iter1, Iter2 + 1, Iter3};
?trm(3) ->
{Iter1, Iter2, Iter3 + 1}
end
end,
{0, 0, 0},
lists:seq(0, 1000)
),
case {I1, I2} of
{0, S} when S > 400 ->
true;
{S, 0} when S > 400 ->
true;
SomethingElse ->
error({{i1, i2}, SomethingElse})
end,
case I3 of
_ when I3 > 300 ->
true;
_ ->
error({i3, I3})
end.
-spec pin_weight_test() -> _. -spec pin_weight_test() -> _.
pin_weight_test() -> pin_weight_test() ->
Pin0 = #{ Pin0 = #{
email => <<"example@mail.com">> email => <<"example@mail.com">>
}, },
Pin1 = #{ Pin1 = #{
email => <<"example2@mail.com">> email => <<"example1@mail.com">>
}, },
Scores = {{alive, 0.0}, {normal, 0.0}}, Scores1 = {{alive, 0.0}, {normal, 0.0}},
Route1 = {hg_route:new(?prv(1), ?trm(1), 50, 1, Pin0), Scores}, Scores2 = {{alive, 0.0}, {normal, 0.0}},
Route2 = {hg_route:new(?prv(2), ?trm(2), 50, 1, Pin1), Scores}, Route1 = {hg_route:new(?prv(1), ?trm(1), 50, 1, Pin0, ?fd_overrides(true)), Scores1},
{_, DiffTimes} = lists:foldl( Route2 = {hg_route:new(?prv(1), ?trm(2), 50, 1, Pin0, ?fd_overrides(true)), Scores2},
fun(_I, {Acc, Iter}) -> Route3 = {hg_route:new(?prv(1), ?trm(1), 50, 1, Pin1, ?fd_overrides(true)), Scores1},
BalancedRoutes = balance_routes([Route1, Route2]), Route4 = {hg_route:new(?prv(1), ?trm(2), 50, 1, Pin1, ?fd_overrides(true)), Scores2},
ScoredRoutes = score_routes(BalancedRoutes), true = lists:foldl(
{{_, ChosenScoredRoute}, _IdealRoute} = find_best_routes(ScoredRoutes), fun(_I, _A) ->
case Acc of {ShuffledRoute1, _} = shuffle_routes([Route1, Route2]),
undefined -> {ShuffledRoute2, _} = shuffle_routes([Route3, Route4]),
{ChosenScoredRoute, Iter}; case true of
_ when ShuffledRoute1 == ?trm(1), ShuffledRoute2 == ?trm(2) ->
true;
_ -> _ ->
ChosenTerminal = hg_route:terminal_ref(ChosenScoredRoute), error({ShuffledRoute1, ShuffledRoute2})
case hg_route:terminal_ref(Acc) of
ChosenTerminal ->
{Acc, Iter};
_ ->
{Acc, Iter + 1}
end
end end
end, end,
{undefined, 0}, true,
lists:seq(0, 1000) lists:seq(0, 1000)
), ).
?assertNotEqual(0, DiffTimes),
?assertEqual(true, DiffTimes > 300), shuffle_routes(Routes) ->
?assertEqual(true, DiffTimes < 700). BalancedRoutes = balance_routes(Routes),
ScoredRoutes = score_routes(BalancedRoutes),
{{_, ChosenScoredRoute}, _IdealRoute} = find_best_routes(ScoredRoutes),
{hg_route:terminal_ref(ChosenScoredRoute), ChosenScoredRoute}.
-spec balance_routes_test_() -> [testcase()]. -spec balance_routes_test_() -> [testcase()].
balance_routes_test_() -> balance_routes_test_() ->

View File

@ -1,5 +1,3 @@
# Документация # Документация
1. [Общее описание](overview.md) 1. [Работа пинов](route_pins.md)
1. [Установка](install.md)
1. [Первоначалная настройка](configuration.md)

35
doc/route_pins.md Normal file
View File

@ -0,0 +1,35 @@
# Пины роутов
## Какая задача
У нас есть 2 и более роутов с одинаковым приоритетом
и какой-то там разбивкой по весу. Например 3 роута с весами 33:33:33.
К нам приходит плательщик. Он оплачивает какую-то услугу
и этот платеж проходит через конкретный терминал конкретного провайдера.
Проще говоря он выбрал один из кандидатов (роутов) из списка с одинаковым приоритетом.
Теперь мы хотим чтобы этот плательщик в будущем ходим через тот же самый роут.
Плательщика определяем каким-то там способом.
## Решение
Мы в каждом роут кандидате можем указать
[список характеристик](https://github.com/valitydev/damsel/blob/master/proto/domain.thrift#L2850-L2856)
по которым мы будем определять какой именно плательщик к нам пришел.
Когда к нам приходит запрос на проведение платежа, то мы собираем все указанные
в конкретном кандидате характеристики и вычисляем хэш этих характеристик.
Этот хэш учитывается при сортировке роутов по самым желаемым.
Если как в примере выше у нас 3 роут кандидата с одинаковым весом
и список характеристик (например смотрим только на имейл) совпадает,
то мы лочим роут с этим значением характеристики.
Все последующие платежи с этими значениями будут проходить по тому роуту, что был использован
в первой операции. Соответственно вес у нас в одном приоритете становится 100:0:0.
Если же один из этих роутов имеет другой набор характеристик, например имейл и IP адрес клиента, то он участвует
в локе пинов с роутами у которых такой же набор характеристик. В данном примере, так как он один, то распределение
становится 66:0:33. Если бы был еще один роут с тем же приоритетом и набором характеристик имейл и IP, то
распределение было бы 50:0:50:0