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

[RIP-37] Add new APIs for producer #3987

Merged
merged 1 commit into from
Mar 18, 2022
Merged

Conversation

aaron-ai
Copy link
Member

As we mentioned in RIP-37 New and unified APIs, establish a new APIs specifications. we divide it into two independent pull requests.

  1. The producer part.
  2. The consumer part.

this pull request is about the producer part.

related issue: #3973

@aaron-ai
Copy link
Member Author

aaron-ai commented Mar 14, 2022

@RongtongJin RongtongJin changed the title Add new APIs for producer [RIP-37] Add new APIs for producer Mar 14, 2022
Copy link
Member

@dongeforever dongeforever left a comment

Choose a reason for hiding this comment

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

Except for the admin operation, the abilities of the new API should be a superset of the old API.

* @return message group, which is optional.
*/
Optional<String> getMessageGroup();

Copy link
Member

Choose a reason for hiding this comment

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

This interface need a method getMessageQueue() too.
It may need to send message to the specified MessageQueue.
The case cannot be covered by getMessageGroup().

The priority of getMessageQueue() is bigger than getMessageGroup().

Copy link
Member Author

Choose a reason for hiding this comment

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

Message here is for producer, user may build message before sending, so Message#getMessageQeue may be not appropriate, but we may consider to add it into the return value of producer sending method.

Copy link
Member

Choose a reason for hiding this comment

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

I repeat this:
Except for the admin operation, the abilities of the new API should be a superset of the old API.

The old API could send messages to the specified message queue?

So how to do it in the new API?

Copy link
Member

Choose a reason for hiding this comment

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

the abilities of the new API should be a superset of the old API

We don't follow this rule, many useless or low-frequency abilities will be removed in the new APIs, the new APIs don't expand the old APIs.

As for sending messages to a specific queue, we were going to add it in the future version. Consider MessageQueue is a major model of RocketMQ, we may need to add it in the first version. But, I am not sure it's the right choice, hope more contributors could express opinions on this topic, especially from user perspectives.

* @throws PersistenceException if encountered persistence failure from server.
* @throws TransactionCheckerNotSetException if {@link TransactionChecker} is not set.
*/
MessageId send(Message message, Transaction transaction) throws ClientException;
Copy link
Member

Choose a reason for hiding this comment

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

This is not the two-phase API!

How about like this :
try {
TransactionMark mark = producer.prepareSend(Message message);
//do process
producer.commit(mark);
} catch(Exception e) {
producer.rollback(mark);
}

Such style is easy to be integrated with db transaction. And the code will not be split.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, we may send more than one message in single transaction.

Copy link
Member

Choose a reason for hiding this comment

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

How to send more than one message using such API?
MessageId send(Message message, Transaction transaction)

The old API for the transaction is not naturally two-phase based. It's better to polish it in the new API.
But currently, the new API is just like the old

Copy link
Member Author

Choose a reason for hiding this comment

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

How to send more than one message using such API? MessageId send(Message message, Transaction transaction)

The old API for the transaction is not naturally two-phase based. It's better to polish it in the new API. But currently, the new API is just like the old

The code example has been added to the javadocs.

* @throws NetworkConnectionException if there is a network connection problem.
* @throws PersistenceException if encountered persistence failure from server.
*/
MessageId send(Message message) throws ClientException;
Copy link
Member

Choose a reason for hiding this comment

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

Where is the SendResult?

The client at least needs to know the offset and messagequeue for checking!

Copy link
Member Author

Choose a reason for hiding this comment

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

SendResult is not essential, because exception may be a better way to indicate the result, but offset and message queue may be considered.

@aaron-ai aaron-ai force-pushed the producer_pr branch 2 times, most recently from cdd3362 to bc2a1b9 Compare March 14, 2022 12:00
@aaron-ai aaron-ai force-pushed the producer_pr branch 2 times, most recently from e06beb9 to 54fa721 Compare March 14, 2022 12:47
* @param topic topic for the message.
* @return the message builder instance.
*/
MessageBuilder setTopic(String topic);
Copy link
Contributor

Choose a reason for hiding this comment

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

if we remove set prefix the api will be more clean to the developer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, setter/getter style is more common in java, MessageBuilder#topic() or MessageBuilder#topic(string topicName) may be more common in cpp.

*
* @return string-formed string id.
*/
String toString();
Copy link
Contributor

Choose a reason for hiding this comment

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

this name is the same as the java origin Object.toString. i think we can change the method name. the origin is anti-parttern naming.

Copy link
Member Author

@aaron-ai aaron-ai Mar 15, 2022

Choose a reason for hiding this comment

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

#toString() here aims to emphasize that the implementation must override this method, maybe there is a better solution?

*
* @return the <strong>deep copy</strong> of message body.
*/
byte[] getBody();
Copy link
Contributor

Choose a reason for hiding this comment

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

deep copy seems will harm the performance. but there seems no better return type here.

Copy link
Contributor

Choose a reason for hiding this comment

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

how about ByteBuffer here which can be set as unmodifable

Copy link
Member Author

Choose a reason for hiding this comment

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

how about ByteBuffer here which can be set as unmodifable

Nice suggestion.

