Skip to content

CDRIVER-4617 Refactor libmongoc to use mcd-rpc #1307

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

Merged
merged 18 commits into from
Jun 16, 2023

Conversation

eramongodb
Copy link
Contributor

@eramongodb eramongodb commented Jun 13, 2023

Description

Followup to #1296. This PR replaces mongoc_rpc_t with mcd_rpc_message in libmongoc. This PR deliberately leaves the (now unused) mongoc_rpc_t utilities to facilitate comparison of equivalent utilities and avoid messy diffs. The mongoc_rpc_t utilities will be removed in a followup commit.

Verified by this patch this patch (includes the commit addressing distinct pointer type warnings on Windows).

RPC Utility Functions

There are four RPC message utility functions in libmongoc that require new equivalents:

  • _mongoc_rpc_op_(egress|ingress)_inc -> mcd_rpc_message_(egress|ingress)
  • _mongoc_rpc_(compress|decompress) -> mcd_rpc_message_(compress|decompress)
  • _mongoc_rpc_decompress_if_necessary -> mcd_rpc_message_decompress_if_necessary
  • _mongoc_rpc_reply_get_first_msg -> mcd_rpc_message_get_body
  • _mongoc_rpc_check_ok -> mcd_rpc_message_check_ok

The new ingress and egress functions are fairly straightforward. Compared to the old mongoc_rpc_t equivalents which conflated the counters with conversion from data / to iovecs (partially addressed for the egress case by #1256), the mcd_rpc_message functions completely separate conversion (with from_data and to_iovecs) from counter increment (with egress and ingress).

The new compression functions (compress, decompress, and decompress_if_necessary) remove the dependency on mongoc_cluster_t by requesting the compressor level (derived from the cluster's URI) explicitly as a parameter instead. They also do not handle ingress/egress counter increments (as currently being done by the _mongoc_rpc_scatter function) to improve separation of concerns.

The get_first_document function was renamed to get_body to better reflect it's intended use (obtaining the Kind 0 section BSON document of an OP_MSG or the first document in an OP_REPLY). The function now properly handles the possibility of the Kind 0 section not being the first section in the OP_MSG, although this freedom does not appear to have been exercised yet by the server.

The check_ok function is a simple translation and does not require further elaboration.

mongoc_cluster_t::request_id

A drive-by fix to make the type of the request ID consistent with its specification and use.

Mock Server

The new mcd_rpc_message utilities already exposed inconsistent opcode handling of RPC messages that were addressed in #1280 (caught by mcd_rpc_op_query_get_flags in assert_request_matches_flags). This PR contains the changes that led to their discovery. Most of the changes involve improvements to type consistency, greater detail on message parsing failure, and refactors to simplify code (notably, removal of the redundant rpc parameter for the request_from_* functions).

Server Monitor Thread

A fairly straightforward one-to-one translation from mongoc_rpc_t to mcd_rpc_message equivalents. Nearby code was updated to improve readability.

mongoc-async-cmd

Also fairly straightforward one-to-one translation, but removed the redundant mongoc_async_cmd::array data member in favor of local variables to store the iovec structures used for async send operations. The existing mongoc_async_cmd::iovecs data member is used directly instead (now made an owning pointer).

mongoc_cluster_run_command_opquery

Refactored into _mongoc_cluster_run_command_opquery_send and _mongoc_cluster_run_command_opquery_recv. The old implementation used a truly bizarre algorithm when receiving the reply that assumes the message is an OP_REPLY, receiving the incoming bytes as if it is an OP_REPLY, then later realizing it may actually be an OP_COMPRESSED message instead, then reading the remaining bytes of the OP_COMPRESSED message. The implementation was changed to use the more consistent pattern of reading messageLength only first, before consuming all the remaining bytes of the incoming message in one go, then validating the opcode of the entire message, also aided by the much nicer-to-use _mongoc_buffer_append_from_stream interface.

mongoc_cluster_run_opmsg

Also refactored into separate _mongoc_cluster_run_opmsg_send and _mongoc_cluster_run_opmsg_recv functions. It's mostly straightforward translation with the exception of more thorough error-handling checks.

I remain fairly confused by the inconsistent use of RUN_CMD_ERR_DECORATE, _handle_network_error, server_stream->stream = NULL, and network_error_reply (reply, cmd) (including their ordering) in the original code. I would appreciate a closer look into their use to confirm they are being used correctly in the new functions.

Cursor Functions

The only major change of note that isn't straightforward one-to-one translation is the use of local iovecs instead of the mongoc_cluster_t::iov array object, a small refactor of some blocks of code into _mongoc_cursor_op_*_send functions, and improved type consistency.

@eramongodb eramongodb self-assigned this Jun 13, 2023
Copy link
Contributor

@vector-of-bool vector-of-bool left a comment

Choose a reason for hiding this comment

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

LGTM, and the new form is often significantly more readable than the prior version.

My only concern is about validation of received messages. I'm unsure how much of a concern it is, or if there are unseen checks that guard against firing assertions.

Copy link
Collaborator

@kevinAlbs kevinAlbs left a comment

Choose a reason for hiding this comment

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

Nicely done. LGTM with minor comments addressed.

break;

case MONGOC_OP_CODE_KILL_CURSORS:
BSON_UNREACHABLE ("unexpected MONGOC_OP_CODE_CURSORS ingress");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
BSON_UNREACHABLE ("unexpected MONGOC_OP_CODE_CURSORS ingress");
BSON_UNREACHABLE ("unexpected MONGOC_OP_KILL_CURSORS ingress");


static void
request_from_insert (request_t *request, const mongoc_rpc_t *rpc);
request_from_insert (request_t *request);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider removing unused mock server functions for receiving the legacy wire protocol:

  • mock_server_receives_insert and associated functions
  • mock_server_receives_bulk_insert and associated functions
  • mock_server_receives_update and associated functions
  • mock_server_receives_delete and associated functions
    - mock_server_receives_getmore and associated functions C driver still uses legacy get more for exhaust cursors in _mongoc_cursor_op_getmore_send.

With the minimum server version of 3.6, I expect the C driver can no longer send any of those protocols. I expect the changes to the mock server for these legacy protocols are not tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Even though C Driver still uses legacy OP_GET_MORE, it seems like the mock server receives function is nevertheless not being used anywhere, so will proceed with removing it anyways. Can easily be reverted if we end up needing a test that uses this assertion (hopefully unlikely).

mcd_rpc_op_compressed_get_compressed_message (rpc),
mcd_rpc_op_compressed_get_compressed_message_length (rpc),
ptr + message_header_length,
&actual_uncompressed_size)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
&actual_uncompressed_size)) {
&actual_uncompressed_size)) {
bson_free (ptr);

Comment on lines 3639 to 3644
RUN_CMD_ERR (MONGOC_ERROR_PROTOCOL,
MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
"message length %" PRId32
" is not within valid range of 16-%" PRId32 " bytes",
message_length,
server_stream->sd->max_msg_size);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
RUN_CMD_ERR (MONGOC_ERROR_PROTOCOL,
MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
"message length %" PRId32
" is not within valid range of 16-%" PRId32 " bytes",
message_length,
server_stream->sd->max_msg_size);
RUN_CMD_ERR (MONGOC_ERROR_PROTOCOL,
MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
"message length %" PRId32
" is not within valid range of %" PRId32 "-%" PRId32
" bytes",
message_header_length,
message_length,
server_stream->sd->max_msg_size);

Use message_header_length constant.

"message length %" PRId32
" is not within valid range of 16-%" PRId32 " bytes",
message_length,
server_stream->sd->max_msg_size);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use message_header_length:

      bson_set_error (error,
                      MONGOC_ERROR_PROTOCOL,
                      MONGOC_ERROR_PROTOCOL_INVALID_REPLY,
                      "message length %" PRId32
                      " is not within valid range of %" PRId32 " -%" PRId32
                      " bytes",
                      message_header_length,
                      message_length,
                      server_stream->sd->max_msg_size);

message_length +=
mcd_rpc_header_set_request_id (rpc, ++cluster->request_id);
message_length += mcd_rpc_header_set_response_to (rpc, 0);
message_length += mcd_rpc_header_set_op_code (rpc, MONGOC_OPCODE_MSG);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
message_length += mcd_rpc_header_set_op_code (rpc, MONGOC_OPCODE_MSG);
message_length += mcd_rpc_header_set_op_code (rpc, MONGOC_OP_CODE_MSG);

@kevinAlbs
Copy link
Collaborator

I remain fairly confused by the inconsistent use of RUN_CMD_ERR_DECORATE,
_handle_network_error, server_stream->stream = NULL, and
network_error_reply (reply, cmd) (including their ordering) in the original
code. I would appreciate a closer look into their use to confirm they are being
used correctly in the new functions.

It looks correct to me.

For some background on server_stream->stream = NULL: I think server_stream->stream = NULL; is to prevent reusing an invalidated mongoc_server_stream_t in functions that use the same mongoc_server_stream_t for multiple commands.
mongoc_cluster_stream_valid is used to determine if a mongoc_server_stream_t is invalid.

A network error may free the mongoc_server_stream_t::mongoc_stream_t:

  • For single threaded via: _handle_network_error => mongoc_cluster_disconnect_node => mongoc_topology_scanner_node_disconnect.
  • For pooled via: _handle_network_error => mongoc_cluster_disconnect_node => _mongoc_cluster_node_destroy.

Adding server_stream->stream = NULL to error handling in _mongoc_cluster_run_command_opquery and _mongoc_cluster_run_command_opquery_recv may be a reasonable precaution. But I expect it would not change any observable behavior. OP_QUERY is only used for exhaust cursors and the handshake. Neither use the same stream for multiple commands.

@eramongodb
Copy link
Contributor Author

eramongodb commented Jun 16, 2023

Latest changes verified by this patch this patch (with merge to address make-release-archive task failure).

@eramongodb eramongodb merged commit 6f58b9d into mongodb:master Jun 16, 2023
@eramongodb eramongodb deleted the cdriver-4617 branch June 16, 2023 18:43
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