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

mongoose_c2s prototype #3729

Merged
merged 56 commits into from
Sep 20, 2022
Merged

mongoose_c2s prototype #3729

merged 56 commits into from
Sep 20, 2022

Conversation

NelsonVides
Copy link
Collaborator

This PR prepares the backbone of this effort. I define here the interface for the new mongoose_c2s in a way that I'm confident allows implementing everything else (presences, roster, stream management, ping, privacy lists, as plug&play modules that hook in the eventing interface.

What has been done in this PR:

  • Using ranch instead of mongoose_tcp_listener: take advantage of the optimised supervision trees and linux port capabilities.
  • Unify ejabberd_c2s and ejabberd_receiver: decrypt and parse on the spot, avoiding more message passing.
  • Move the c2s implementation to gen_statem: specially take advantage of the gen_statem:actions() capabilities.
  • Implement the latest RFC6120: skip redundant roundtrip for session, make presences independent of messaging.

For implementation detail, see the explanations in the internal documentation.

@mongoose-im

This comment was marked as outdated.

@codecov
Copy link

codecov bot commented Aug 11, 2022

Codecov Report

Base: 82.73% // Head: 13.58% // Decreases project coverage by -69.15% ⚠️

Coverage data is based on head (3d7a35a) compared to base (6b39a79).
Patch coverage: 47.27% of modified lines in pull request are covered.

Additional details and impacted files
@@                    Coverage Diff                    @@
##           feature/mongoose_c2s    #3729       +/-   ##
=========================================================
- Coverage                 82.73%   13.58%   -69.16%     
=========================================================
  Files                       529      535        +6     
  Lines                     33957    34535      +578     
=========================================================
- Hits                      28094     4690    -23404     
- Misses                     5863    29845    +23982     
Impacted Files Coverage Δ
src/ejabberd_sm.erl 31.92% <0.00%> (-52.67%) ⬇️
src/gen_hook.erl 70.45% <ø> (-14.78%) ⬇️
src/global_distrib/mod_global_distrib_receiver.erl 0.00% <0.00%> (-80.73%) ⬇️
src/mongoose_hooks.erl 14.14% <ø> (-81.32%) ⬇️
src/sasl/cyrsasl.erl 72.72% <ø> (-18.19%) ⬇️
src/c2s/mongoose_c2s_socket.erl 22.05% <22.05%> (ø)
src/c2s/mongoose_c2s_acc.erl 29.62% <29.62%> (ø)
src/c2s/mongoose_c2s.erl 46.73% <46.73%> (ø)
src/c2s/mongoose_c2s_hooks.erl 58.06% <58.06%> (ø)
src/c2s/mongoose_c2s_stanzas.erl 65.85% <65.85%> (ø)
... and 491 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

This time the listener is supervised using ranch, which already does
everything our mongoose_tcp_listener does, plus a few more details like
using pools of supervisors instead of all the many c2s processes having
to be started and handled their termination through a single supervisor
that can become a bottleneck. Using ranch of course also means one piece
of our architecture that we don't need to maintain, but that the
industry already maintains and optimise for us. Open source!

The new mongoose_c2s_listener module implements our mongoose_listener
behaviour, that gives mongoose_listener_sup a child_spec with the ranch
definitions.
@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

The flow is as follows:

init casts a `connect` message, to indicate the process that it needs to
establish the tcp connection, outside of the init function that blocks
the supervisor.

Then all states are handled in a single handle_event. The event connect
in state connecting indicates to end negotiating the connection. The
event info with payload from tcp or ssl, regardless of the state, simply
parses the payload using exml_stream, and adds all parsed packets as
internal action events of the OTP's gen_statem behaviour, that is, they
will be processed immediately as the next event in the state machine,
without involved message passing.

Then, as internal events, all packets are processed, according to their
running state machine state. One specific event is expected in that
state, and an appropriate handle_* helper does the work. States can be
compound, so now instead of sticking data of a specific state into the
state-machine memory, we can make it part of the state itself, and it
will be used in the next event in that state, see `{wait_for_feature,
after auth, _}` and `{wait_for_feature, before_auth, _}`.

Code that specifies stanzas is moved to a helper module that construct
all the verbose #xmlel{} packets, to keep the c2s module smaller and
separate protocol from state machine.
@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@NelsonVides
Copy link
Collaborator Author

