Improve the mqueue design
This commit is contained in:
parent
0d617c17e0
commit
496d046d52
|
@ -58,25 +58,27 @@
|
||||||
|
|
||||||
-define(HIGH_WM, 0.6).
|
-define(HIGH_WM, 0.6).
|
||||||
|
|
||||||
|
-define(PQUEUE, priority_queue).
|
||||||
|
|
||||||
-type(priority() :: {iolist(), pos_integer()}).
|
-type(priority() :: {iolist(), pos_integer()}).
|
||||||
|
|
||||||
-type(option() :: {type, simple | priority}
|
-type(option() :: {type, simple | priority}
|
||||||
| {max_length, pos_integer() | infinity}
|
| {max_length, non_neg_integer()} %% Max queue length
|
||||||
| {priority, list(priority())}
|
| {priority, list(priority())}
|
||||||
| {low_watermark, float()} %% Low watermark
|
| {low_watermark, float()} %% Low watermark
|
||||||
| {high_watermark, float()} %% High watermark
|
| {high_watermark, float()} %% High watermark
|
||||||
| {queue_qos0, boolean()}). %% Queue Qos0?
|
| {store_qos0, boolean()}). %% Queue Qos0?
|
||||||
|
|
||||||
-type(stat() :: {max_len, infinity | pos_integer()}
|
-type(stat() :: {max_len, non_neg_integer()}
|
||||||
| {len, non_neg_integer()}
|
| {len, non_neg_integer()}
|
||||||
| {dropped, non_neg_integer()}).
|
| {dropped, non_neg_integer()}).
|
||||||
|
|
||||||
-record(mqueue, {type :: simple | priority,
|
-record(mqueue, {type :: simple | priority,
|
||||||
name, q :: queue:queue() | priority_queue:q(),
|
name, q :: queue:queue() | ?PQUEUE:q(),
|
||||||
%% priority table
|
%% priority table
|
||||||
pseq = 0, priorities = [],
|
pseq = 0, priorities = [],
|
||||||
%% len of simple queue
|
%% len of simple queue
|
||||||
len = 0, max_len = infinity,
|
len = 0, max_len = 0,
|
||||||
low_wm = ?LOW_WM, high_wm = ?HIGH_WM,
|
low_wm = ?LOW_WM, high_wm = ?HIGH_WM,
|
||||||
qos0 = false, dropped = 0,
|
qos0 = false, dropped = 0,
|
||||||
alarm_fun}).
|
alarm_fun}).
|
||||||
|
@ -89,19 +91,19 @@
|
||||||
-spec(new(iolist(), list(option()), fun()) -> mqueue()).
|
-spec(new(iolist(), list(option()), fun()) -> mqueue()).
|
||||||
new(Name, Opts, AlarmFun) ->
|
new(Name, Opts, AlarmFun) ->
|
||||||
Type = get_value(type, Opts, simple),
|
Type = get_value(type, Opts, simple),
|
||||||
MaxLen = get_value(max_length, Opts, infinity),
|
MaxLen = get_value(max_length, Opts, 0),
|
||||||
init_q(#mqueue{type = Type, name = iolist_to_binary(Name),
|
init_q(#mqueue{type = Type, name = iolist_to_binary(Name),
|
||||||
len = 0, max_len = MaxLen,
|
len = 0, max_len = MaxLen,
|
||||||
low_wm = low_wm(MaxLen, Opts),
|
low_wm = low_wm(MaxLen, Opts),
|
||||||
high_wm = high_wm(MaxLen, Opts),
|
high_wm = high_wm(MaxLen, Opts),
|
||||||
qos0 = get_value(queue_qos0, Opts, false),
|
qos0 = get_value(store_qos0, Opts, false),
|
||||||
alarm_fun = AlarmFun}, Opts).
|
alarm_fun = AlarmFun}, Opts).
|
||||||
|
|
||||||
init_q(MQ = #mqueue{type = simple}, _Opts) ->
|
init_q(MQ = #mqueue{type = simple}, _Opts) ->
|
||||||
MQ#mqueue{q = queue:new()};
|
MQ#mqueue{q = queue:new()};
|
||||||
init_q(MQ = #mqueue{type = priority}, Opts) ->
|
init_q(MQ = #mqueue{type = priority}, Opts) ->
|
||||||
Priorities = get_value(priority, Opts, []),
|
Priorities = get_value(priority, Opts, []),
|
||||||
init_p(Priorities, MQ#mqueue{q = priority_queue:new()}).
|
init_p(Priorities, MQ#mqueue{q = ?PQUEUE:new()}).
|
||||||
|
|
||||||
init_p([], MQ) ->
|
init_p([], MQ) ->
|
||||||
MQ;
|
MQ;
|
||||||
|
@ -113,13 +115,13 @@ insert_p(Topic, P, MQ = #mqueue{priorities = Tab, pseq = Seq}) ->
|
||||||
<<PInt:48>> = <<P:8, (erlang:phash2(Topic)):32, Seq:8>>,
|
<<PInt:48>> = <<P:8, (erlang:phash2(Topic)):32, Seq:8>>,
|
||||||
{PInt, MQ#mqueue{priorities = [{Topic, PInt} | Tab], pseq = Seq + 1}}.
|
{PInt, MQ#mqueue{priorities = [{Topic, PInt} | Tab], pseq = Seq + 1}}.
|
||||||
|
|
||||||
low_wm(infinity, _Opts) ->
|
low_wm(0, _Opts) ->
|
||||||
infinity;
|
undefined;
|
||||||
low_wm(MaxLen, Opts) ->
|
low_wm(MaxLen, Opts) ->
|
||||||
round(MaxLen * get_value(low_watermark, Opts, ?LOW_WM)).
|
round(MaxLen * get_value(low_watermark, Opts, ?LOW_WM)).
|
||||||
|
|
||||||
high_wm(infinity, _Opts) ->
|
high_wm(0, _Opts) ->
|
||||||
infinity;
|
undefined;
|
||||||
high_wm(MaxLen, Opts) ->
|
high_wm(MaxLen, Opts) ->
|
||||||
round(MaxLen * get_value(high_watermark, Opts, ?HIGH_WM)).
|
round(MaxLen * get_value(high_watermark, Opts, ?HIGH_WM)).
|
||||||
|
|
||||||
|
@ -132,10 +134,10 @@ type(#mqueue{type = Type}) ->
|
||||||
Type.
|
Type.
|
||||||
|
|
||||||
is_empty(#mqueue{type = simple, len = Len}) -> Len =:= 0;
|
is_empty(#mqueue{type = simple, len = Len}) -> Len =:= 0;
|
||||||
is_empty(#mqueue{type = priority, q = Q}) -> priority_queue:is_empty(Q).
|
is_empty(#mqueue{type = priority, q = Q}) -> ?PQUEUE:is_empty(Q).
|
||||||
|
|
||||||
len(#mqueue{type = simple, len = Len}) -> Len;
|
len(#mqueue{type = simple, len = Len}) -> Len;
|
||||||
len(#mqueue{type = priority, q = Q}) -> priority_queue:len(Q).
|
len(#mqueue{type = priority, q = Q}) -> ?PQUEUE:len(Q).
|
||||||
|
|
||||||
max_len(#mqueue{max_len = MaxLen}) -> MaxLen.
|
max_len(#mqueue{max_len = MaxLen}) -> MaxLen.
|
||||||
|
|
||||||
|
@ -148,14 +150,14 @@ dropped(#mqueue{dropped = Dropped}) -> Dropped.
|
||||||
stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped}) ->
|
stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped}) ->
|
||||||
[{len, case Type of
|
[{len, case Type of
|
||||||
simple -> Len;
|
simple -> Len;
|
||||||
priority -> priority_queue:len(Q)
|
priority -> ?PQUEUE:len(Q)
|
||||||
end} | [{max_len, MaxLen}, {dropped, Dropped}]].
|
end} | [{max_len, MaxLen}, {dropped, Dropped}]].
|
||||||
|
|
||||||
%% @doc Enqueue a message.
|
%% @doc Enqueue a message.
|
||||||
-spec(in(mqtt_message(), mqueue()) -> mqueue()).
|
-spec(in(mqtt_message(), mqueue()) -> mqueue()).
|
||||||
in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
||||||
MQ;
|
MQ;
|
||||||
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = 0}) ->
|
||||||
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
|
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
|
||||||
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
|
||||||
when Len >= MaxLen ->
|
when Len >= MaxLen ->
|
||||||
|
@ -166,43 +168,45 @@ in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
||||||
|
|
||||||
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
priorities = Priorities,
|
priorities = Priorities,
|
||||||
max_len = infinity}) ->
|
max_len = 0}) ->
|
||||||
case lists:keysearch(Topic, 1, Priorities) of
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
{value, {_, Pri}} ->
|
{value, {_, Pri}} ->
|
||||||
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)};
|
MQ#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)};
|
||||||
false ->
|
false ->
|
||||||
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
||||||
MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
MQ1#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)}
|
||||||
end;
|
end;
|
||||||
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
priorities = Priorities,
|
priorities = Priorities,
|
||||||
max_len = MaxLen}) ->
|
max_len = MaxLen}) ->
|
||||||
case lists:keysearch(Topic, 1, Priorities) of
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
{value, {_, Pri}} ->
|
{value, {_, Pri}} ->
|
||||||
case priority_queue:plen(Pri, Q) >= MaxLen of
|
case ?PQUEUE:plen(Pri, Q) >= MaxLen of
|
||||||
true ->
|
true ->
|
||||||
{_, Q1} = priority_queue:out(Pri, Q),
|
{_, Q1} = ?PQUEUE:out(Pri, Q),
|
||||||
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q1)};
|
MQ#mqueue{q = ?PQUEUE:in(Msg, Pri, Q1)};
|
||||||
false ->
|
false ->
|
||||||
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
MQ#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)}
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
||||||
MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
MQ1#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
out(MQ = #mqueue{type = simple, len = 0}) ->
|
out(MQ = #mqueue{type = simple, len = 0}) ->
|
||||||
{empty, MQ};
|
{empty, MQ};
|
||||||
out(MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
|
out(MQ = #mqueue{type = simple, q = Q, len = Len, max_len = 0}) ->
|
||||||
{R, Q2} = queue:out(Q),
|
{R, Q2} = queue:out(Q),
|
||||||
{R, MQ#mqueue{q = Q2, len = Len - 1}};
|
{R, MQ#mqueue{q = Q2, len = Len - 1}};
|
||||||
out(MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
out(MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
||||||
{R, Q2} = queue:out(Q),
|
{R, Q2} = queue:out(Q),
|
||||||
{R, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})};
|
{R, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})};
|
||||||
out(MQ = #mqueue{type = priority, q = Q}) ->
|
out(MQ = #mqueue{type = priority, q = Q}) ->
|
||||||
{R, Q2} = priority_queue:out(Q),
|
{R, Q2} = ?PQUEUE:out(Q),
|
||||||
{R, MQ#mqueue{q = Q2}}.
|
{R, MQ#mqueue{q = Q2}}.
|
||||||
|
|
||||||
|
maybe_set_alarm(MQ = #mqueue{high_wm = undefined}) ->
|
||||||
|
MQ;
|
||||||
maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun = AlarmFun})
|
maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun = AlarmFun})
|
||||||
when Len > HighWM ->
|
when Len > HighWM ->
|
||||||
Alarm = #mqtt_alarm{id = iolist_to_binary(["queue_high_watermark.", Name]),
|
Alarm = #mqtt_alarm{id = iolist_to_binary(["queue_high_watermark.", Name]),
|
||||||
|
@ -213,6 +217,8 @@ maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun
|
||||||
maybe_set_alarm(MQ) ->
|
maybe_set_alarm(MQ) ->
|
||||||
MQ.
|
MQ.
|
||||||
|
|
||||||
|
maybe_clear_alarm(MQ = #mqueue{low_wm = undefined}) ->
|
||||||
|
MQ;
|
||||||
maybe_clear_alarm(MQ = #mqueue{name = Name, len = Len, low_wm = LowWM, alarm_fun = AlarmFun})
|
maybe_clear_alarm(MQ = #mqueue{name = Name, len = Len, low_wm = LowWM, alarm_fun = AlarmFun})
|
||||||
when Len < LowWM ->
|
when Len < LowWM ->
|
||||||
MQ#mqueue{alarm_fun = AlarmFun(clear, list_to_binary(["queue_high_watermark.", Name]))};
|
MQ#mqueue{alarm_fun = AlarmFun(clear, list_to_binary(["queue_high_watermark.", Name]))};
|
||||||
|
|
Loading…
Reference in New Issue