* @throws TransactionCheckerNotSetException if {@link TransactionChecker} is not set.
* @throws ProducerClosedAlreadyException if producer is closed already.
*/
Transaction beginTransaction() throws ClientException;
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe an interface extand producer add some Transaction relate method will be better.

Copy link
Member

Choose a reason for hiding this comment

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

Could you please provide some code samples? @WJL3333

*/
@SuppressWarnings("UnstableApiUsage")
@Override
void close();
Copy link
Contributor

Choose a reason for hiding this comment

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

will close throw exception here?

* @param clientConfiguration client's configuration.
* @return the producer builder instance.
*/
ProducerBuilder setClientConfiguration(ClientConfiguration clientConfiguration);
Copy link
Contributor

Choose a reason for hiding this comment

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

same as Messagebuilder. no set prefix will make api more clean in fluent api call.

Copy link
Member

Choose a reason for hiding this comment

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

We can find many fluent builder samples in JDK which use the setter pattern, such as https://docs.oracle.com/javase/7/docs/api/java/util/Locale.Builder.html

Copy link
Member

Choose a reason for hiding this comment

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

@WJL3333 Lombok doesn't support prefixed builders at the beginning, but this project resolved this issue later, please refer to projectlombok/lombok#1805 (comment), there are many reasons support we use prefixed builders.

* @throws NetworkTimeoutException if encountered network timeout to communicate with server.
* @throws NetworkConnectionException if there is a network connection problem.
*/
Producer start() throws ClientException;
Copy link
Contributor

Choose a reason for hiding this comment

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

may be move start to the producer.
some case will just build the producer without start.

Copy link
Member Author

@aaron-ai aaron-ai Mar 15, 2022

Choose a reason for hiding this comment

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

Actually producerBuilder#start could avoid to start producer repeatedly. What's more, a producer which is not started does not make sense.

Copy link
Member

Choose a reason for hiding this comment

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

It's strange if we don't have a build method in a builder interface.

If we only provide a start method in the producer builder, that means all the producers are started by default, so why we should have a start?

Copy link
Member Author

@aaron-ai aaron-ai Mar 15, 2022

Choose a reason for hiding this comment

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

It's strange if we don't have a build method in a builder interface.

If we only provide a start method in the producer builder, that means all the producers are started by default, so why we should have a start?

I agree with the opinon that lack of #build() may be a little bit weird, let's look at it another way. Is it unnecessary for the user to start producer manually? If so, then I think it's just a naming issue.

* <strong>actually we omit the exception on purpose because {@link TransactionChecker} is the unique right way
* to solve the suspended transaction rather than commit or roll-back repeatedly.</strong>
*/
void commit();
Copy link
Contributor

Choose a reason for hiding this comment

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

will commit throw exception need the caller process?

Copy link
Member

Choose a reason for hiding this comment

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

TransactionChecker is less real-time, which may result in high latency when the commit failed.

@aaron-ai aaron-ai force-pushed the producer_pr branch 2 times, most recently from e36e9ef to 62a1f28 Compare March 15, 2022 09:18
/**
* Builder to config and start {@link Producer}.
*/
public interface ProducerBuilder {
Copy link
Member

Choose a reason for hiding this comment

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

Do we have a chance to force our developers to set the required fields when building the Producer or Message?

Copy link
Contributor

Choose a reason for hiding this comment

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

Builder should throw null field check exception when call build interface.

* @throws PersistenceException if encountered persistence failure from server.
* @throws TransactionCheckerNotSetException if {@link TransactionChecker} is not set.
*/
MessageView send(Message message, Transaction transaction) throws ClientException;
Copy link
Member

Choose a reason for hiding this comment

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

Tons of exceptions are thrown, it's daunting.

I suggest we only declare a ClientException with managed code, request-id, message, etc.

These exceptions are unchecked, which means we don't want our users to use these exceptions to control their logic, so a simple ClientException is enough.

Copy link
Member Author

@aaron-ai aaron-ai Mar 15, 2022

Choose a reason for hiding this comment

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

These exceptions are unchecked, which means we don't want our users to use these exceptions to control their logic, so a simple ClientException is enough.

First of all, exceptions are inevitable. In fact, about the issue that whether to use checked exception or not, maybe no need to prevent users to do responding operation by the exception thrown deliberately? For example, users just want to count how many times the network timeout exception occurred.

In addition, about the issue to use error codes or specific exception, actuall they can all solve the problem in theory, here's my concern

  1. It is very easy to abuse error code.
  2. Not all exceptions will have the same parameters, such as requestId, they may only exist in some exceptions.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.08%) to 49.592% when pulling d42aa99 on aaron-ai:producer_pr into 176e0d5 on apache:5.0.0-beta.

@coveralls
Copy link

coveralls commented Mar 17, 2022

Coverage Status

Coverage decreased (-0.008%) to 49.654% when pulling d30f3f5 on aaron-ai:producer_pr into 0b7291b on apache:5.0.0-beta.

@aaron-ai aaron-ai force-pushed the producer_pr branch 2 times, most recently from cbcb38c to fc90eac Compare March 17, 2022 13:46
@aaron-ai aaron-ai requested a review from zhouxinyu March 17, 2022 16:20
@aaron-ai aaron-ai force-pushed the producer_pr branch 2 times, most recently from c529eec to 23fa2c2 Compare March 18, 2022 02:10
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.

9 participants