As of now, in small-tests only websockets are failing, because they look at internals of ranch and ejabberd_sm, which are now different. So we could either ignore that error or manually comment out those test, and fix it in a subsequent PR.

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, I just added a few minor comments.

src/c2s/mongoose_c2s.erl Show resolved Hide resolved
FinalEl = jlib:replace_from_to(From, To, El),
ParamsAcc = #{from_jid => From, to_jid => To, element => FinalEl},
Acc1 = mongoose_acc:update_stanza(ParamsAcc, Acc),
case mongoose_c2s_hooks:user_receive_packet(HostType, Acc1, hook_arg(StateData)) of
Copy link
Member

@chrzaszcz chrzaszcz Sep 20, 2022

Choose a reason for hiding this comment

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

I agree that it might be cleaner not to have to pattern match for the specific stanza types in handlers. I'm just not convinced that it's faster, because for each message we need to run two hooks instead of one, and calling a hook takes some time, including reading the ETS table. Anyway, we've already discussed this, and the reviewed version is certainly good enough for me.

src/c2s/mongoose_c2s.erl Show resolved Hide resolved
src/config/mongoose_config_spec.erl Outdated Show resolved Hide resolved
src/config/mongoose_config_spec.erl Show resolved Hide resolved
<<"proxy_protocol">> => false,
<<"hibernate_after">> => 0,
<<"max_stanza_size">> => infinity,
Copy link
Member

Choose a reason for hiding this comment

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

Why change it? I think it makes more sense than after the change, and we use the atom infinity quite consistently throughout the code. OTP also uses it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This one because the process function is ran after the defaults, not before, so if I leave infinity as default, then c2s will get an infinity and will have to do the conversion for exml.

Copy link
Member

@chrzaszcz chrzaszcz Sep 20, 2022

Choose a reason for hiding this comment

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

I meant "why not keep infinity". I see that exml uses zero. Let's keep it.

@mongoose-im
Copy link
Collaborator

mongoose-im commented Sep 20, 2022

dynamic_domains_pgsql_mnesia_24 / pgsql_mnesia / 3d7a35a
Reports root/ big
OK: 7 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


ldap_mnesia_24 / ldap_mnesia / 3d7a35a
Reports root/ big
OK: 4 / Failed: 0 / User-skipped: 6 / Auto-skipped: 0


pgsql_mnesia_24 / pgsql_mnesia / 3d7a35a
Reports root/ big
OK: 9 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


riak_mnesia_24 / riak_mnesia / 3d7a35a
Reports root/ big
OK: 4 / Failed: 0 / User-skipped: 6 / Auto-skipped: 0


ldap_mnesia_25 / ldap_mnesia / 3d7a35a
Reports root/ big
OK: 4 / Failed: 0 / User-skipped: 6 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_25 / pgsql_mnesia / 3d7a35a
Reports root/ big
OK: 7 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


internal_mnesia_25 / internal_mnesia / 3d7a35a
Reports root/ big
OK: 4 / Failed: 0 / User-skipped: 6 / Auto-skipped: 0


mysql_redis_25 / mysql_redis / 3d7a35a
Reports root/ big
OK: 9 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


dynamic_domains_mysql_redis_25 / mysql_redis / 3d7a35a
Reports root/ big
OK: 7 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


pgsql_mnesia_25 / pgsql_mnesia / 3d7a35a
Reports root/ big
OK: 9 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


elasticsearch_and_cassandra_25 / elasticsearch_and_cassandra_mnesia / 3d7a35a
Reports root/ big
OK: 4 / Failed: 0 / User-skipped: 6 / Auto-skipped: 0


dynamic_domains_mssql_mnesia_25 / odbc_mssql_mnesia / 3d7a35a
Reports root/ big
OK: 7 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


mssql_mnesia_25 / odbc_mssql_mnesia / 3d7a35a
Reports root/ big
OK: 9 / Failed: 0 / User-skipped: 1 / Auto-skipped: 0


small_tests_24 / small_tests / 3d7a35a
Reports root / small


small_tests_25 / small_tests / 3d7a35a
Reports root / small

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 🆗

@chrzaszcz chrzaszcz merged commit 05df7d1 into feature/mongoose_c2s Sep 20, 2022
@chrzaszcz chrzaszcz deleted the c2s_prototype branch September 20, 2022 13:10
@jacekwegr jacekwegr added this to the 6.1.0 milestone Apr 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants