Skip to content

Conversation

@jgates108
Copy link
Contributor

No description provided.

@jgates108 jgates108 changed the base branch from main to tickets/DM-43715 December 10, 2025 20:36
@jgates108 jgates108 marked this pull request as ready for review December 10, 2025 21:20
/// This class handles the message used to inform the czar that a result file
/// for an UberJob is ready.
class UberJobReadyMsg {
class UberJobReadyMsg : public UberJobStatusMsg {
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a general comment regarding the design of this class. It looks like this is just a hererogenous container for a bunch of data members. This is AKA the PDO (Plain Data Object). In this case, it may be unreasonable to maintain the complex interface that requires passing a bunch of values to the class's constructor and the factory method create. My recommendation is:

  • make the data members public
  • eliminate the constructor and allow filling in the data members directly by the client (note that you already have a special method for populating the data members from the JSON object. This method should stay.)
  • eliminate the static factory method create()

The second problem with the current design is that you have to pass 11 variables to the factory method and then to the constructor.

Problem number 3 is that both the factorial method (with 11 parameters) and the constructor (with 11 parameters) are public. Normally, if you want to deal with the shared pointers to objects of the class you would want to make the constructor private. Though I think it has to be eliminated as I suggested at the very beginning of this message.

Copy link
Contributor Author

@jgates108 jgates108 Dec 11, 2025

Choose a reason for hiding this comment

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

Edit: Commenting on multiple classes at once,

For UberJobStatusMsg:
Nearly all of the data members are const, and therefore must be set in a constructor.
The constructor is protected.
This is the base class for 2 other classes, which need the constructor, so the constructor cannot be private.
I agree the style needs to be fixed, and since all members are constant, making them public is sensible.

For UberJobReadyMsg:
const members need to be set in constructor, but probably should be public.
Constructor should be private.
There are 11 members because that's the information that needs to be put in the message. The sole purpose of this class is to encode and decode a fairly complicated message.

Copy link
Contributor

Choose a reason for hiding this comment

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

A constructor with 11 arguments is too heavy to read (by anyone trying to understand and validate your code), it's hard to use, and it's easy to make mistakes (swap strings or numbers). One solution to this problem would be to identify related parameters and group them into a simple PDO. Here is a good candidate:

struct AuthContext {
    std::string instanceId;
    std::string authKey;
    std::string adminAuthKey;
};

Then you can pass this object around by value:

UberJobReadyMsg const msg(AuthContext const& authContext, ...);

Moreover, you can get a value of AuthContext from some configuration service (worker/czar config), etc.

As far as the const-ness of the public data members of some POD is concerned, there are other ways to make them all const by:

UberJobReadyMsg const msg;

Or, if you have the shared pointer:

std::shared_ptr<UberJobReadyMsg const> ptr = ...;

This is up to you. I'm not insisting on my suggestions. My point is that in C++ not everything needs to be a full-blown class. Simple PDOs are often more adequate solutions. The full-blown classes are mostly needed when objects have a mutable/complex state and non-trivial behavior (methods), especially of the state is modified/used by mulriple threads.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The const has a big advantage in preventing accidental modification anywhere in the code.
Having everything defined in the constructor means that missing a value will cause a compilation error.

AuthContext method look like a good way to reduce the number of parameters in the constructor without losing the other benefits.

_czarName(czarName),
_czarId(czarId),
_queryId(queryId),
_uberJobId(uberJobId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like you pushed those members that start with the underscore (_uberJobId, etc.) into the base class by making them protected? In this case, you should not use the leading underscores in the names of the members. Please, see the LSST C++ Style Guide: https://developer.lsst.io/v/DM-6639/coding/cpp_style_guide.html#names-representing-methods-or-functions-should-naturally-describe-the-action-of-the-method-and-so-will-usually-start-with-a-verb-and-must-be-written-in-mixed-case-starting-with-lowercase-private-member-function-names-must-additionally-lead-with-an-underscore

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'm not particular happy with that aspect of the style guide as changing scope makes it very easy to shadow variables, so I delayed a bit too long in making those changes.

Copy link
Contributor

Choose a reason for hiding this comment

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

You still have plenty of time to make everything right.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are among the things I've been fixing today...

std::string const _czarName;
CzarId const _czarId;
QueryId const _queryId;
UberJobId const _uberJobId;
Copy link
Contributor

Choose a reason for hiding this comment

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

The names of the protected members should not begin with underscores. This is a requirement of the LSST DM C++ Style Guide. Only the private members can.

I would also make these members public and significantly simplify the design of this class and its dependencies by allowing a client to read/write the data members directly. By design, this is just the PDO (Plain Data Object), nothing more.

UberJobStatusMsg& operator=(UberJobStatusMsg const&) = delete;
virtual ~UberJobStatusMsg() = default;

virtual std::shared_ptr<nlohmann::json> toJsonPtr() const = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

A comment regarding returning shared_ptr. That's fine if the rest of the code expects and benefits from shared ownership. If callers don't need shared_ptr, returning JSON by value can be simpler and often cheaper due to move semantics.

UberJobId getUberJobId() const { return _uberJobId; }

/// Returns a string for logging.
virtual std::ostream& dumpOS(std::ostream& os) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't have to name this function differently. C++ allows selective overloading if you define both methods as:

virtual std::ostream& dump(std::ostream& os) const;
std::string dump() const;

jsJr["rowCount"] = _rowCount;
jsJr["fileSize"] = _fileSize;
return jsJr;
return jsJrReqPtr;
Copy link
Contributor

Choose a reason for hiding this comment

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

It's simple to return a value rather than the shared pointer:

json UberJobReadyMsg::toJsonPtr() const {
    return json::object({
        {"fileUrl", fileUrl},
        {"rowCount", _rowCount},
        {"fileSize", _fileSize;}
    });
 }

There won't be any performance penalty due to the value (by move) return optimization

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original idea was to just store copies of the json messages and resend them. It got messy, so I dropped it, but using pointers allowed me to send the message and keep the same message in the map without extra copies. Removing the pointers does make the code simpler.

Copy link
Contributor

@iagaponenko iagaponenko left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@fritzm fritzm left a comment

Choose a reason for hiding this comment

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

LGTM -- thanks!

@jgates108 jgates108 merged commit c27b543 into tickets/DM-43715 Dec 15, 2025
8 checks passed
@jgates108 jgates108 deleted the tickets/DM-53242 branch December 15, 2025 16:45
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.

4 participants