[CXX-1389] Provide consistency to write concern Created: 10/Jul/17  Updated: 07/Feb/24

Status: Backlog
Project: C++ Driver
Component/s: API
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: Yoann Couillec Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bg-rf, rb-track
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: PNG File semantics.png     PNG File write_concern_1.png    
Epic Link: C++ Developer Experience Improvements

 Description   

in the current API. write concern are managed by only one class, beside the fact that several roles has been identified (https://docs.mongodb.com/manual/reference/write-concern/)

  • Majority
  • Not Acknowledged
  • Node
  • Tag

The consequence is that it leads to lack of consistency states for write concern objects.
Inconsistency managed by the driver. But in this case it's hard to cover all unexpected configuration. And If it happens, then the user faces an exception followed by a potential core dump.

Furthermore, the documentation of the API should explicitely provide which state to avoid ...

All that can be solved by building consistent states. And this can be obtained with a clean classes hierarchy that declare a class for each identified role.



 Comments   
Comment by Yoann Couillec [ 06/Oct/17 ]

Concerning the idea of immutable write concern, I definitely agree. This keep control of the encapsulated values.
A class hierarchy for write concerns could be

Comment by Yoann Couillec [ 06/Oct/17 ]

Hello Derick.

I am glad you ask.

General observation

  • While coding using cxx driver, I dove into the source code of both C and C++ drivers.
    From that, I saw a weakness in the design of write concerns, which can be generalized to read concerns, read preferences and options.

The main issue is the usage of all-in-one classes. A class that does everything.

The biggest issue is that it leads to inconsistent states.
And the way to get out of them is to check values to be sure being in a safe state.

But the checks can only be performed at runtime.
So if an inconsistent state is detected, either it raises an error, which is fair.
Or worse, it will continue, ignoring the inconsistent state and setting some fields to recover a safe state. As a consequence, the state has been internally modified without awareness of the user.

The problem can be solved by defining class hierarchies instead of all-in-one classes
The main advantage is that inconsistent states are checked at compile-time.
Another advantage is that you don't have to check if the state is safe or not.

A good demonstration is write concern.

Write concern

The ` w` field of write concerns

First of all, there are several identified levels of acknowledgements. However there is just one class/structure representing them all.
As a consequence, the unique class/structure has to expose all methods/functions concerning all roles and you can set parameters even if it has no sense.

The identified roles are:

  • unacknowledged. Setting journal to true/false makes sense while setting a timeout does not.
    Then you have an inconsistent state by setting a timeout with a `w` set to 0.
  • tags. Setting journal and timeout both make sense. but calling the nodes(int) function leads to inconsistent state.
    In that case you have a list of tags but the `w` field contains a number greater than 0.
  • nodes
  • majority
  • server (called default). The design choice for this one, is a bit surprising.
    To say that the server value should be used you have to leave the optional field unset.
    I don't see any justification in using optional here.
Specification

The specification defines `w` as:

w: Optional<Int32 | String>

While `String` concerns both tags write concerns and majority ones.
Int32 mixes two domains: the number of the nodes and the types of write concerns.

So actually, we have here 4 levels of interpretation:

  • the optional one: if it is set, it's a client write concern, if not, it's a server write concern
  • when the optional is set: it's a string or an int
  • when the optional is set and it's an integer: the int means the number of nodes
  • when the optional is set and it's a string: tag or majority write concern ...

Here is the semantics of this field

Source code of mongoc(xx)

To catch up the inconsistencies, a function has been defined (mongoc_write_concern_is_valid) with a respectable goal "Checks to see if @write_concern is valid and does not contain conflicting options."
Unfortunately, It does not catch all the cases...

When a call to `mongoc_write_concern_is_valid` is performed, the operation is abandoned and an exception is raised.

With a class hierarchy, there is no need to check inconsistency at runtime and no need to raise an exception.
All is checked at compile-time.
It's gain of time for designers and maintainers

Comment by Derick Rethans [ 28/Sep/17 ]

HI,

Sorry for only just getting to this.

Could you give an example of what can currently go wrong, in your opinion?

And, how the class hierarchy could work? Right now, the different levels don't really inherit from each other.

Do you think that an API as the PHP driver has makes more sense? (See: http://php.net/manual/en/mongodb-driver-writeconcern.construct.php)

cheers,
Derick

Generated at Wed Feb 07 22:02:28 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.