nickva commented on code in PR #31:
URL: https://github.com/apache/couchdb-ioq/pull/31#discussion_r3581016601


##########
src/ioq_server2.erl:
##########
@@ -51,14 +51,14 @@
 
 
 -record(state, {
-    reqs :: khash:khash(),
-    waiters :: khash:khash(),
+    reqs :: #{any() => ioq_request()},

Review Comment:
   These could be type aliases in this module too perhaps `reqs()` and 
`waiters()` it just makes the type signatures a bit neater.
   
   Another idea that for plain maps, which we initialize to #{} always, we can 
initialize them in the record definition directly. That could eliminate some 
dialyzer warning possible since it avoids the intermediate pre-initialized 
`undefined` values.
   ```
   reqs = #{} :: ...
   waiters = #{} :: ...
   ```



##########
src/ioq_server2.erl:
##########
@@ -293,8 +287,8 @@ start_link(Name, SID, Bind) ->
 
 init([Name, SID]) ->
     {ok, HQ} = hqueue:new(),
-    {ok, Reqs} = khash:new(),
-    {ok, Waiters} = khash:new(),
+    Reqs = #{},

Review Comment:
   If these are defaulted to `#{}` in the record definition we don't have to 
worry about them here.  We would have to explicitly set `hqueue:new()` though



##########
src/ioq_server2.erl:
##########
@@ -472,15 +465,15 @@ submit_request(Req, #state{iterations=Iterations}=State) 
->
     couch_stats:increment_counter([couchdb, io_queue2, Class, count]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, count]),
     couch_stats:update_histogram([couchdb, io_queue2, submit_delay], Latency),
-    khash:put(Reqs, Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}),
-    State#state{iterations=Iterations+1}.
+    Reqs = maps:put(Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}, 
State#state.reqs),
+    State#state{iterations = Iterations + 1, reqs = Reqs}.
 
 
--spec send_response(khash:khash(), ioq_request(), term()) -> [ok].
-send_response(Waiters, #ioq_request{key=Key}, Reply) ->
-    Waiting = khash:get(Waiters, Key),
-    khash:del(Waiters, Key),
-    [gen_server:reply(W, Reply) || W <- Waiting].
+-spec send_response(state(), ioq_request(), term()) -> state().
+send_response(#state{waiters = Waiters} = State0, #ioq_request{key = Key}, 
Reply) ->
+    {ok, Waiting} = maps:find(Key, Waiters),

Review Comment:
   Just a note here that we're asserting the existence of the key a bit earlier 
than in  the old code. In the old code, if somehow the key was missing we would 
have crashed in the `[gen_server:reply(W, Reply) || W <- Waiting]` with a bad 
`undefined` generator now we'll crash on badmatch in the `maps:find/2`.
   
    In fact, we should simplify this even more and use `maps:take` to emphasize 
the assertion even more
   
   ```erlang
   {Waiting, Waiters1} = maps:take(Key, Waiters}
   ...
   State0#state{waiters = Waiters1}
   ```
   
   A tiny bit shorter
   



##########
src/ioq_server2.erl:
##########
@@ -1095,15 +1093,17 @@ random_server(Servers) ->
     lists:nth(rand:uniform(length(Servers)), Servers).
 
 
-test_io_error(#state{waiters=Waiters, reqs=Reqs}=State) ->
+test_io_error(#state{waiters=Waiters, reqs=Reqs}=State0) ->
     Key = asdf,
     Ref = make_ref(),
     RefTag = make_ref(),
     Req = #ioq_request{ref=Ref, key=Key},
-    khash:put(Waiters, Key, [{self(), RefTag}]),
-    khash:put(Reqs, Ref, Req),
+    State1 = State0#state{
+        waiters = maps:put(Key, [{self(), RefTag}], Waiters),

Review Comment:
   Use `#{Key => ...}`



##########
src/ioq_server2.erl:
##########
@@ -506,20 +499,20 @@ enqueue_request(Req, #state{queue=HQ, 
waiters=Waiters}=State0) ->
     couch_stats:increment_counter([couchdb, io_queue2, queued]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, queued]),
 
-    case khash:get(State#state.waiters, ReqKey, not_found) of
-        not_found ->
+    Waiters = case maps:find(ReqKey, State#state.waiters) of
+        error ->
             Priority = prioritize_request(Req, State),
             Req1 = Req#ioq_request{
                 key = ReqKey,
                 init_priority = Priority
             },
             hqueue:insert(HQ, Priority, Req1),
-            khash:put(State#state.waiters, ReqKey, [From]);
-        Pids ->
+            maps:put(ReqKey, [From], State#state.waiters);

Review Comment:
   To avoid repeating `State#state.waiters` deconstruct #state{queue=HQ, 
waiters = Waiters0} = State` at the start of the function. Same idea as above, 
match first on general record types then deconstruct right below to pick out 
individual variables we need



##########
src/ioq_server2.erl:
##########
@@ -343,32 +337,32 @@ handle_cast(_Msg, State) ->
     {noreply, State, 0}.
 
 
-handle_info({Ref, Reply}, #state{reqs = Reqs} = State) ->
-    case khash:get(Reqs, Ref) of
-        undefined ->
-            ok;
-        #ioq_request{ref=Ref}=Req ->
-            ok = khash:del(Reqs, Ref),
+handle_info({Ref, Reply}, #state{reqs = Reqs} = State0) ->
+    State1 = case maps:find(Ref, Reqs) of
+        error ->
+            State0;
+        {ok, #ioq_request{ref = Ref} = Req} ->
             TResponse = os:timestamp(),
             ServiceTime = time_delta(TResponse, Req#ioq_request.tsub),
             IOWait = time_delta(TResponse, Req#ioq_request.t0),
             couch_stats:update_histogram(
                 [couchdb, io_queue2, svctm], ServiceTime),
             couch_stats:update_histogram([couchdb, io_queue2, iowait], IOWait),
             erlang:demonitor(Ref, [flush]),
-            send_response(State#state.waiters, Req, Reply)
+            State = send_response(State0, Req, Reply),
+            State#state{reqs = maps:remove(Ref, Reqs)}
     end,
-    {noreply, State, 0};
-handle_info({'DOWN', Ref, _, _, Reason}, #state{reqs = Reqs} = State) ->
-    case khash:get(Reqs, Ref) of
-        undefined ->
-            ok;
-        #ioq_request{ref=Ref}=Req ->
+    {noreply, State1, 0};
+handle_info({'DOWN', Ref, _, _, Reason}, #state{reqs = Reqs} = State0) ->

Review Comment:
   Like above we could split this into two heads one for an element that exist 
and one for when it doesn't:
   ```
   handle_info({'DOWN', Ref, _, _, Reason}, #state{reqs = #{Ref := Reqs}} = 
State0) ->
    ...
   ```



##########
src/ioq_server2.erl:
##########
@@ -343,32 +337,32 @@ handle_cast(_Msg, State) ->
     {noreply, State, 0}.
 
 
-handle_info({Ref, Reply}, #state{reqs = Reqs} = State) ->
-    case khash:get(Reqs, Ref) of
-        undefined ->
-            ok;
-        #ioq_request{ref=Ref}=Req ->
-            ok = khash:del(Reqs, Ref),
+handle_info({Ref, Reply}, #state{reqs = Reqs} = State0) ->

Review Comment:
   I think we could split it at the stop level into two clauses by matching the 
Ref directly.
   
   ```erlang
   handle_info({Ref, Reply}, #state{reqs = #{Ref := Req}} = State0) ->
      % we have the request
      ;
   handle_info({Ref, Reply}, #state{reqs = #{}} = State) ->
      % don't have the request
      State.
   ```
   
   Check if the pattern `(...K..., #{K := V})` would work, if it doesn't there 
is an `is_map_key(K, Map)` guard that would work



##########
src/ioq_server2.erl:
##########
@@ -472,15 +465,15 @@ submit_request(Req, #state{iterations=Iterations}=State) 
->
     couch_stats:increment_counter([couchdb, io_queue2, Class, count]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, count]),
     couch_stats:update_histogram([couchdb, io_queue2, submit_delay], Latency),
-    khash:put(Reqs, Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}),
-    State#state{iterations=Iterations+1}.
+    Reqs = maps:put(Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}, 
State#state.reqs),

Review Comment:
   Since we're deconstructing variables close to the function start, let's 
stick with that, otherwise `State#state.reqs` looks a bit out of place.
   
   If the head becomes too long, what I'd do is first match on the general 
record shape in the args, and then deconstruct each right below:
   
   ```erlang
   submit_request(#ioq_request{} = Req, #state{} = State} ->
      #ioq_request{fd = ..., } = Req,
      #state{reqs = Reqs, iterations = Iterations} = State,
   
   



##########
src/ioq_config.erl:
##########
@@ -174,7 +174,7 @@ set_config(Section, Key, Value, Reason) ->
     ok = config:set(Section, Key, Value, Reason).
 
 
--spec build_shard_priorities() -> {ok, khash:khash()}.
+-spec build_shard_priorities() -> {ok, #{any() => float()}}.

Review Comment:
   suggestion nit: define a type alias for the priority map in `ioq.hrl` so it 
might be something like `-type ioq_pmap() :: #{any() => float()}.`



##########
src/ioq_server2.erl:
##########
@@ -343,32 +337,32 @@ handle_cast(_Msg, State) ->
     {noreply, State, 0}.
 
 
-handle_info({Ref, Reply}, #state{reqs = Reqs} = State) ->
-    case khash:get(Reqs, Ref) of
-        undefined ->
-            ok;
-        #ioq_request{ref=Ref}=Req ->
-            ok = khash:del(Reqs, Ref),
+handle_info({Ref, Reply}, #state{reqs = Reqs} = State0) ->
+    State1 = case maps:find(Ref, Reqs) of
+        error ->
+            State0;
+        {ok, #ioq_request{ref = Ref} = Req} ->
             TResponse = os:timestamp(),
             ServiceTime = time_delta(TResponse, Req#ioq_request.tsub),
             IOWait = time_delta(TResponse, Req#ioq_request.t0),
             couch_stats:update_histogram(
                 [couchdb, io_queue2, svctm], ServiceTime),
             couch_stats:update_histogram([couchdb, io_queue2, iowait], IOWait),
             erlang:demonitor(Ref, [flush]),
-            send_response(State#state.waiters, Req, Reply)
+            State = send_response(State0, Req, Reply),
+            State#state{reqs = maps:remove(Ref, Reqs)}
     end,
-    {noreply, State, 0};
-handle_info({'DOWN', Ref, _, _, Reason}, #state{reqs = Reqs} = State) ->
-    case khash:get(Reqs, Ref) of
-        undefined ->
-            ok;
-        #ioq_request{ref=Ref}=Req ->
+    {noreply, State1, 0};
+handle_info({'DOWN', Ref, _, _, Reason}, #state{reqs = Reqs} = State0) ->
+    State1 = case maps:find(Ref, Reqs) of
+        error ->
+            State0;
+        {ok, #ioq_request{ref = Ref} = Req} ->
             couch_stats:increment_counter([couchdb, io_queue2, io_errors]),
-            ok = khash:del(Reqs, Ref),
-            send_response(State#state.waiters, Req, {'EXIT', Reason})
+            State = send_response(State0, Req, {'EXIT', Reason}),
+            State#state{reqs = maps:remove(Req, Reqs)}

Review Comment:
   Here I believe we'd want `maps:remove(Ref, Reqs`) as we're keying requests 
by Refs. If we miss this part we could leak requests as maps:remove(Req, Reqs) 
would silently fail as a non-op.
   



##########
src/ioq_server2.erl:
##########
@@ -472,15 +465,15 @@ submit_request(Req, #state{iterations=Iterations}=State) 
->
     couch_stats:increment_counter([couchdb, io_queue2, Class, count]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, count]),
     couch_stats:update_histogram([couchdb, io_queue2, submit_delay], Latency),
-    khash:put(Reqs, Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}),
-    State#state{iterations=Iterations+1}.
+    Reqs = maps:put(Ref, Req#ioq_request{tsub=SubmitTime, ref=Ref}, 
State#state.reqs),

Review Comment:
   Avoid using `maps:put/2` in general. In the Apache CouchDB code-base we 
usually use the syntactic update instead
   
   ```erlang
   Map#{Key := Val}
   ```
   If we also want to assert that the key should already exist and it's an 
update
   
   ```erlang
   Map#{Key => Val}
   ```
   If the key may not exist and it's an "upsert"



##########
src/ioq_server2.erl:
##########
@@ -506,20 +499,20 @@ enqueue_request(Req, #state{queue=HQ, 
waiters=Waiters}=State0) ->
     couch_stats:increment_counter([couchdb, io_queue2, queued]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, queued]),
 
-    case khash:get(State#state.waiters, ReqKey, not_found) of
-        not_found ->
+    Waiters = case maps:find(ReqKey, State#state.waiters) of

Review Comment:
   A simpler pattern instead of maps:find is to match the map directly
   
   ```erlang
   Waiters = case Waiters0 of
       #{ReqKey := Pids} -> % found;
       #{} -> % not found
   end
   ```



##########
src/ioq_server2.erl:
##########
@@ -506,20 +499,20 @@ enqueue_request(Req, #state{queue=HQ, 
waiters=Waiters}=State0) ->
     couch_stats:increment_counter([couchdb, io_queue2, queued]),
     couch_stats:increment_counter([couchdb, io_queue2, RW, queued]),
 
-    case khash:get(State#state.waiters, ReqKey, not_found) of
-        not_found ->
+    Waiters = case maps:find(ReqKey, State#state.waiters) of
+        error ->
             Priority = prioritize_request(Req, State),
             Req1 = Req#ioq_request{
                 key = ReqKey,
                 init_priority = Priority
             },
             hqueue:insert(HQ, Priority, Req1),
-            khash:put(State#state.waiters, ReqKey, [From]);
-        Pids ->
+            maps:put(ReqKey, [From], State#state.waiters);
+        {ok, Pids} ->
             couch_stats:increment_counter([couchdb, io_queue2, merged]),
-            khash:put(Waiters, ReqKey, [From | Pids])
+            maps:put(ReqKey, [From | Pids], State#state.waiters)

Review Comment:
   `Waiters0#{ReqKey := [From | Pids]}`



##########
IOQ2.md:
##########
@@ -373,7 +373,7 @@ module and are directly usable for easy testing. You can 
also see the full list
 of priority values from those priority data structures like so:
 
 ```erlang
-([email protected])14> khash:to_list(ShardP).
+([email protected])14> maps:to_list(ShardP).

Review Comment:
   Good call updating the readme!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to