Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial code #1

Merged
merged 64 commits into from
Mar 23, 2023
Merged

Add initial code #1

merged 64 commits into from
Mar 23, 2023

Conversation

arcusfelis
Copy link
Contributor

@arcusfelis arcusfelis commented May 9, 2022

Check readme for description

Copy link
Member

@pawlooss1 pawlooss1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 👍 I left some comments that I think could improve readability and maintainability.

Comment on lines 35 to 42
case Catch of
true -> just_run_safely(Info#{what => long_task_failed}, Fun);
false -> Fun()
end
after
Diff = diff(Start),
?LOG_INFO(Info#{what => long_task_finished, time_ms => Diff}),
Pid ! stop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indent is unnecessary big. I see a similar case in some other places as well. What do you think about plugging in some code formatter, such as https://github.com/WhatsApp/erlfmt? I think it could help us keep the formatting consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have so many questions to erlfmt :)
At least for nodejs formatters you can configure the style.

That aligning when when is used is kinda wtf:

resolve(#amp_strategy{deliver = [Value | _]}, #amp_rule{
    condition = deliver, value = Value, action = Action
}) when
    Action == drop orelse Action == error
->
    match;
resolve(#amp_strategy{'match-resource' = Value}, #amp_rule{
    condition = 'match-resource', value = any
}) when
    Value /= undefined
->
    match;
resolve(#amp_strategy{'match-resource' = Value}, #amp_rule{
    condition = 'match-resource', value = Value
}) ->

And brackets:

    CommandTags = ets:match(
        ejabberd_commands,
        #ejabberd_commands{
            name = '$1',
            tags = '$2',
            _ = '_'
        }
    ),

Maybe we should use it in mongooseim ;)

Pid = spawn_mon(Info, Parent, Start),
try
case Catch of
true -> just_run_safely(Info#{what => long_task_failed}, Fun);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting what => long_task_failed in this line suggests that our task has already failed. I think it would be more straightforward if we just put it directly in the logging in line 71.

src/cets_call.erl Outdated Show resolved Hide resolved
src/cets_call.erl Outdated Show resolved Hide resolved
src/cets_call.erl Outdated Show resolved Hide resolved
rebar.config Outdated
no_opaque, no_fail_call, no_contracts, no_behaviours,
no_undefined_callbacks, unmatched_returns, error_handling,
underspecs
% overspecs, specdiffs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either remove or uncomment this line.

src/cets.app.src Outdated
{registered, []},
{applications, [kernel, stdlib]},
{env, []}
% {mod, {cets_app, []}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either remove or uncomment this line.

src/cets_discovery_file.erl Show resolved Hide resolved
Info2 = Info#{local_pid => LocalPid,
remote_pid => RemotePid, remote_node => node(RemotePid)},
F = fun() -> join1(LockKey, Info2, LocalPid, RemotePid) end,
cets_long:run_safely(Info2#{what => join_failed}, F).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I get this correctly, what key of this map is going to be overwritten in cets_long.erl in every logging line. Could you check it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I get this correctly, what key of this map is going to be overwritten in cets_long.erl in every logging line. Could you check it?

What's more, this can be misleading especially when debugging/tracing. Finding what => join_failed in the arguments would suggest that something has already failed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, there will be more cets_long calls inside this cets_long call. I don't think we need doubled monitors. Maybe skip the outer cets_long entirely and just catch errors?

end_per_testcase(_, _Config) ->
ok.

test_multinode(Config) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test checks a few thing, e.g. joining nodes (both before and after adding data), inserting values, deleting values, bulk operations. Could you split it to minimal test cases where each one tests exactly one thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added more tests for each operation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but it just doesn't help with the readability of this particular one.

Copy link
Member

@chrzaszcz chrzaszcz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks promising 🙂 I added some comments.

README.md Outdated
- `delete(Server, Key)` - deletes an object from the table.
- `delete_many(Server, Keys)` - deletes several objects from the table.

cets_join module contains the merging logic.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cets_join module contains the merging logic.
`cets_join` module contains the merging logic.

README.md Outdated

cets_join module contains the merging logic.

cets_discovery module handles search of new nodes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cets_discovery module handles search of new nodes.
`cets_discovery` module handles search of new nodes.

...and it would be good to format the remainder of this page as well.

src/cets.erl Outdated
stop(Server) ->
gen_server:stop(Server).

-spec dump(server_ref()) -> Records :: [tuple()].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-spec dump(server_ref()) -> Records :: [tuple()].
-spec dump(table_name()) -> Records :: [tuple()].

run_test.sh Outdated Show resolved Hide resolved
src/cets.erl Outdated Show resolved Hide resolved
.gitignore Outdated
@@ -0,0 +1,4 @@
_build/
rebar3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no rebar3 in the repo.

src/cets.erl Outdated
replicate(From, Msg, #{mon_tab := MonTab, other_servers := Servers}) ->
%% Reply would be routed directly to FromPid
Msg2 = {remote_op, From, Msg},
replicate2(Servers, Msg2),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we gain anything by the increased amount of code and the additional function with a 2 suffix. lists:foreach is also fine for me - easier to read, debug, understand...

Suggested change
replicate2(Servers, Msg2),
[send_to_remote(RemotePid, Msg2) || RemotePid <- Servers],

src/cets.erl Outdated
ok.

apply_backlog(State = #{backlog := Backlog}) ->
apply_backlog_ops(lists:reverse(Backlog), State),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

src/cets_discovery.erl Show resolved Hide resolved
State#{timer_ref := TimerRef}.

cancel_old_timer(#{timer_ref := OldRef}) when is_reference(OldRef) ->
_ = erlang:cancel_timer(OldRef),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of this pattern match? It has no effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prevents from dialyzer warning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which option triggers this warning? I see a lot of them enabled. Sometimes it might be better to disable a check if it forces us to write unnecessary code.

init({Tab, Opts}) ->
process_flag(message_queue_data, off_heap),
MonTab = list_to_atom(atom_to_list(Tab) ++ "_mon"),
_ = ets:new(Tab, [ordered_set, named_table, public]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another note: why ordered_set? It slows down read time. Also: how about allowing some customisation instead of hardcoding the table type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ordered_set is required for ejabberd_sm to work - for reads all sessions of the user, for example.

Address some review comments
Test each operation of test_multinode separately
prevent cets from killing the processes (and losing ETS data) in the event of accidentally calling it with LocalPid same as RemotePid
Expose mon_pid in cets:info
Copy link
Member

@chrzaszcz chrzaszcz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

Comment on lines +63 to +64
{ok, LocalDump} = remote_or_local_dump(LocalPid),
{ok, RemoteDump} = remote_or_local_dump(RemotePid),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually in some tests the local one is the remote one, they are swapped, that's what I meant.

end_per_testcase(_, _Config) ->
ok.

test_multinode(Config) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but it just doesn't help with the readability of this particular one.

{ok, Pid2} = start(Node2, Tab),
{ok, Pid3} = start(Node3, Tab),
{ok, Pid4} = start(Node4, Tab),
ok = join(Node1, Tab, Pid3, Pid1),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This happens a few times: the first pid is remote, and the second is local, while in the join function it is the other way around. Maybe don't swap the order to make it easier to understand?

@chrzaszcz chrzaszcz merged commit d90265d into main Mar 23, 2023
@NelsonVides NelsonVides deleted the rebased branch July 26, 2023 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants