From 77306f39ecdfae60316fe6e75f51c778ddc93591 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 8 Jul 2021 19:25:01 +0800 Subject: [PATCH 001/133] Initial commit --- .bazelignore | 2 + .bazelrc | 55 + .clang-format | 18 + .clang-tidy | 81 + .gitignore | 16 + .gitlab-ci.yml | 16 + .travis.yml | 173 + BUILD | 0 CMakeLists.txt | 172 + README.md | 61 + WORKSPACE | 17 + api/BUILD.bazel | 8 + api/CMakeLists.txt | 4 + api/rocketmq/AdminServer.h | 25 + api/rocketmq/AsyncCallback.h | 32 + api/rocketmq/CommunicationMode.h | 14 + api/rocketmq/ConsumerType.h | 57 + api/rocketmq/Credentials.h | 42 + api/rocketmq/CredentialsProvider.h | 85 + api/rocketmq/DefaultMQProducer.h | 107 + api/rocketmq/DefaultMQPullConsumer.h | 47 + api/rocketmq/DefaultMQPushConsumer.h | 100 + api/rocketmq/ErrorCode.h | 23 + api/rocketmq/ExpressionType.h | 10 + api/rocketmq/Logger.h | 56 + api/rocketmq/MQClientException.h | 72 + api/rocketmq/MQMessage.h | 61 + api/rocketmq/MQMessageExt.h | 49 + api/rocketmq/MQMessageListener.h | 35 + api/rocketmq/MQMessageQueue.h | 115 + api/rocketmq/MQSelector.h | 15 + api/rocketmq/OffsetStore.h | 18 + api/rocketmq/PullResult.h | 29 + api/rocketmq/RocketMQ.h | 8 + api/rocketmq/SendResult.h | 74 + api/rocketmq/State.h | 11 + api/rocketmq/Transaction.h | 18 + bazel/BUILD | 0 bazel/curl.bzl | 201 + bazel/rocketmq_deps.bzl | 127 + ci/build.sh | 10 + docs/DevelopmentGuide.md | 18 + docs/gtest.md | 8 + docs/test_coverage.md | 15 + example/CMakeLists.txt | 1 + example/rocketmq/BUILD.bazel | 89 + example/rocketmq/BenchmarkPushConsumer.cpp | 65 + example/rocketmq/CMakeLists.txt | 65 + example/rocketmq/ExampleAsyncProducer.cpp | 229 + .../rocketmq/ExampleBroadcastPushConsumer.cpp | 48 + example/rocketmq/ExampleProducer.cpp | 87 + example/rocketmq/ExamplePullConsumer.cpp | 36 + example/rocketmq/ExamplePushConsumer.cpp | 47 + .../PushConsumerWithCustomExecutor.cpp | 131 + example/rocketmq/PushConsumerWithThrottle.cpp | 66 + example/rocketmq/SqlConsumer.cpp | 44 + example/rocketmq/SqlProducer.cpp | 44 + proto/BUILD.bazel | 111 + proto/apache/rocketmq/v1/admin.proto | 145 + proto/apache/rocketmq/v1/definition.proto | 292 + proto/apache/rocketmq/v1/service.proto | 266 + proto/google/protobuf/any.proto | 158 + proto/google/protobuf/duration.proto | 116 + proto/google/protobuf/empty.proto | 52 + proto/google/protobuf/field_mask.proto | 245 + proto/google/protobuf/timestamp.proto | 147 + proto/google/rpc/code.proto | 186 + proto/google/rpc/error_details.proto | 249 + proto/google/rpc/status.proto | 47 + rpm/rocketmq-cpp.sh | 6 + rpm/rocketmq-cpp.spec | 75 + src/main/cpp/CMakeLists.txt | 74 + src/main/cpp/admin/AdminClient.cpp | 95 + src/main/cpp/admin/AdminFacade.cpp | 8 + src/main/cpp/admin/AdminServerImpl.cpp | 102 + src/main/cpp/admin/AdminServiceImpl.cpp | 49 + src/main/cpp/admin/BUILD.bazel | 43 + src/main/cpp/admin/CMakeLists.txt | 44 + src/main/cpp/admin/include/AdminClient.h | 29 + src/main/cpp/admin/include/AdminServerImpl.h | 43 + src/main/cpp/admin/include/AdminServiceImpl.h | 20 + src/main/cpp/admin/include/ServerCall.h | 59 + src/main/cpp/base/BUILD.bazel | 65 + src/main/cpp/base/CMakeLists.txt | 57 + src/main/cpp/base/HostInfo.cpp | 59 + src/main/cpp/base/HttpClient.cpp | 67 + src/main/cpp/base/MQMessage.cpp | 212 + src/main/cpp/base/MessageSystemFlag.cpp | 13 + src/main/cpp/base/MixAll.cpp | 183 + src/main/cpp/base/PermissionHelper.cpp | 15 + src/main/cpp/base/Protocol.cpp | 11 + src/main/cpp/base/RateLimiter.cpp | 38 + src/main/cpp/base/RequestIdGenerator.cpp | 44 + src/main/cpp/base/TopAddressing.cpp | 58 + src/main/cpp/base/UtilAll.cpp | 219 + src/main/cpp/base/include/Functional.h | 28 + src/main/cpp/base/include/Histogram.h | 63 + src/main/cpp/base/include/HostInfo.h | 29 + src/main/cpp/base/include/HttpClient.h | 29 + src/main/cpp/base/include/InvocationContext.h | 90 + src/main/cpp/base/include/LoggerImpl.h | 3 + src/main/cpp/base/include/MessageAccessor.h | 44 + src/main/cpp/base/include/MessageSystemFlag.h | 31 + src/main/cpp/base/include/MixAll.h | 72 + src/main/cpp/base/include/PermissionHelper.h | 18 + src/main/cpp/base/include/Protocol.h | 78 + src/main/cpp/base/include/RateLimiter.h | 164 + .../cpp/base/include/RequestIdGenerator.h | 20 + src/main/cpp/base/include/TopAddressing.h | 39 + src/main/cpp/base/include/UtilAll.h | 53 + src/main/cpp/client/BUILD.bazel | 42 + src/main/cpp/client/CMakeLists.txt | 66 + src/main/cpp/client/ClientApi.cpp | 15 + src/main/cpp/client/ClientConfig.cpp | 57 + src/main/cpp/client/ClientInstance.cpp | 1275 +++ src/main/cpp/client/ClientManager.cpp | 40 + src/main/cpp/client/LogInterceptor.cpp | 52 + src/main/cpp/client/LogInterceptorFactory.cpp | 11 + src/main/cpp/client/Metadata.cpp | 19 + src/main/cpp/client/RpcClientImpl.cpp | 143 + src/main/cpp/client/Signature.cpp | 66 + src/main/cpp/client/TlsHelper.cpp | 233 + src/main/cpp/client/TopicAssignmentInfo.cpp | 78 + src/main/cpp/client/TopicPublishInfo.cpp | 138 + src/main/cpp/client/TracingUtility.cpp | 138 + src/main/cpp/client/include/Assignment.h | 48 + src/main/cpp/client/include/Broker.h | 32 + src/main/cpp/client/include/BrokerData.h | 52 + src/main/cpp/client/include/ClientApi.h | 21 + src/main/cpp/client/include/ClientCallback.h | 15 + src/main/cpp/client/include/ClientConfig.h | 92 + src/main/cpp/client/include/ClientInstance.h | 291 + src/main/cpp/client/include/ClientManager.h | 40 + .../client/include/HeartbeatDataCallback.h | 14 + src/main/cpp/client/include/Identifiable.h | 20 + .../client/include/InterceptorContinuation.h | 25 + src/main/cpp/client/include/LogInterceptor.h | 16 + .../client/include/LogInterceptorFactory.h | 13 + src/main/cpp/client/include/Metadata.h | 23 + .../cpp/client/include/OpenSSLCompatible.h | 366 + .../include/OrphanTransactionCallback.h | 15 + src/main/cpp/client/include/Partition.h | 57 + .../client/include/ReceiveMessageCallback.h | 13 + .../cpp/client/include/ReceiveMessageResult.h | 156 + src/main/cpp/client/include/RpcClient.h | 114 + src/main/cpp/client/include/RpcClientImpl.h | 76 + .../cpp/client/include/SendMessageContext.h | 30 + src/main/cpp/client/include/ServiceAddress.h | 94 + src/main/cpp/client/include/Signature.h | 11 + src/main/cpp/client/include/TlsHelper.h | 43 + src/main/cpp/client/include/Topic.h | 39 + .../cpp/client/include/TopicAssignmentInfo.h | 43 + .../cpp/client/include/TopicPublishInfo.h | 51 + .../client/include/TopicRouteChangeCallback.h | 15 + src/main/cpp/client/include/TopicRouteData.h | 37 + src/main/cpp/client/include/TracingUtility.h | 59 + src/main/cpp/client/mocks/CMakeLists.txt | 17 + src/main/cpp/client/mocks/RpcClientMock.cpp | 14 + .../client/mocks/include/IdentifiableMock.h | 15 + .../cpp/client/mocks/include/RpcClientMock.h | 52 + src/main/cpp/concurrent/BUILD.bazel | 20 + src/main/cpp/concurrent/CMakeLists.txt | 13 + src/main/cpp/concurrent/CountdownLatch.cpp | 34 + .../cpp/concurrent/include/CountdownLatch.h | 28 + src/main/cpp/log/BUILD.bazel | 13 + src/main/cpp/log/CMakeLists.txt | 14 + src/main/cpp/log/Logger.cpp | 99 + src/main/cpp/rocketmq/AwaitPullCallback.cpp | 31 + src/main/cpp/rocketmq/BUILD.bazel | 13 + src/main/cpp/rocketmq/BaseImpl.cpp | 320 + src/main/cpp/rocketmq/CMakeLists.txt | 39 + .../ConsumeMessageConcurrentlyService.cpp | 286 + .../rocketmq/ConsumeMessageOrderlyService.cpp | 39 + .../cpp/rocketmq/ConsumeMessageService.cpp | 27 + src/main/cpp/rocketmq/CredentialsProvider.cpp | 97 + src/main/cpp/rocketmq/DefaultMQProducer.cpp | 102 + .../cpp/rocketmq/DefaultMQProducerImpl.cpp | 481 + .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 42 + .../rocketmq/DefaultMQPullConsumerImpl.cpp | 147 + .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 82 + .../rocketmq/DefaultMQPushConsumerImpl.cpp | 702 ++ .../cpp/rocketmq/MessageClientIDSetter.cpp | 59 + src/main/cpp/rocketmq/ProcessQueue.cpp | 236 + src/main/cpp/rocketmq/SendCallbacks.cpp | 78 + src/main/cpp/rocketmq/TransactionImpl.cpp | 23 + .../cpp/rocketmq/include/AwaitPullCallback.h | 25 + src/main/cpp/rocketmq/include/BaseImpl.h | 91 + .../rocketmq/include/ConsumeMessageService.h | 114 + .../rocketmq/include/DefaultMQProducerImpl.h | 114 + .../include/DefaultMQPullConsumerImpl.h | 31 + .../include/DefaultMQPushConsumerImpl.h | 213 + .../cpp/rocketmq/include/FilterExpression.h | 24 + .../rocketmq/include/MessageClientIDSetter.h | 81 + src/main/cpp/rocketmq/include/ProcessQueue.h | 160 + src/main/cpp/rocketmq/include/SendCallbacks.h | 68 + .../cpp/rocketmq/include/TransactionImpl.h | 32 + src/main/cpp/scheduler/BUILD.bazel | 21 + src/main/cpp/scheduler/CMakeLists.txt | 24 + src/main/cpp/scheduler/Scheduler.cpp | 263 + src/main/cpp/scheduler/include/Scheduler.h | 129 + src/test/cpp/benchmark/BUILD.bazel | 11 + src/test/cpp/benchmark/BenchmarkTest.cpp | 18 + src/test/cpp/it/BUILD.bazel | 40 + src/test/cpp/it/CMakeLists.txt | 42 + src/test/cpp/it/ClientInstanceTest.cpp | 12 + .../DefaultMQPushConsumerIntegrationTest.cpp | 45 + src/test/cpp/it/README.md | 4 + src/test/cpp/it/RpcClientIntegrationTest.cpp | 263 + src/test/cpp/it/RpcClientTest.cpp | 353 + src/test/cpp/it/TopAddressingTest.cpp | 62 + src/test/cpp/it/TopicPublishInfoTest.cpp | 157 + src/test/cpp/ut/AbseilTimeTest.cpp | 11 + src/test/cpp/ut/AsanDemo.cpp | 7 + src/test/cpp/ut/BUILD | 98 + src/test/cpp/ut/CMakeLists.txt | 41 + src/test/cpp/ut/CurlTest.cpp | 29 + src/test/cpp/ut/EventTest.cpp | 37 + src/test/cpp/ut/GTestExample.cpp | 4 + src/test/cpp/ut/HexTest.cpp | 17 + src/test/cpp/ut/LogTest.cpp | 29 + src/test/cpp/ut/README.md | 3 + src/test/cpp/ut/ThreadPoolTest.cpp | 47 + src/test/cpp/ut/ZlibTest.cpp | 81 + src/test/cpp/ut/admin/AdminServerTest.cpp | 43 + src/test/cpp/ut/admin/BUILD.bazel | 13 + src/test/cpp/ut/admin/CMakeLists.txt | 6 + src/test/cpp/ut/base/BUILD.bazel | 62 + src/test/cpp/ut/base/CMakeLists.txt | 31 + src/test/cpp/ut/base/HistogramTest.cpp | 41 + src/test/cpp/ut/base/MixAllTest.cpp | 56 + src/test/cpp/ut/base/RateLimiterTest.cpp | 49 + .../cpp/ut/base/RequestIdGeneratorTest.cpp | 16 + src/test/cpp/ut/base/UtilAllTest.cpp | 77 + src/test/cpp/ut/client/BUILD.bazel | 55 + src/test/cpp/ut/client/CMakeLists.txt | 40 + src/test/cpp/ut/client/CountdownLatchTest.cpp | 33 + src/test/cpp/ut/client/RpcClientTest.cpp | 66 + src/test/cpp/ut/client/SchedulerTest.cpp | 109 + src/test/cpp/ut/client/TlsHelperTest.cpp | 15 + src/test/cpp/ut/client/TracingUtilityTest.cpp | 33 + src/test/cpp/ut/logger/BUILD.bazel | 17 + src/test/cpp/ut/logger/CMakeLists.txt | 5 + src/test/cpp/ut/logger/LoggerTest.cpp | 25 + src/test/cpp/ut/rocketmq/BUILD.bazel | 140 + src/test/cpp/ut/rocketmq/CMakeLists.txt | 71 + .../ut/rocketmq/CredentialsProviderTest.cpp | 79 + .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 150 + .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 163 + src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 135 + .../ut/rocketmq/MessageClientIDSetterTest.cpp | 25 + src/test/cpp/ut/rocketmq/MessageTest.cpp | 10 + src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 17 + src/test/cpp/ut/rocketmq/Semaphore.cpp | 25 + src/test/cpp/ut/rocketmq/SemaphoreTest.cpp | 27 + .../cpp/ut/rocketmq/SendCallbacksTest.cpp | 28 + .../cpp/ut/rocketmq/include/MQClientTest.h | 76 + src/test/cpp/ut/rocketmq/include/Semaphore.h | 20 + third_party/BUILD | 0 third_party/CMakeLists.txt | 3 + third_party/ThreadPool.BUILD | 8 + third_party/cpp_httplib.BUILD | 6 + third_party/crc/BUILD.bazel | 9 + third_party/crc/CMakeLists.txt | 4 + third_party/crc/include/CRC.h | 2066 +++++ third_party/curl.BUILD | 37 + third_party/date/CMakeLists.txt | 4 + third_party/date/include/date/chrono_io.h | 34 + third_party/date/include/date/date.h | 8200 +++++++++++++++++ third_party/date/include/date/ios.h | 50 + third_party/date/include/date/islamic.h | 3031 ++++++ third_party/date/include/date/iso_week.h | 1751 ++++ third_party/date/include/date/julian.h | 3052 ++++++ third_party/date/include/date/ptz.h | 861 ++ third_party/date/include/date/solar_hijri.h | 3151 +++++++ third_party/date/include/date/tz.h | 2792 ++++++ third_party/date/include/date/tz_private.h | 316 + third_party/filesystem.BUILD | 9 + third_party/fmtlib.BUILD | 12 + third_party/libuv.BUILD | 92 + third_party/spdlog.BUILD | 21 + third_party/thread_pool/CMakeLists.txt | 2 + third_party/thread_pool/include/ThreadPool.h | 92 + third_party/zlib.BUILD | 36 + tools/env.sh | 6 + 284 files changed, 45141 insertions(+) create mode 100644 .bazelignore create mode 100644 .bazelrc create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .travis.yml create mode 100644 BUILD create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 WORKSPACE create mode 100644 api/BUILD.bazel create mode 100644 api/CMakeLists.txt create mode 100644 api/rocketmq/AdminServer.h create mode 100644 api/rocketmq/AsyncCallback.h create mode 100644 api/rocketmq/CommunicationMode.h create mode 100644 api/rocketmq/ConsumerType.h create mode 100644 api/rocketmq/Credentials.h create mode 100644 api/rocketmq/CredentialsProvider.h create mode 100644 api/rocketmq/DefaultMQProducer.h create mode 100644 api/rocketmq/DefaultMQPullConsumer.h create mode 100644 api/rocketmq/DefaultMQPushConsumer.h create mode 100644 api/rocketmq/ErrorCode.h create mode 100644 api/rocketmq/ExpressionType.h create mode 100644 api/rocketmq/Logger.h create mode 100644 api/rocketmq/MQClientException.h create mode 100644 api/rocketmq/MQMessage.h create mode 100644 api/rocketmq/MQMessageExt.h create mode 100644 api/rocketmq/MQMessageListener.h create mode 100644 api/rocketmq/MQMessageQueue.h create mode 100644 api/rocketmq/MQSelector.h create mode 100644 api/rocketmq/OffsetStore.h create mode 100644 api/rocketmq/PullResult.h create mode 100644 api/rocketmq/RocketMQ.h create mode 100644 api/rocketmq/SendResult.h create mode 100644 api/rocketmq/State.h create mode 100644 api/rocketmq/Transaction.h create mode 100644 bazel/BUILD create mode 100644 bazel/curl.bzl create mode 100644 bazel/rocketmq_deps.bzl create mode 100755 ci/build.sh create mode 100644 docs/DevelopmentGuide.md create mode 100644 docs/gtest.md create mode 100644 docs/test_coverage.md create mode 100644 example/CMakeLists.txt create mode 100644 example/rocketmq/BUILD.bazel create mode 100644 example/rocketmq/BenchmarkPushConsumer.cpp create mode 100644 example/rocketmq/CMakeLists.txt create mode 100644 example/rocketmq/ExampleAsyncProducer.cpp create mode 100644 example/rocketmq/ExampleBroadcastPushConsumer.cpp create mode 100644 example/rocketmq/ExampleProducer.cpp create mode 100644 example/rocketmq/ExamplePullConsumer.cpp create mode 100644 example/rocketmq/ExamplePushConsumer.cpp create mode 100644 example/rocketmq/PushConsumerWithCustomExecutor.cpp create mode 100644 example/rocketmq/PushConsumerWithThrottle.cpp create mode 100644 example/rocketmq/SqlConsumer.cpp create mode 100644 example/rocketmq/SqlProducer.cpp create mode 100644 proto/BUILD.bazel create mode 100644 proto/apache/rocketmq/v1/admin.proto create mode 100644 proto/apache/rocketmq/v1/definition.proto create mode 100644 proto/apache/rocketmq/v1/service.proto create mode 100644 proto/google/protobuf/any.proto create mode 100644 proto/google/protobuf/duration.proto create mode 100644 proto/google/protobuf/empty.proto create mode 100644 proto/google/protobuf/field_mask.proto create mode 100644 proto/google/protobuf/timestamp.proto create mode 100644 proto/google/rpc/code.proto create mode 100644 proto/google/rpc/error_details.proto create mode 100644 proto/google/rpc/status.proto create mode 100644 rpm/rocketmq-cpp.sh create mode 100644 rpm/rocketmq-cpp.spec create mode 100644 src/main/cpp/CMakeLists.txt create mode 100644 src/main/cpp/admin/AdminClient.cpp create mode 100644 src/main/cpp/admin/AdminFacade.cpp create mode 100644 src/main/cpp/admin/AdminServerImpl.cpp create mode 100644 src/main/cpp/admin/AdminServiceImpl.cpp create mode 100644 src/main/cpp/admin/BUILD.bazel create mode 100644 src/main/cpp/admin/CMakeLists.txt create mode 100644 src/main/cpp/admin/include/AdminClient.h create mode 100644 src/main/cpp/admin/include/AdminServerImpl.h create mode 100644 src/main/cpp/admin/include/AdminServiceImpl.h create mode 100644 src/main/cpp/admin/include/ServerCall.h create mode 100644 src/main/cpp/base/BUILD.bazel create mode 100644 src/main/cpp/base/CMakeLists.txt create mode 100644 src/main/cpp/base/HostInfo.cpp create mode 100644 src/main/cpp/base/HttpClient.cpp create mode 100644 src/main/cpp/base/MQMessage.cpp create mode 100644 src/main/cpp/base/MessageSystemFlag.cpp create mode 100644 src/main/cpp/base/MixAll.cpp create mode 100644 src/main/cpp/base/PermissionHelper.cpp create mode 100644 src/main/cpp/base/Protocol.cpp create mode 100644 src/main/cpp/base/RateLimiter.cpp create mode 100644 src/main/cpp/base/RequestIdGenerator.cpp create mode 100644 src/main/cpp/base/TopAddressing.cpp create mode 100644 src/main/cpp/base/UtilAll.cpp create mode 100644 src/main/cpp/base/include/Functional.h create mode 100644 src/main/cpp/base/include/Histogram.h create mode 100644 src/main/cpp/base/include/HostInfo.h create mode 100644 src/main/cpp/base/include/HttpClient.h create mode 100644 src/main/cpp/base/include/InvocationContext.h create mode 100644 src/main/cpp/base/include/LoggerImpl.h create mode 100644 src/main/cpp/base/include/MessageAccessor.h create mode 100644 src/main/cpp/base/include/MessageSystemFlag.h create mode 100644 src/main/cpp/base/include/MixAll.h create mode 100644 src/main/cpp/base/include/PermissionHelper.h create mode 100644 src/main/cpp/base/include/Protocol.h create mode 100644 src/main/cpp/base/include/RateLimiter.h create mode 100644 src/main/cpp/base/include/RequestIdGenerator.h create mode 100644 src/main/cpp/base/include/TopAddressing.h create mode 100644 src/main/cpp/base/include/UtilAll.h create mode 100644 src/main/cpp/client/BUILD.bazel create mode 100644 src/main/cpp/client/CMakeLists.txt create mode 100644 src/main/cpp/client/ClientApi.cpp create mode 100644 src/main/cpp/client/ClientConfig.cpp create mode 100644 src/main/cpp/client/ClientInstance.cpp create mode 100644 src/main/cpp/client/ClientManager.cpp create mode 100644 src/main/cpp/client/LogInterceptor.cpp create mode 100644 src/main/cpp/client/LogInterceptorFactory.cpp create mode 100644 src/main/cpp/client/Metadata.cpp create mode 100644 src/main/cpp/client/RpcClientImpl.cpp create mode 100644 src/main/cpp/client/Signature.cpp create mode 100644 src/main/cpp/client/TlsHelper.cpp create mode 100644 src/main/cpp/client/TopicAssignmentInfo.cpp create mode 100644 src/main/cpp/client/TopicPublishInfo.cpp create mode 100644 src/main/cpp/client/TracingUtility.cpp create mode 100644 src/main/cpp/client/include/Assignment.h create mode 100644 src/main/cpp/client/include/Broker.h create mode 100644 src/main/cpp/client/include/BrokerData.h create mode 100644 src/main/cpp/client/include/ClientApi.h create mode 100644 src/main/cpp/client/include/ClientCallback.h create mode 100644 src/main/cpp/client/include/ClientConfig.h create mode 100644 src/main/cpp/client/include/ClientInstance.h create mode 100644 src/main/cpp/client/include/ClientManager.h create mode 100644 src/main/cpp/client/include/HeartbeatDataCallback.h create mode 100644 src/main/cpp/client/include/Identifiable.h create mode 100644 src/main/cpp/client/include/InterceptorContinuation.h create mode 100644 src/main/cpp/client/include/LogInterceptor.h create mode 100644 src/main/cpp/client/include/LogInterceptorFactory.h create mode 100644 src/main/cpp/client/include/Metadata.h create mode 100644 src/main/cpp/client/include/OpenSSLCompatible.h create mode 100644 src/main/cpp/client/include/OrphanTransactionCallback.h create mode 100644 src/main/cpp/client/include/Partition.h create mode 100644 src/main/cpp/client/include/ReceiveMessageCallback.h create mode 100644 src/main/cpp/client/include/ReceiveMessageResult.h create mode 100644 src/main/cpp/client/include/RpcClient.h create mode 100644 src/main/cpp/client/include/RpcClientImpl.h create mode 100644 src/main/cpp/client/include/SendMessageContext.h create mode 100644 src/main/cpp/client/include/ServiceAddress.h create mode 100644 src/main/cpp/client/include/Signature.h create mode 100644 src/main/cpp/client/include/TlsHelper.h create mode 100644 src/main/cpp/client/include/Topic.h create mode 100644 src/main/cpp/client/include/TopicAssignmentInfo.h create mode 100644 src/main/cpp/client/include/TopicPublishInfo.h create mode 100644 src/main/cpp/client/include/TopicRouteChangeCallback.h create mode 100644 src/main/cpp/client/include/TopicRouteData.h create mode 100644 src/main/cpp/client/include/TracingUtility.h create mode 100644 src/main/cpp/client/mocks/CMakeLists.txt create mode 100644 src/main/cpp/client/mocks/RpcClientMock.cpp create mode 100644 src/main/cpp/client/mocks/include/IdentifiableMock.h create mode 100644 src/main/cpp/client/mocks/include/RpcClientMock.h create mode 100644 src/main/cpp/concurrent/BUILD.bazel create mode 100644 src/main/cpp/concurrent/CMakeLists.txt create mode 100644 src/main/cpp/concurrent/CountdownLatch.cpp create mode 100644 src/main/cpp/concurrent/include/CountdownLatch.h create mode 100644 src/main/cpp/log/BUILD.bazel create mode 100644 src/main/cpp/log/CMakeLists.txt create mode 100644 src/main/cpp/log/Logger.cpp create mode 100644 src/main/cpp/rocketmq/AwaitPullCallback.cpp create mode 100644 src/main/cpp/rocketmq/BUILD.bazel create mode 100644 src/main/cpp/rocketmq/BaseImpl.cpp create mode 100644 src/main/cpp/rocketmq/CMakeLists.txt create mode 100644 src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp create mode 100644 src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp create mode 100644 src/main/cpp/rocketmq/ConsumeMessageService.cpp create mode 100644 src/main/cpp/rocketmq/CredentialsProvider.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQProducer.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp create mode 100644 src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp create mode 100644 src/main/cpp/rocketmq/MessageClientIDSetter.cpp create mode 100644 src/main/cpp/rocketmq/ProcessQueue.cpp create mode 100644 src/main/cpp/rocketmq/SendCallbacks.cpp create mode 100644 src/main/cpp/rocketmq/TransactionImpl.cpp create mode 100644 src/main/cpp/rocketmq/include/AwaitPullCallback.h create mode 100644 src/main/cpp/rocketmq/include/BaseImpl.h create mode 100644 src/main/cpp/rocketmq/include/ConsumeMessageService.h create mode 100644 src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h create mode 100644 src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h create mode 100644 src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h create mode 100644 src/main/cpp/rocketmq/include/FilterExpression.h create mode 100644 src/main/cpp/rocketmq/include/MessageClientIDSetter.h create mode 100644 src/main/cpp/rocketmq/include/ProcessQueue.h create mode 100644 src/main/cpp/rocketmq/include/SendCallbacks.h create mode 100644 src/main/cpp/rocketmq/include/TransactionImpl.h create mode 100644 src/main/cpp/scheduler/BUILD.bazel create mode 100644 src/main/cpp/scheduler/CMakeLists.txt create mode 100644 src/main/cpp/scheduler/Scheduler.cpp create mode 100644 src/main/cpp/scheduler/include/Scheduler.h create mode 100644 src/test/cpp/benchmark/BUILD.bazel create mode 100644 src/test/cpp/benchmark/BenchmarkTest.cpp create mode 100644 src/test/cpp/it/BUILD.bazel create mode 100644 src/test/cpp/it/CMakeLists.txt create mode 100644 src/test/cpp/it/ClientInstanceTest.cpp create mode 100644 src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp create mode 100644 src/test/cpp/it/README.md create mode 100644 src/test/cpp/it/RpcClientIntegrationTest.cpp create mode 100644 src/test/cpp/it/RpcClientTest.cpp create mode 100644 src/test/cpp/it/TopAddressingTest.cpp create mode 100644 src/test/cpp/it/TopicPublishInfoTest.cpp create mode 100644 src/test/cpp/ut/AbseilTimeTest.cpp create mode 100644 src/test/cpp/ut/AsanDemo.cpp create mode 100644 src/test/cpp/ut/BUILD create mode 100644 src/test/cpp/ut/CMakeLists.txt create mode 100644 src/test/cpp/ut/CurlTest.cpp create mode 100644 src/test/cpp/ut/EventTest.cpp create mode 100644 src/test/cpp/ut/GTestExample.cpp create mode 100644 src/test/cpp/ut/HexTest.cpp create mode 100644 src/test/cpp/ut/LogTest.cpp create mode 100644 src/test/cpp/ut/README.md create mode 100644 src/test/cpp/ut/ThreadPoolTest.cpp create mode 100644 src/test/cpp/ut/ZlibTest.cpp create mode 100644 src/test/cpp/ut/admin/AdminServerTest.cpp create mode 100644 src/test/cpp/ut/admin/BUILD.bazel create mode 100644 src/test/cpp/ut/admin/CMakeLists.txt create mode 100644 src/test/cpp/ut/base/BUILD.bazel create mode 100644 src/test/cpp/ut/base/CMakeLists.txt create mode 100644 src/test/cpp/ut/base/HistogramTest.cpp create mode 100644 src/test/cpp/ut/base/MixAllTest.cpp create mode 100644 src/test/cpp/ut/base/RateLimiterTest.cpp create mode 100644 src/test/cpp/ut/base/RequestIdGeneratorTest.cpp create mode 100644 src/test/cpp/ut/base/UtilAllTest.cpp create mode 100644 src/test/cpp/ut/client/BUILD.bazel create mode 100644 src/test/cpp/ut/client/CMakeLists.txt create mode 100644 src/test/cpp/ut/client/CountdownLatchTest.cpp create mode 100644 src/test/cpp/ut/client/RpcClientTest.cpp create mode 100644 src/test/cpp/ut/client/SchedulerTest.cpp create mode 100644 src/test/cpp/ut/client/TlsHelperTest.cpp create mode 100644 src/test/cpp/ut/client/TracingUtilityTest.cpp create mode 100644 src/test/cpp/ut/logger/BUILD.bazel create mode 100644 src/test/cpp/ut/logger/CMakeLists.txt create mode 100644 src/test/cpp/ut/logger/LoggerTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/BUILD.bazel create mode 100644 src/test/cpp/ut/rocketmq/CMakeLists.txt create mode 100644 src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/ExecutorTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/MessageTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/Semaphore.cpp create mode 100644 src/test/cpp/ut/rocketmq/SemaphoreTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/include/MQClientTest.h create mode 100644 src/test/cpp/ut/rocketmq/include/Semaphore.h create mode 100644 third_party/BUILD create mode 100644 third_party/CMakeLists.txt create mode 100644 third_party/ThreadPool.BUILD create mode 100644 third_party/cpp_httplib.BUILD create mode 100644 third_party/crc/BUILD.bazel create mode 100644 third_party/crc/CMakeLists.txt create mode 100644 third_party/crc/include/CRC.h create mode 100644 third_party/curl.BUILD create mode 100644 third_party/date/CMakeLists.txt create mode 100644 third_party/date/include/date/chrono_io.h create mode 100644 third_party/date/include/date/date.h create mode 100644 third_party/date/include/date/ios.h create mode 100644 third_party/date/include/date/islamic.h create mode 100644 third_party/date/include/date/iso_week.h create mode 100644 third_party/date/include/date/julian.h create mode 100644 third_party/date/include/date/ptz.h create mode 100644 third_party/date/include/date/solar_hijri.h create mode 100644 third_party/date/include/date/tz.h create mode 100644 third_party/date/include/date/tz_private.h create mode 100644 third_party/filesystem.BUILD create mode 100644 third_party/fmtlib.BUILD create mode 100644 third_party/libuv.BUILD create mode 100644 third_party/spdlog.BUILD create mode 100644 third_party/thread_pool/CMakeLists.txt create mode 100644 third_party/thread_pool/include/ThreadPool.h create mode 100644 third_party/zlib.BUILD create mode 100755 tools/env.sh diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 000000000..8c28fe2ba --- /dev/null +++ b/.bazelignore @@ -0,0 +1,2 @@ +_build +cmake-build-debug \ No newline at end of file diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 000000000..cfea7c592 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,55 @@ +build:linux --copt=-fPIC +build:linux --cxxopt=-std=c++11 + +# We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace. +build --define absl=1 + +# Pass PATH, CC, CXX and LLVM_CONFIG variables from the environment. +build --action_env=CC +build --action_env=CXX +build --action_env=LD_LIBRARY_PATH +build --action_env=LLVM_CONFIG +build --action_env=PATH + +build --copt=-maes + +# Address sanitizer +# To use it: bazel build --config asan +build:asan --compiler clang +build:asan --strip=never +build:asan --copt -fsanitize=address +build:asan --copt -DADDRESS_SANITIZER +build:asan --copt -O1 +build:asan --copt -g +build:asan --copt -fno-omit-frame-pointer +build:asan --linkopt -fsanitize=address + +# Thread sanitizer +# bazel build --config tsan +build:tsan --compiler clang +build:tsan --strip=never +build:tsan --copt -fsanitize=thread +build:tsan --copt -DTHREAD_SANITIZER +build:tsan --copt -DDYNAMIC_ANNOTATIONS_ENABLED=1 +build:tsan --copt -DDYNAMIC_ANNOTATIONS_EXTERNAL_IMPL=1 +build:tsan --copt -O1 +build:tsan --copt -fno-omit-frame-pointer +build:tsan --linkopt -fsanitize=thread + +# --config msan: Memory sanitizer +build:msan --compiler clang +build:msan --strip=never +build:msan --copt -fsanitize=memory +build:msan --copt -DADDRESS_SANITIZER +build:msan --copt -O1 +build:msan --copt -fno-omit-frame-pointer +build:msan --linkopt -fsanitize=memory + +# --config ubsan: Undefined Behavior Sanitizer +build:ubsan --compiler clang +build:ubsan --strip=never +build:ubsan --copt -fsanitize=undefined +build:ubsan --copt -O1 +build:ubsan --copt -fno-omit-frame-pointer +build:ubsan --linkopt -fsanitize=undefined +build:ubsan --linkopt -lubsan \ No newline at end of file diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..bb3e5d7da --- /dev/null +++ b/.clang-format @@ -0,0 +1,18 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +ColumnLimit: 120 +DerivePointerAlignment: false +PointerAlignment: Left +SortIncludes: true +IndentWidth: 2 +NamespaceIndentation: Inner +... + +--- +Language: Proto +ColumnLimit: 120 +SpacesInContainerLiterals: false +AllowShortFunctionsOnASingleLine: None +ReflowComments: false +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..693858657 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,81 @@ +Checks: '-clang-analyzer-core.NonNullParamChecker, + -clang-analyzer-optin.cplusplus.UninitializedObject, + abseil-duration-*, + abseil-faster-strsplit-delimiter, + abseil-no-namespace, + abseil-redundant-strcat-calls, + abseil-str-cat-append, + abseil-string-find-startswith, + abseil-upgrade-duration-conversions, + bugprone-assert-side-effect, + bugprone-unused-raii, + bugprone-use-after-move, + clang-analyzer-core.DivideZero, + misc-unused-using-decls, + modernize-deprecated-headers, + modernize-loop-convert, + modernize-make-shared, + modernize-make-unique, + modernize-return-braced-init-list, + modernize-use-default-member-init, + modernize-use-equals-default, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-faster-string-find, + performance-for-range-copy, + performance-inefficient-algorithm, + performance-inefficient-vector-operation, + performance-noexcept-move-constructor, + performance-move-constructor-init, + performance-type-promotion-in-math-fn, + performance-unnecessary-copy-initialization, + readability-braces-around-statements, + readability-container-size-empty, + readability-identifier-naming, + readability-redundant-control-flow, + readability-redundant-member-init, + readability-redundant-smartptr-get, + readability-redundant-string-cstr' + +WarningsAsErrors: '*' + +CheckOptions: + - key: bugprone-assert-side-effect.AssertMacros + value: 'ASSERT' + + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view;absl::string_view' + + - key: modernize-use-auto.MinTypeNameLength + value: '10' + + - key: readability-identifier-naming.ClassCase + value: 'CamelCase' + + - key: readability-identifier-naming.EnumCase + value: 'CamelCase' + + - key: readability-identifier-naming.EnumConstantCase + value: 'CamelCase' + + - key: readability-identifier-naming.ParameterCase + value: 'lower_case' + + - key: readability-identifier-naming.PrivateMemberCase + value: 'lower_case' + + - key: readability-identifier-naming.PrivateMemberSuffix + value: '_' + + - key: readability-identifier-naming.StructCase + value: 'CamelCase' + + - key: readability-identifier-naming.TypeAliasCase + value: 'CamelCase' + + - key: readability-identifier-naming.UnionCase + value: 'CamelCase' + + - key: readability-identifier-naming.FunctionCase + value: 'camelBack' diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..2d9a80cb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +bazel-bin +bazel-out +bazel-rocketmq-cpp +bazel-testlogs +cmake-build-debug +.clwb +compile_commands.json +.vscode +logs +_build +cmake-build-debug-coverage +.idea +.cache +.DS_Store +external +bazel-apache_rocketmq_client_cpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..006e24622 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,16 @@ +stages: + - cmake_build + - bazel_build + +before_script: + - export http_proxy=http://30.57.177.111:3128; export https_proxy=http://30.57.177.111:3128; export ALL_PROXY=30.57.177.111:3128; export PATH=/usr/local/gcc/bin:$PATH + +job1: + stage: cmake_build + script: + - mkdir _build; cd _build; cmake -DBUILD_TESTS=1 -DBUILD_INTEGRATION_TESTS=1 -DBUILD_EXAMPLES=1 ..; make -j $(nproc) + +job2: + stage: bazel_build + script: + - bazel build //... \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..d2440c443 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,173 @@ +language: cpp +dist: focal +jobs: + include: + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-4.9 + - cmake + env: + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-5 + - cmake + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-6 + - cmake + env: + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - g++-7 + - cmake + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + # Clang on Linux + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.6 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.7 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" + + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.8 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" + + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-3.9 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-3.9 + - cmake + env: + - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" + + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-4.0 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-4.0 + - cmake + env: + - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" + + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main' + key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc' + packages: + - clang-5.0 + - cmake + env: + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" + # GCC on Mac +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" +# +# - os: osx +# osx_image: xcode8 +# env: +# - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" +before_install: + - eval "${MATRIX_EVAL}" + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update ; + brew install cmake ; + fi + +script: + - ci/build.sh diff --git a/BUILD b/BUILD new file mode 100644 index 000000000..e69de29bb diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..3eacb2760 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,172 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) +project(rocketmq VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(VERSION "1.0.0") + +include(FetchContent) + +find_package(Threads) + +FetchContent_Declare( + gRPC + GIT_REPOSITORY https://gitee.com/lizhanhui/grpc.git + GIT_TAG v1.38.0-p2 +) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.10.x +) +FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.8.1 +) + +FetchContent_Declare( + uv + GIT_REPOSITORY https://github.com/libuv/libuv.git + GIT_TAG v1.34.0 +) + +FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 7.0.2 +) + +FetchContent_Declare( + ghc + GIT_REPOSITORY https://github.com/gulrak/filesystem.git + GIT_TAG v1.3.2 +) + +FetchContent_Declare( + opentelemetry + GIT_REPOSITORY https://github.com/aaron-ai/opentelemetry-cpp.git + GIT_TAG v0.3.0-lingchu-fix-01 +) + +set(FETCHCONTENT_QUIET OFF) +option(INSTALL_GTEST OFF) +set(BUILD_TESTING OFF) +set(ABSL_RUN_TESTS OFF) +set(SPDLOG_FMT_EXTERNAL ON) +if (BUILD_TESTS) + FetchContent_MakeAvailable(googletest) +endif () + +FetchContent_MakeAvailable(fmt gRPC spdlog uv ghc) + +option(ENABLE_TRACING OFF) +if (ENABLE_TRACING) + FetchContent_MakeAvailable(opentelemetry) +endif () + +find_package(CURL REQUIRED) +message("CURL ${CURL_LIBRARIES}") + +# Since FetchContent uses add_subdirectory under the hood, we can use +# the grpc targets directly from this build. +set(_PROTOBUF_LIBPROTOBUF libprotobuf) +set(_REFLECTION grpc++_reflection) +set(_PROTOBUF_PROTOC $) +set(_GRPC_GRPCPP grpc++) +if(CMAKE_CROSSCOMPILING) + message("CMake cross compiling") + find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) +else() + set(_GRPC_CPP_PLUGIN_EXECUTABLE $) + message("CMake non-cross compiling: ${_GRPC_CPP_PLUGIN_EXECUTABLE}") +endif() + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/proto) +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/proto/dummy.cc) +file(GLOB PROTO_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/proto/apache/rocketmq/v1/*.proto" + "${CMAKE_CURRENT_SOURCE_DIR}/proto/google/rpc/*.proto") +foreach(proto_file ${PROTO_FILES}) + get_filename_component(proto_file_base_name ${proto_file} NAME_WE) + message("Proto file base file name: ${proto_file_base_name}, abs: ${proto_file}") + set(proto_src_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.pb.cc") + set(proto_hdr_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.pb.h") + set(grpc_src_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.grpc.pb.cc") + set(grpc_hdr_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.grpc.pb.h") + + add_custom_command( + OUTPUT "${proto_src_file}" "${proto_hdr_file}" "${grpc_src_file}" "${grpc_hdr_file}" + COMMAND ${_PROTOBUF_PROTOC} + ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}/proto" + --cpp_out "${CMAKE_BINARY_DIR}/proto" + -I "${CMAKE_SOURCE_DIR}/proto" + --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" + "${proto_file}" + DEPENDS "${proto_file}" + COMMENT "Protoc to generate gRPC stub files" + ) + + add_custom_target(${proto_file_base_name} + ALL + DEPENDS ${proto_src_file} ${proto_hdr_file} ${grpc_src_file} ${grpc_hdr_file} + COMMENT "Custom target" + VERBATIM) +endforeach() + +file(GLOB_RECURSE STUB_FILES ${CMAKE_BINARY_DIR}/proto/*.cc) +add_library(mq_proto_library STATIC ${STUB_FILES}) +target_include_directories(mq_proto_library PUBLIC ${CMAKE_BINARY_DIR}/proto) +target_link_libraries(mq_proto_library + PUBLIC + ${_REFLECTION} + ${_GRPC_GRPCPP} + ${_PROTOBUF_LIBPROTOBUF}) + +enable_testing() + +add_subdirectory(third_party) + +option(ENABLE_TLS ON) +if (ENABLE_TLS) + message("TLS enabled") + find_package(OpenSSL REQUIRED) + add_compile_definitions(TLS_ENABLED=ON) +endif () + +add_subdirectory(api) +add_subdirectory(src/main/cpp) + +if (BUILD_TESTS) + add_subdirectory(src/test/cpp/ut) +endif() + +if (BUILD_INTEGRATION_TESTS) + add_subdirectory(src/test/cpp/it) +endif () + +if (BUILD_EXAMPLES) + add_subdirectory(example) +endif () + +include(GNUInstallDirs) +install(DIRECTORY api/rocketmq + DESTINATION include) +install(TARGETS rocketmq_static rocketmq_shared + LIBRARY DESTINATION lib) +install(FILES + example/rocketmq/ExampleProducer.cpp + example/rocketmq/ExamplePushConsumer.cpp + DESTINATION example) + +set(CPACK_PACKAGE_VERSION ${VERSION}) +set(CPACK_GENERATOR "RPM") +set(CPACK_PACKAGE_NAME "rocketmq-cpp") +set(CPACK_PACKAGE_RELEASE 1) +set(CPACK_PACKAGE_CONTACT "Zhanhui Li") +set(CPACK_PACKAGE_VENDOR "Alibaba") +set(CPACK_PACKAGING_INSTALL_PREFIX "/home/admin") +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}") +include(CPack) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..be6132828 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +### Introduction +MQ products in the market supports two styles of API to acknowledge success of message consumption. + +1. Rich client SDK manages individually and syncs to broker in batch manner; +2. On consumption of each message, thin client SDK acknowledge it to broker instantly. + +Both of which have famous adopters, Kafka, for example prefers 1 over 2 to achieve best performance and accept SDK +complexities. SQS/IBM MQ, etc choose choice 2. RocketMQ support both of them. + +### Pop SDK +This SDK implements choice of message consumption and acknowledgement. Aka, SDK leaves most of the complexities to +brokers to enjoy client simplicity and robustness. + +### API +To make the transition as smooth as possible, API compatibility is maintained. Namespace for now is changed to rocketmq +from metaq during proof of concept phase. + +### How to build +We use [Modern CMake](https://cliutils.gitlab.io/modern-cmake/) to manage and compile this project, therefore, fairly +easy to compile. +1. Clone this repo `git clone git@gitlab.alibaba-inc.com:shutian.lzh/rocketmq-cpp.git` +2. Build `cd rocketmq-cpp; mkdir _build; cd _build && cmake ..; make` +3. Enjoy + +[Google Bazel](https://bazel.build/) is another build tool we supported, you can use `bazel build //...` to build the whole project. + +### IDE +[Clangd](https://clangd.llvm.org/) is a really cool code completion tool. Use the following command to generate compile_command.json if you prefer to use it. +``` +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. +``` + +### Sanitizer +Clang/GCC has developed cool collection of sanitizer tools: Address, Undefined, Thread... + +To run with sanitizer on, +``` +bazel run -c dbg --config=asan //src/test:asan_demo +``` + +### Example +Talk is cheap, show me the code!!! + +Here you go: + +1. [ExampleProducer.cpp](example/rocketmq/ExampleProducer.cpp) + +1. [ExamplePushConsumer.cpp](example/rocketmq/ExamplePushConsumer.cpp) + +### FAQ +1. Q: Can this pop SDK deploy along with existing Pull SDK? + + A: We are currently working on this issue and this feature will be supported soon. Once the development is completed + and verified in production, we will get this item updated. + +1. Q: Is this SDK production ready? + + A: To be honest, this SDK is just built and many features are still under active development. However, we take quality + seriously and various measures are employed to achieve this goal, code review, unit test coverage, compiler + sanitizers, thread-annotations-to-avoid-concurrent-issues, ... + \ No newline at end of file diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000..86484441d --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,17 @@ +workspace(name = "org_apache_rocketmq") +load("//bazel:rocketmq_deps.bzl", "rocketmq_deps") +rocketmq_deps() + +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps", "grpc_test_only_deps") +grpc_deps() +grpc_test_only_deps() +load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") +grpc_extra_deps() + +load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains", "rules_proto_grpc_repos") +rules_proto_grpc_toolchains() +rules_proto_grpc_repos() + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") +rules_proto_dependencies() +rules_proto_toolchains() \ No newline at end of file diff --git a/api/BUILD.bazel b/api/BUILD.bazel new file mode 100644 index 000000000..d971e6d5e --- /dev/null +++ b/api/BUILD.bazel @@ -0,0 +1,8 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "rocketmq_interface", + hdrs = glob(["rocketmq/*.h"]), + strip_include_prefix = "//api", + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt new file mode 100644 index 000000000..b41166c59 --- /dev/null +++ b/api/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(rocketmq_interface INTERFACE) +target_include_directories(rocketmq_interface + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/api/rocketmq/AdminServer.h b/api/rocketmq/AdminServer.h new file mode 100644 index 000000000..70d97c4f2 --- /dev/null +++ b/api/rocketmq/AdminServer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "State.h" + +namespace rocketmq { +namespace admin { + +class AdminServer { +public: + virtual ~AdminServer() = default; + + virtual bool start() = 0; + + virtual bool stop() = 0; + + virtual int port() const = 0; +}; + +class AdminFacade { +public: + static AdminServer& getServer(); +}; + +} // namespace admin +} // namespace rocketmq \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h new file mode 100644 index 000000000..c0a30f474 --- /dev/null +++ b/api/rocketmq/AsyncCallback.h @@ -0,0 +1,32 @@ +#pragma once + +#include "MQClientException.h" +#include "PullResult.h" +#include "SendResult.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct AsyncCallback {}; + +enum SendCallbackType { noAutoDeleteSendCallback = 0, autoDeleteSendCallback = 1 }; + +using sendCallbackType = SendCallbackType; + +class SendCallback : public AsyncCallback { +public: + virtual ~SendCallback() = default; + virtual void onSuccess(const SendResult& send_result) = 0; + virtual void onException(const MQException& e) = 0; + virtual SendCallbackType getSendCallbackType() { return noAutoDeleteSendCallback; } +}; + +class PullCallback : public AsyncCallback { +public: + virtual ~PullCallback() = default; + + virtual void onSuccess(const PullResult& pull_result) = 0; + + virtual void onException(const MQException& e) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/CommunicationMode.h b/api/rocketmq/CommunicationMode.h new file mode 100644 index 000000000..ad29a4906 --- /dev/null +++ b/api/rocketmq/CommunicationMode.h @@ -0,0 +1,14 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum CommunicationMode +{ + ComMode_SYNC, + ComMode_ASYNC, + ComMode_ONEWAY +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ConsumerType.h b/api/rocketmq/ConsumerType.h new file mode 100644 index 000000000..65b2f4870 --- /dev/null +++ b/api/rocketmq/ConsumerType.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum ConsumeType { + CONSUME_ACTIVELY, + CONSUME_PASSIVELY, +}; + +enum ConsumeFromWhere { + CONSUME_FROM_LAST_OFFSET, + CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST, + CONSUME_FROM_MIN_OFFSET, + CONSUME_FROM_MAX_OFFSET, + CONSUME_FROM_FIRST_OFFSET, + CONSUME_FROM_TIMESTAMP, +}; + +enum ConsumeInitialMode { + MIN, + MAX, +}; + +enum MessageModel { + BROADCASTING, + CLUSTERING, +}; + +enum QueryOffsetPolicy : uint8_t { + BEGINNING = 0, + END = 1, + TIME_POINT = 2, +}; + +struct OffsetQuery { + MQMessageQueue message_queue; + QueryOffsetPolicy policy; + std::chrono::system_clock::time_point time_point; +}; + +struct PullMessageQuery { + MQMessageQueue message_queue; + int64_t offset; + int32_t batch_size; + std::chrono::system_clock::duration await_time; + std::chrono::system_clock::duration timeout; +}; + +using Executor = std::function&)>; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h new file mode 100644 index 000000000..35c9229a0 --- /dev/null +++ b/api/rocketmq/Credentials.h @@ -0,0 +1,42 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Credentials { +public: + Credentials() : expiration_instant_(std::chrono::system_clock::time_point::max()) {} + + Credentials(std::string access_key, std::string access_secret) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), + expiration_instant_(std::chrono::system_clock::time_point::max()) {} + + bool operator==(const Credentials& rhs) const { + return access_key_ == rhs.access_key_ && access_secret_ == rhs.access_secret_ && + session_token_ == rhs.session_token_ && expiration_instant_ == rhs.expiration_instant_; + } + + bool operator!=(const Credentials& rhs) const { return !(*this == rhs); } + + bool empty() const { return access_key_.empty() || access_secret_.empty(); } + + bool expired() const { return std::chrono::system_clock::now() > expiration_instant_; } + + const std::string& accessKey() const { return access_key_; } + + const std::string& accessSecret() const { return access_secret_; } + + std::chrono::system_clock::time_point expirationInstant() const { return expiration_instant_; } + +private: + std::string access_key_; + std::string access_secret_; + std::string session_token_; + std::chrono::system_clock::time_point expiration_instant_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h new file mode 100644 index 000000000..ddce476f1 --- /dev/null +++ b/api/rocketmq/CredentialsProvider.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include "Credentials.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class CredentialsProvider { +public: + virtual ~CredentialsProvider() = default; + + virtual Credentials getCredentials() = 0; + + virtual std::chrono::system_clock::duration refreshInterval() = 0; +}; + +using CredentialsProviderPtr = std::shared_ptr; + +class StaticCredentialsProvider : public CredentialsProvider { +public: + StaticCredentialsProvider(std::string access_key, std::string access_secret); + + ~StaticCredentialsProvider() override = default; + + Credentials getCredentials() override; + + std::chrono::system_clock::duration refreshInterval() override; + +private: + std::string access_key_; + std::string access_secret_; +}; + +/** + * Read credentials from environment variables ROCKETMQ_ACCESS_KEY, ROCKETMQ_ACCESS_SECRET + */ +class EnvironmentVariablesCredentialsProvider : public CredentialsProvider { +public: + EnvironmentVariablesCredentialsProvider(); + + ~EnvironmentVariablesCredentialsProvider() override = default; + + Credentials getCredentials() override; + + std::chrono::system_clock::duration refreshInterval() override { return std::chrono::system_clock::duration::zero(); } + +private: + std::string access_key_; + std::string access_secret_; + + static const char* ENVIRONMENT_ACCESS_KEY; + static const char* ENVIRONMENT_ACCESS_SECRET; +}; + +/** + * Read credentials from configuration file: ~/rocketmq/credentials. By default, the client library would refresh every + * 10 seconds. + */ +class ConfigFileCredentialsProvider : public CredentialsProvider { +public: + ConfigFileCredentialsProvider(); + + Credentials getCredentials() override; + + std::chrono::system_clock::duration refreshInterval() override; + + /** + * For test purpose only. + * @return + */ + static const char* credentialFile() { + return CREDENTIAL_FILE_; + } +private: + std::chrono::system_clock::duration refresh_interval_{std::chrono::seconds(10)}; + std::string access_key_; + std::string access_secret_; + static const char* CREDENTIAL_FILE_; + static const char* ACCESS_KEY_FIELD_NAME; + static const char* ACCESS_SECRET_FIELD_NAME; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h new file mode 100644 index 000000000..8976c7bdf --- /dev/null +++ b/api/rocketmq/DefaultMQProducer.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +#include "AsyncCallback.h" +#include "Logger.h" +#include "MQMessage.h" +#include "MQSelector.h" +#include "SendResult.h" +#include "CredentialsProvider.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * This class employs pointer-to-implementation paradigm to achieve the goal of stable ABI. + * Refer https://en.cppreference.com/w/cpp/language/pimpl for an explanation. + */ +class DefaultMQProducerImpl; + +class DefaultMQProducer { +public: + explicit DefaultMQProducer(const std::string& group_name); + + ~DefaultMQProducer() = default; + + void start(); + + void shutdown(); + + /** + * Acquire previous send timeout in milliseconds. + * @return Send timeout in milliseconds + */ + int getSendMsgTimeout() const; + + /** + * Set default send message timeout in milliseconds. + * @param timeout_millis Timeout used when sending messages. + */ + void setSendMsgTimeout(int timeout_millis); + + /** + * Send message in synchronous manner. + * @param message Message to send. + * @param filter_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + SendResult send(const MQMessage& message, bool filter_active_broker = false); + SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); + SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); + + SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, + bool select_active_broker = false); + + /** + * Send message in asynchronous manner. + * @param message Message to send. + * @param send_callback Callback to execute on completion of message sending. + * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + void send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker = false); + void send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback); + void send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); + + /** + * send message in Oneway(The implementation is simply ignore the result of send message in synchronous) + * @param message Message to send. + * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. + */ + void sendOneway(const MQMessage& message, bool select_active_broker = false); + void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); + void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + + void setNamesrvAddr(const std::string& name_server_address_list); + + void setGroupName(const std::string& group_name); + + void setInstanceName(const std::string& instance_name); + + void enableTracing(bool enabled); + + bool isTracingEnabled(); + + int getRetryTimes() const; + + void setRetryTimes(int times); + + std::vector getTopicMessageQueueInfo(const std::string& topic); + + void setUnitName(std::string unit_name); + + const std::string& getUnitName(); + + uint32_t compressBodyThreshold() const; + void compressBodyThreshold(uint32_t threshold); + + void arn(const std::string& arn); + + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void setRegion(const std::string& region); + +private: + std::shared_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h new file mode 100644 index 000000000..8a5de93fe --- /dev/null +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +#include "ConsumerType.h" +#include "MQMessageQueue.h" +#include "MQMessageExt.h" +#include "AsyncCallback.h" +#include "CredentialsProvider.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPullConsumerImpl; + +class DefaultMQPullConsumer { +public: + explicit DefaultMQPullConsumer(const std::string& group_name); + + void start(); + + void shutdown(); + + std::future> queuesFor(const std::string& topic); + + std::future queryOffset(const OffsetQuery& query); + + bool pull(const PullMessageQuery& request, PullResult& pull_result); + + void pull(const PullMessageQuery& request, PullCallback* callback); + + void setArn(const std::string& arn); + + void setNamesrvAddr(const std::string& name_srv); + + void setCredentialsProvider(std::shared_ptr credentials_provider); + +private: + std::shared_ptr impl_; +}; + + +ROCKETMQ_NAMESPACE_END + diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h new file mode 100644 index 000000000..b3b5e9cb8 --- /dev/null +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include "AsyncCallback.h" +#include "ConsumerType.h" +#include "ExpressionType.h" +#include "Logger.h" +#include "MQMessageListener.h" +#include "MQMessageQueue.h" +#include "CredentialsProvider.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPushConsumerImpl; + +class DefaultMQPushConsumer { +public: + explicit DefaultMQPushConsumer(const std::string& group_name); + + ~DefaultMQPushConsumer() = default; + + void start(); + + void shutdown(); + + void subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type = ExpressionType::TAG); + + void setConsumeFromWhere(ConsumeFromWhere policy); + + void registerMessageListener(MQMessageListener* listener); + + void setNamesrvAddr(const std::string& name_srv); + + void setGroupName(const std::string& group_name); + + void setConsumeThreadCount(int thread_count); + + void setInstanceName(const std::string& instance_name); + + int getProcessQueueTableSize(); + + void setUnitName(std::string unit_name); + + const std::string& getUnitName() const; + + void enableTracing(bool enabled); + + bool isTracingEnabled(); + + /** + * SDK of this version always uses asynchronous IO operation. As such, this function is no-op + * to keep backward compatibility. + */ + void setAsyncPull(bool); + + /** + * By default maxCacheMsgSize is 1024. Valid range is: 1~65535. Should not change this value unless you know what + * you are doing. + */ + void setMaxCacheMsgSizePerQueue(int max_cache_size); + + /** + * Maximum number of messages passed to each callback. + * @param batch_size Batch size + */ + void setConsumeMessageBatchMaxSize(int batch_size); + + /** + * Lifecycle of executor is managed by external application. Passed-in executor should remain valid after consumer + * start and before stopping. + * @param executor Executor pool used to invoke consume callback. + */ + void setCustomExecutor(const Executor& executor); + + /** + * This function sets maximum number of message that may be consumed per second. + * @param topic Topic to control + * @param threshold Threshold before throttling is enforced. + */ + void setThrottle(const std::string& topic, uint32_t threshold); + + /** + * Set abstract-resource-namespace, in which canonical name of topic, group remains unique. + * @param arn Abstract resource namespace. + */ + void setArn(const char *arn); + + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void setMessageModel(MessageModel message_model); + +private: + std::shared_ptr impl_; + std::string group_name_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h new file mode 100644 index 000000000..e4f3a25c2 --- /dev/null +++ b/api/rocketmq/ErrorCode.h @@ -0,0 +1,23 @@ +#pragma once + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +constexpr int NO_TOPIC_ROUTE_INFO = -1; + +constexpr int FAILED_TO_SELECT_MESSAGE_QUEUE = -2; + +constexpr int FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE = -3; + +constexpr int FAILED_TO_SEND_MESSAGE = -4; + +constexpr int FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY = -5; + +constexpr int ILLEGAL_STATE = -6; + +constexpr int MESSAGE_ILLEGAL = -7; + +constexpr int BAD_CONFIGURATION = -8; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h new file mode 100644 index 000000000..dcafc24e0 --- /dev/null +++ b/api/rocketmq/ExpressionType.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum ExpressionType : int8_t { TAG = 0, SQL92 = 1 }; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h new file mode 100644 index 000000000..b9fa8aec6 --- /dev/null +++ b/api/rocketmq/Logger.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +#include "RocketMQ.h" + +#ifndef SPDLOG_ACTIVE_LEVEL +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +enum Level { Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4 }; + +class Logger { +public: + Logger(); + + ~Logger(); + + void setLevel(Level level); + + void setFileSize(int32_t file_size) { + file_size_ = file_size; + } + + void setFileCount(int32_t file_count) { + file_count_ = file_count; + } + + void setPattern(std::string pattern) { + pattern_ = std::move(pattern); + } + + void init(); + +private: + Level level_; + std::string log_home_; + int32_t file_size_; + int32_t file_count_; + std::string pattern_; + + std::once_flag init_once_; + + void init0(); + + static const char* DEFAULT_PATTERN; + static const char* USER_HOME_ENV; +}; + +Logger& getLogger(); + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h new file mode 100644 index 000000000..5c6c0f97f --- /dev/null +++ b/api/rocketmq/MQClientException.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MQException : public std::exception { +public: + MQException(const std::string& msg, int error, const char* file, int line) throw() + : m_error(error), m_line(line), m_file(file) { + try { + std::stringstream ss; + ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; + m_msg = ss.str(); + } catch (...) { + } + } + + MQException(const std::string& msg, int error, const char* file, const char* type, int line) throw() + : m_error(error), m_line(line), m_file(file), m_type(type) { + try { + std::stringstream ss; + ss << "msg: " << msg << ",error:" << error << ",in file <" << file << "> line:" << line; + m_msg = ss.str(); + } catch (...) { + } + } + + virtual ~MQException() throw() {} + + const char* what() const throw() { return m_msg.c_str(); } + + int GetError() const throw() { return m_error; } + + virtual const char* GetType() const throw() { return m_type.c_str(); } + +protected: + int m_error; + int m_line; + std::string m_msg; + std::string m_file; + std::string m_type; +}; + +inline std::ostream& operator<<(std::ostream& os, const MQException& e) { + os << "Type: " << e.GetType() << " , " << e.what(); + return os; +} + +#define DEFINE_MQ_CLIENT_EXCEPTION(name) \ + class name : public MQException { \ + public: \ + name(const std::string& msg, int error, const char* file, int line) throw() \ + : MQException(msg, error, file, #name, line) {} \ + virtual const char* GetType() const throw() { return m_type.c_str(); } \ + }; + +DEFINE_MQ_CLIENT_EXCEPTION(MQClientException) +DEFINE_MQ_CLIENT_EXCEPTION(MQBrokerException) +DEFINE_MQ_CLIENT_EXCEPTION(InterruptedException) +DEFINE_MQ_CLIENT_EXCEPTION(RemotingException) +DEFINE_MQ_CLIENT_EXCEPTION(UnknownHostException) + +#define THROW_MQ_EXCEPTION(e, msg, err) throw e(msg, err, __FILE__, __LINE__) +#define NEW_MQ_EXCEPTION(e, msg, err) e(msg, err, __FILE__, __LINE__) + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h new file mode 100644 index 000000000..544e13798 --- /dev/null +++ b/api/rocketmq/MQMessage.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageImpl; + +class MessageAccessor; + +class MQMessage { +public: + MQMessage(); + MQMessage(const std::string& topic, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); + + virtual ~MQMessage(); + + MQMessage(const MQMessage& other); + MQMessage& operator=(const MQMessage& other); + + void setProperty(const std::string& name, const std::string& value); + std::string getProperty(const std::string& name) const; + + const std::string& getTopic() const; + void setTopic(const std::string& topic); + void setTopic(const char* data, int len); + + std::string getTags() const; + void setTags(const std::string& tags); + + const std::vector& getKeys() const; + void setKey(const std::string& key); + void setKeys(const std::vector& keys); + + int getDelayTimeLevel() const; + void setDelayTimeLevel(int level); + + const std::string& getBody() const; + void setBody(const char* data, int len); + void setBody(const std::string& body); + + int32_t bodyLength() const; + + const std::map& getProperties() const; + void setProperties(const std::map& properties); + +protected: + MessageImpl* impl_; + + friend class MessageAccessor; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h new file mode 100644 index 000000000..ae5f3ef2b --- /dev/null +++ b/api/rocketmq/MQMessageExt.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageAccessor; + +class MQMessageExt : public MQMessage { +public: + MQMessageExt(); + + MQMessageExt(const MQMessageExt& other); + + MQMessageExt& operator=(const MQMessageExt& other); + + int32_t getQueueId() const; + + /** + * @return Milli-seconds since epoch in perspective of system_clock. + */ + int64_t getBornTimestamp() const; + + std::chrono::system_clock::time_point bornTimestamp() const; + + std::string getBornHost() const; + + std::chrono::system_clock::time_point storeTimestamp() const; + int64_t getStoreTimestamp() const; + + std::string getStoreHost() const; + + const std::string& getMsgId() const; + + int64_t getQueueOffset() const; + + int32_t getReconsumeTimes() const; + + const std::string& receiptHandle() const; + + const std::string& traceContext() const; + + bool operator==(const MQMessageExt& other); + + friend class MessageAccessor; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageListener.h b/api/rocketmq/MQMessageListener.h new file mode 100644 index 000000000..07be63c71 --- /dev/null +++ b/api/rocketmq/MQMessageListener.h @@ -0,0 +1,35 @@ +#pragma once + +#include "MQMessageExt.h" +#include "MQMessageQueue.h" +#include +#include "iostream" + +ROCKETMQ_NAMESPACE_BEGIN + +enum ConsumeStatus { CONSUME_SUCCESS, RECONSUME_LATER }; + +enum MessageListenerType { messageListenerDefaultly = 0, messageListenerOrderly = 1, messageListenerConcurrently = 2 }; + +class MQMessageListener { +public: + virtual ~MQMessageListener() {} + virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; + virtual MessageListenerType getMessageListenerType() { return messageListenerDefaultly; } +}; + +class MessageListenerOrderly : public MQMessageListener { +public: + virtual ~MessageListenerOrderly() {} + virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; + virtual MessageListenerType getMessageListenerType() { return messageListenerOrderly; } +}; + +class MessageListenerConcurrently : public MQMessageListener { +public: + virtual ~MessageListenerConcurrently() {} + virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; + virtual MessageListenerType getMessageListenerType() { return messageListenerConcurrently; } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h new file mode 100644 index 000000000..d7d907070 --- /dev/null +++ b/api/rocketmq/MQMessageQueue.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageQueue { +public: + MQMessageQueue() = default; + + MQMessageQueue(std::string topic, std::string broker_name, int queue_id) + : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) {} + + MQMessageQueue(const MQMessageQueue& other) { this->operator=(other); } + + MQMessageQueue(MQMessageQueue&& other) noexcept { + topic_ = std::move(other.topic_); + broker_name_ = std::move(other.broker_name_); + queue_id_ = other.queue_id_; + service_address_ = other.service_address_; + } + + MQMessageQueue& operator=(const MQMessageQueue& other) { + if (this == &other) { + return *this; + } + + topic_ = other.topic_; + broker_name_ = other.broker_name_; + queue_id_ = other.queue_id_; + service_address_ = other.service_address_; + return *this; + } + + const std::string& getTopic() const { return topic_; } + + void setTopic(const std::string& topic) { topic_ = topic; } + + const std::string& getBrokerName() const { return broker_name_; } + + void setBrokerName(const std::string& broker_name) { broker_name_ = broker_name; } + + int getQueueId() const { return queue_id_; } + + void setQueueId(int queue_id) { queue_id_ = queue_id; } + + bool operator!=(const MQMessageQueue& rhs) const { + return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || queue_id_ != rhs.queue_id_; + } + + bool operator==(const MQMessageQueue& mq) const { + return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && queue_id_ == mq.queue_id_; + } + + bool operator<(const MQMessageQueue& mq) const { + if (topic_ != mq.topic_) { + return topic_ < mq.topic_; + } + + if (broker_name_ != mq.broker_name_) { + return broker_name_ < mq.broker_name_; + } + + return queue_id_ < mq.queue_id_; + } + + int compareTo(const MQMessageQueue& mq) const { + if (this == &mq) { + return 0; + } + + if (this->operator<(mq)) { + return -1; + } + + if (mq.operator<(*this)) { + return 1; + } + + return 0; + } + + std::string simpleName() const { return topic_ + "_" + broker_name_ + "_" + std::to_string(queue_id_); } + + std::string toString() const { + std::stringstream ss; + ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ << ", queueId=" << queue_id_ << "]"; + return ss.str(); + } + + template friend H AbslHashValue(H h, const MQMessageQueue& mq) { + return H::combine(std::move(h), mq.topic_, mq.broker_name_, mq.queue_id_); + } + + const std::string& serviceAddress() const { + return service_address_; + } + + void serviceAddress(std::string service_address) { + service_address_ = std::move(service_address); + } + +private: + std::string topic_; + std::string broker_name_; + int queue_id_{0}; + + std::string service_address_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQSelector.h b/api/rocketmq/MQSelector.h new file mode 100644 index 000000000..1131691e1 --- /dev/null +++ b/api/rocketmq/MQSelector.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "MQMessageQueue.h" +#include "MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageQueueSelector { +public: + virtual ~MessageQueueSelector() {} + virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/OffsetStore.h b/api/rocketmq/OffsetStore.h new file mode 100644 index 000000000..309bc0209 --- /dev/null +++ b/api/rocketmq/OffsetStore.h @@ -0,0 +1,18 @@ +#pragma once + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OffsetStore { +public: + virtual ~OffsetStore() = default; + + virtual void load() = 0; + + virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; + + virtual bool readOffset(const MQMessageQueue& message_queue, int64_t& offset) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h new file mode 100644 index 000000000..473cae3fe --- /dev/null +++ b/api/rocketmq/PullResult.h @@ -0,0 +1,29 @@ +#pragma once + +#include "MQMessageExt.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class PullResult { +public: + PullResult(int64_t min, int64_t max, int64_t next, std::vector messages) + : min_(min), max_(max), next_(next), messages_(std::move(messages)) {} + + int64_t min() const { return min_; } + + int64_t max() const { return max_; } + + int64_t next() const { return next_; } + + const std::vector& messages() const { return messages_; } + +private: + int64_t min_; + int64_t max_; + int64_t next_; + std::vector messages_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/RocketMQ.h b/api/rocketmq/RocketMQ.h new file mode 100644 index 000000000..6c1f4d1d4 --- /dev/null +++ b/api/rocketmq/RocketMQ.h @@ -0,0 +1,8 @@ +#pragma once + +#ifndef ROCKETMQ_NAMESPACE +#define ROCKETMQ_NAMESPACE rocketmq +#endif + +#define ROCKETMQ_NAMESPACE_BEGIN namespace ROCKETMQ_NAMESPACE { +#define ROCKETMQ_NAMESPACE_END } \ No newline at end of file diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h new file mode 100644 index 000000000..1bd924dfe --- /dev/null +++ b/api/rocketmq/SendResult.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum SendStatus { SEND_OK, SEND_FLUSH_DISK_TIMEOUT, SEND_FLUSH_SLAVE_TIMEOUT, SEND_SLAVE_NOT_AVAILABLE }; + +class SendResult { + public: + SendResult() {} + + SendResult(const SendStatus& send_status, std::string msg_id, MQMessageQueue message_queue, + long long queue_offset, std::string region_id) + : send_status_(send_status), + message_id_(std::move(msg_id)), + message_queue_(std::move(message_queue)), + queue_offset_(queue_offset), + region_id_(std::move(region_id)) {} + + ~SendResult() {} + + SendResult(const SendResult& other) { this->operator=(other); } + + SendResult& operator=(const SendResult& other) { + if (this == &other) { + return *this; + } + + send_status_ = other.send_status_; + message_id_ = other.message_id_; + message_queue_ = other.message_queue_; + queue_offset_ = other.queue_offset_; + transaction_id_ = other.transaction_id_; + region_id_ = other.region_id_; + return *this; + } + + const std::string& getMsgId() const { return message_id_; } + + void setMsgId(const std::string& msg_id) { message_id_ = msg_id; } + + const std::string& getRegionId() const { return region_id_; } + + void setRegionId(const std::string& region_id) { region_id_ = region_id; } + + SendStatus getSendStatus() const { return send_status_; } + + void setSendStatus(const SendStatus& send_status) { send_status_ = send_status; } + + MQMessageQueue& getMessageQueue() const { return message_queue_; } + + void setMessageQueue(const MQMessageQueue& message_queue) { message_queue_ = message_queue; } + + long long getQueueOffset() const { return queue_offset_; } + + void setQueueOffset(long long queue_offset) { queue_offset_ = queue_offset; } + + const std::string& getTransactionId() const { return transaction_id_; } + + void setTransactionId(const std::string& transaction_id) { transaction_id_ = transaction_id; } + + private: + SendStatus send_status_; + std::string message_id_; + mutable MQMessageQueue message_queue_; + long long queue_offset_; + std::string transaction_id_; + std::string region_id_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/State.h b/api/rocketmq/State.h new file mode 100644 index 000000000..db603cc7e --- /dev/null +++ b/api/rocketmq/State.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum State : uint8_t { CREATED = 0, STARTING = 1, STARTED = 2, STOPPING = 3, STOPPED = 4 }; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h new file mode 100644 index 000000000..2f90b18d8 --- /dev/null +++ b/api/rocketmq/Transaction.h @@ -0,0 +1,18 @@ +#pragma once + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Transaction { +public: + Transaction() = default; + + virtual ~Transaction() = default; + + virtual bool commit() = 0; + + virtual bool rollback() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/bazel/BUILD b/bazel/BUILD new file mode 100644 index 000000000..e69de29bb diff --git a/bazel/curl.bzl b/bazel/curl.bzl new file mode 100644 index 000000000..21d99e458 --- /dev/null +++ b/bazel/curl.bzl @@ -0,0 +1,201 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Compiler options for building libcurl. + +BASE_CURL_COPTS = [ + # Disable everything else except HTTP protocol. + "-DHTTP_ONLY=1", + "-DENABLE_IPV6=1", + "-DGETHOSTNAME_TYPE_ARG2=size_t", + "-DGETSERVBYPORT_R_ARGS=6", + "-DGETSERVBYPORT_R_BUFSIZE=4096", + "-DHAVE_ALARM=1", + "-DHAVE_ALLOCA_H=1", + "-DHAVE_ARPA_INET_H=1", + "-DHAVE_ARPA_TFTP_H=1", + "-DHAVE_ASSERT_H=1", + "-DHAVE_BASENAME=1", + "-DHAVE_BOOL_T=1", + "-DHAVE_CLOCK_GETTIME_MONOTONIC=1", + "-DHAVE_CONNECT=1", + "-DHAVE_DLFCN_H=1", + "-DHAVE_ENGINE_LOAD_BUILTIN_ENGINES=1", + "-DHAVE_ERRNO_H=1", + "-DHAVE_FCNTL=1", + "-DHAVE_FCNTL_H=1", + "-DHAVE_FCNTL_O_NONBLOCK=1", + "-DHAVE_FDOPEN=1", + "-DHAVE_FREEADDRINFO=1", + "-DHAVE_FREEIFADDRS=1", + "-DHAVE_FSETXATTR=1", + "-DHAVE_FSETXATTR_5=1", + "-DHAVE_FTRUNCATE=1", + "-DHAVE_GAI_STRERROR=1", + "-DHAVE_GETADDRINFO=1", + "-DHAVE_GETADDRINFO_THREADSAFE=1", + "-DHAVE_GETEUID=1", + "-DHAVE_GETHOSTBYADDR=1", + "-DHAVE_GETHOSTBYADDR_R=1", + "-DHAVE_GETHOSTBYADDR_R_8=1", + "-DHAVE_GETHOSTBYNAME=1", + "-DHAVE_GETHOSTBYNAME_R=1", + "-DHAVE_GETHOSTBYNAME_R_6=1", + "-DHAVE_GETHOSTNAME=1", + "-DHAVE_GETIFADDRS=1", + "-DHAVE_GETNAMEINFO=1", + "-DHAVE_GETPPID=1", + "-DHAVE_GETPWUID=1", + "-DHAVE_GETPWUID_R=1", + "-DHAVE_GETRLIMIT=1", + "-DHAVE_GETSERVBYPORT_R=1", + "-DHAVE_GETTIMEOFDAY=1", + "-DHAVE_GMTIME_R=1", + "-DHAVE_IFADDRS_H=1", + "-DHAVE_IF_NAMETOINDEX=1", + "-DHAVE_INET_NTOP=1", + "-DHAVE_INET_PTON=1", + "-DHAVE_INTTYPES_H=1", + "-DHAVE_IOCTL=1", + "-DHAVE_IOCTL_FIONBIO=1", + "-DHAVE_IOCTL_SIOCGIFADDR=1", + "-DHAVE_LIBGEN_H=1", + "-DHAVE_LL=1", + "-DHAVE_LOCALE_H=1", + "-DHAVE_LOCALTIME_R=1", + "-DHAVE_LONGLONG=1", + "-DHAVE_MALLOC_H=1", + "-DHAVE_MEMORY_H=1", + "-DHAVE_NETDB_H=1", + "-DHAVE_NETINET_IN_H=1", + "-DHAVE_NETINET_TCP_H=1", + "-DHAVE_NET_IF_H=1", + "-DHAVE_PIPE=1", + "-DHAVE_POLL=1", + "-DHAVE_POLL_FINE=1", + "-DHAVE_POLL_H=1", + "-DHAVE_POSIX_STRERROR_R=1", + "-DHAVE_PTHREAD_H=1", + "-DHAVE_PWD_H=1", + "-DHAVE_RECV=1", + "-DHAVE_SELECT=1", + "-DHAVE_SEND=1", + "-DHAVE_SETJMP_H=1", + "-DHAVE_SETLOCALE=1", + "-DHAVE_SETRLIMIT=1", + "-DHAVE_SETSOCKOPT=1", + "-DHAVE_SGTTY_H=1", + "-DHAVE_SIGACTION=1", + "-DHAVE_SIGINTERRUPT=1", + "-DHAVE_SIGNAL=1", + "-DHAVE_SIGNAL_H=1", + "-DHAVE_SIGSETJMP=1", + "-DHAVE_SIG_ATOMIC_T=1", + "-DHAVE_SOCKADDR_IN6_SIN6_SCOPE_ID=1", + "-DHAVE_SOCKET=1", + "-DHAVE_SOCKETPAIR=1", + "-DHAVE_STDBOOL_H=1", + "-DHAVE_STDINT_H=1", + "-DHAVE_STDIO_H=1", + "-DHAVE_STDLIB_H=1", + "-DHAVE_STRCASECMP=1", + "-DHAVE_STRDUP=1", + "-DHAVE_STRERROR_R=1", + "-DHAVE_STRINGS_H=1", + "-DHAVE_STRING_H=1", + "-DHAVE_STRNCASECMP=1", + "-DHAVE_STRSTR=1", + "-DHAVE_STRTOK_R=1", + "-DHAVE_STRTOLL=1", + "-DHAVE_STRUCT_SOCKADDR_STORAGE=1", + "-DHAVE_STRUCT_TIMEVAL=1", + "-DHAVE_SYS_IOCTL_H=1", + "-DHAVE_SYS_PARAM_H=1", + "-DHAVE_SYS_POLL_H=1", + "-DHAVE_SYS_RESOURCE_H=1", + "-DHAVE_SYS_SELECT_H=1", + "-DHAVE_SYS_SOCKET_H=1", + "-DHAVE_SYS_STAT_H=1", + "-DHAVE_SYS_TIME_H=1", + "-DHAVE_SYS_TYPES_H=1", + "-DHAVE_SYS_UIO_H=1", + "-DHAVE_SYS_UN_H=1", + "-DHAVE_SYS_WAIT_H=1", + "-DHAVE_SYS_XATTR_H=1", + "-DHAVE_TERMIOS_H=1", + "-DHAVE_TERMIO_H=1", + "-DHAVE_TIME_H=1", + "-DHAVE_UNISTD_H=1", + "-DHAVE_UTIME=1", + "-DHAVE_UTIMES=1", + "-DHAVE_UTIME_H=1", + "-DHAVE_VARIADIC_MACROS_C99=1", + "-DHAVE_VARIADIC_MACROS_GCC=1", + "-DHAVE_WRITABLE_ARGV=1", + "-DHAVE_WRITEV=1", + "-DRECV_TYPE_ARG1=int", + "-DRECV_TYPE_ARG2=void*", + "-DRECV_TYPE_ARG3=size_t", + "-DRECV_TYPE_ARG4=int", + "-DRECV_TYPE_RETV=ssize_t", + "-DRETSIGTYPE=void", + "-DSELECT_QUAL_ARG5=", + "-DSELECT_TYPE_ARG1=int", + "-DSELECT_TYPE_ARG234=fd_set*", + "-DSELECT_TYPE_RETV=int", + "-DSEND_QUAL_ARG2=const", + "-DSEND_TYPE_ARG1=int", + "-DSEND_TYPE_ARG2=void*", + "-DSEND_TYPE_ARG3=size_t", + "-DSEND_TYPE_ARG4=int", + "-DSEND_TYPE_RETV=ssize_t", + "-DSIZEOF_CURL_OFF_T=8", + "-DSIZEOF_INT=4", + "-DSIZEOF_LONG=8", + "-DSIZEOF_OFF_T=8", + "-DSIZEOF_SHORT=2", + "-DSIZEOF_SIZE_T=8", + "-DSIZEOF_TIME_T=8", + "-DSTDC_HEADERS=1", + "-DSTRERROR_R_TYPE_ARG3=size_t", + "-DTIME_WITH_SYS_TIME=1", + "-DUSE_THREADS_POSIX=1", + "-DUSE_UNIX_SOCKETS=1", + + # Extra defines needed by curl + "-DBUILDING_LIBCURL", + "-DCURL_HIDDEN_SYMBOLS", +] + +LINUX_CURL_COPTS = [ + "-DHAVE_LINUX_TCP_H=1", + "-DHAVE_MSG_NOSIGNAL=1", +] + +CURL_COPTS = select({ + "//:windows": [ + # Disable everything else except HTTP protocol. + "/DHTTP_ONLY=1", + "/DCURL_STATICLIB", + "/DWIN32", + "/DBUILDING_LIBCURL", + "/DUSE_WIN32_IDN", + "/DWANT_IDN_PROTOTYPES", + "/DUSE_IPV6", + "/DUSE_WINDOWS_SSPI", + "/DUSE_SCHANNEL", + ], + "//:osx": BASE_CURL_COPTS, + "//conditions:default": BASE_CURL_COPTS + LINUX_CURL_COPTS, +}) \ No newline at end of file diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl new file mode 100644 index 000000000..d0ebd634a --- /dev/null +++ b/bazel/rocketmq_deps.bzl @@ -0,0 +1,127 @@ +"""Load dependencies needed to compile and test the RocketMQ library as a 3rd-party consumer.""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") + +def rocketmq_deps(): + """Loads dependencies need to compile and test the RocketMQ library.""" + native.bind( + name = "opentelementry_api", + actual = "@com_github_opentelemetry//api:api", + ) + + if "com_google_googletest" not in native.existing_rules(): + http_archive( + name = "com_google_googletest", + sha256 = "b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5", + strip_prefix = "googletest-release-1.11.0", + urls = [ + "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz", + ], + ) + + if "com_github_gulrak_filesystem" not in native.existing_rules(): + http_archive( + name = "com_github_gulrak_filesystem", + strip_prefix = "filesystem-1.5.0", + sha256 = "eb6f3b0739908ad839cde68885d70e7324db191b9fad63d9915beaa40444d9cb", + urls = [ + "https://github.com/gulrak/filesystem/archive/v1.5.0.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:filesystem.BUILD", + ) + + if "com_github_progschj_ThreadPool" not in native.existing_rules(): + new_git_repository( + name = "com_github_progschj_ThreadPool", + remote = "https://github.com/progschj/ThreadPool.git", + branch = "master", + build_file = "@org_apache_rocketmq//third_party:ThreadPool.BUILD" + ) + + if "com_github_gabime_spdlog" not in native.existing_rules(): + http_archive( + name = "com_github_gabime_spdlog", + strip_prefix = "spdlog-1.8.2", + sha256 = "e20e6bd8f57e866eaf25a5417f0a38a116e537f1a77ac7b5409ca2b180cec0d5", + urls = [ + "https://github.com/gabime/spdlog/archive/v1.8.2.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:spdlog.BUILD", + ) + + if "com_github_fmtlib_fmt" not in native.existing_rules(): + http_archive( + name = "com_github_fmtlib_fmt", + sha256 = "5cae7072042b3043e12d53d50ef404bbb76949dad1de368d7f993a15c8c05ecc", + strip_prefix = "fmt-7.1.3", + urls = [ + "https://github.com/fmtlib/fmt/archive/7.1.3.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:fmtlib.BUILD", + ) + + if "com_google_protobuf" not in native.existing_rules(): + http_archive( + name = "com_google_protobuf", + strip_prefix = "protobuf-3.17.2", + urls = [ + "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.17.2.tar.gz", + ], + ) + + if "rules_proto_grpc" not in native.existing_rules(): + http_archive( + name = "rules_proto_grpc", + sha256 = "7954abbb6898830cd10ac9714fbcacf092299fda00ed2baf781172f545120419", + strip_prefix = "rules_proto_grpc-3.1.1", + urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/3.1.1.tar.gz"], + ) + + if "curl" not in native.existing_rules(): + http_archive( + name = "curl", + strip_prefix = "curl-7.73.0", + sha256 = "ba98332752257b47b9dea6d8c0ad25ec1745c20424f1dd3ff2c99ab59e97cf91", + urls = ["https://curl.haxx.se/download/curl-7.73.0.tar.gz"], + build_file = "@org_apache_rocketmq//third_party:curl.BUILD", + ) + + if "libuv" not in native.existing_rules(): + http_archive( + name = "libuv", + sha256 = "dfb4fe1ff0b47340978490a14bf253475159ecfcbad46ab2a350c78f9ce3360f", + strip_prefix = "libuv-15ae750151ac9341e5945eb38f8982d59fb99201", + urls = [ + "https://storage.googleapis.com/grpc-bazel-mirror/github.com/libuv/libuv/archive/15ae750151ac9341e5945eb38f8982d59fb99201.tar.gz", + "https://github.com/libuv/libuv/archive/15ae750151ac9341e5945eb38f8982d59fb99201.tar.gz", + ], + build_file = "@org_apache_rocketmq//third_party:libuv.BUILD", + ) + + if "com_google_absl" not in native.existing_rules(): + http_archive( + name = "com_google_absl", + strip_prefix = "abseil-cpp-20210324.2", + urls = [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20210324.2.tar.gz", + ], + ) + + if "com_github_grpc_grpc" not in native.existing_rules(): + http_archive( + name = "com_github_grpc_grpc", + strip_prefix = "grpc-1.38.0", + sha256 = "abd9e52c69000f2c051761cfa1f12d52d8b7647b6c66828a91d462e796f2aede", + urls = ["https://github.com/grpc/grpc/archive/v1.38.0.tar.gz"], + ) + + if "com_github_opentelemetry" not in native.existing_rules(): + http_archive( + name = "com_github_opentelemetry", + sha256 = "2b5b423096c6e486ec23e77d0d75b28e32f86e7cdf6e7560da686c71c69908d3", + strip_prefix = "opentelemetry-cpp-1.0.0-rc2", + urls = [ + "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.0.0-rc2.tar.gz", + ], + ) \ No newline at end of file diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 000000000..3b48b7d66 --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +CI_DIR=$(dirname "$0") +BASE_DIR=$(pwd $CI_DIR/..) + +cd $BASE_DIR +mkdir _build +cd _build +cmake .. +make -j $(nproc) +ctest \ No newline at end of file diff --git a/docs/DevelopmentGuide.md b/docs/DevelopmentGuide.md new file mode 100644 index 000000000..6933d804d --- /dev/null +++ b/docs/DevelopmentGuide.md @@ -0,0 +1,18 @@ +### 前言 +C++是一门复杂的开发语言. 我们需要遵从一些约定, 以降低使用它的复杂度, 提升工程的可读性. 目前业界最广为接受的约定无疑是Google的 +[C++ Style Guide](https://google.github.io/styleguide/cppguide.html)了, 本项目尽量遵从这个约定. + +尽量, 主要是出于考虑商业产品API的一致性. 我们需要和现有[SDK API](http://gitlab.alibaba-inc.com/middleware/rocketmq-client4cpp)保持一致. +这样, 我们就有一些妥协的结果. 以异常为例, 在实现内部, 我们不适用异常. 但在DefaultMQProducer这个层面, 为了保持已有API兼容, 我们需 +要将返回值转义为异常. + +### 编译工具 +项目使用CMake和Bazel两种管理工具. + + +### Define the following environment variables +export no_grpc_proxy=mq.docker.dev +export GRPC_TRACE=all +export GRPC_VERBOSITY=DEBUG +export http_proxy=http://30.57.177.111:3128 +export https_proxy=http://30.57.177.111:3128 \ No newline at end of file diff --git a/docs/gtest.md b/docs/gtest.md new file mode 100644 index 000000000..0c40d9d7f --- /dev/null +++ b/docs/gtest.md @@ -0,0 +1,8 @@ +## Run a single test case +1. Get all test cases by running tests with --gtest_list_tests +1. Parse this data into your GUI +1. Select test cases you want ro run +1. Run test executable with option --gtest_filter= + +## Run tests multiple times +bazel test --runs_per_test=10 //... \ No newline at end of file diff --git a/docs/test_coverage.md b/docs/test_coverage.md new file mode 100644 index 000000000..2c836a9ce --- /dev/null +++ b/docs/test_coverage.md @@ -0,0 +1,15 @@ +### Generate Coverage Data +```text + bazel coverage -s \ + --instrument_test_targets \ + --experimental_cc_coverage \ + --combined_report=lcov \ + --coverage_report_generator=@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main \ + //src/test/... +``` + +### Generate HTML pages +```text +genhtml bazel-out/_coverage/_coverage_report.dat \ + --output-directory coverage_html +``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 000000000..ee3f37f78 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(rocketmq) \ No newline at end of file diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel new file mode 100644 index 000000000..01684fba5 --- /dev/null +++ b/example/rocketmq/BUILD.bazel @@ -0,0 +1,89 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary") + +cc_binary( + name = "example_producer", + srcs = [ + "ExampleProducer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_async_producer", + srcs = [ + "ExampleAsyncProducer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "sql_producer", + srcs = [ + "SqlProducer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_push_consumer", + srcs = [ + "ExamplePushConsumer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "push_consumer_with_custom_executor", + srcs = [ + "PushConsumerWithCustomExecutor.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "push_consumer_with_throttle", + srcs = [ + "PushConsumerWithThrottle.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "sql_consumer", + srcs = [ + "SqlConsumer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_pull_consumer", + srcs = [ + "ExamplePullConsumer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) \ No newline at end of file diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp new file mode 100644 index 000000000..dddb8dad0 --- /dev/null +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -0,0 +1,65 @@ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include +#include + +using namespace rocketmq; + +class CounterMessageListener : public MessageListenerConcurrently { +public: + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} + + ConsumeStatus consumeMessage(const std::vector& msgs) override { + counter_.fetch_add(msgs.size()); + return ConsumeStatus::CONSUME_SUCCESS; + } + +private: + std::atomic_long& counter_; +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + std::atomic_long counter(0); + + DefaultMQPushConsumer push_consumer("CID_sample"); + MQMessageListener* listener = new CounterMessageListener(counter); + + push_consumer.setGroupName("CID_sample"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TopicTest", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::atomic_bool stopped(false); + std::thread report_thread([&counter, &stopped]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps; + while (true) { + qps = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(qps, 0, std::memory_order_relaxed)) { + break; + } + } + std::cout << "QPS: " << qps << std::endl; + } + }); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + stopped.store(true); + + if (report_thread.joinable()) { + report_thread.join(); + } + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/CMakeLists.txt b/example/rocketmq/CMakeLists.txt new file mode 100644 index 000000000..c4f9af5d7 --- /dev/null +++ b/example/rocketmq/CMakeLists.txt @@ -0,0 +1,65 @@ +add_executable(example_producer ExampleProducer.cpp) +target_link_libraries(example_producer + PRIVATE + CURL::libcurl + rocketmq + $<$:-static-libgcc> + $<$:-static-libstdc++>) + +add_executable(example_async_producer ExampleAsyncProducer.cpp) +target_link_libraries(example_async_producer + PRIVATE + CURL::libcurl + rocketmq + $<$:-static-libgcc> + $<$:-static-libstdc++>) + + +add_executable(example_push_consumer ExamplePushConsumer.cpp) +target_link_libraries(example_push_consumer + PRIVATE + CURL::libcurl + rocketmq + spdlog::spdlog_header_only + $<$:-static-libgcc> + $<$:-static-libstdc++>) + + +add_executable(example_broadcast_push_consumer ExampleBroadcastPushConsumer.cpp) +target_link_libraries(example_broadcast_push_consumer + PRIVATE + CURL::libcurl + rocketmq + spdlog::spdlog_header_only + $<$:-static-libgcc> + $<$:-static-libstdc++>) + +add_executable(benchmark_push_consumer BenchmarkPushConsumer.cpp) +target_link_libraries(benchmark_push_consumer + CURL::libcurl + rocketmq) + +add_executable(sql_producer SqlProducer.cpp) +target_link_libraries(sql_producer + CURL::libcurl + rocketmq) + +add_executable(sql_consumer SqlConsumer.cpp) +target_link_libraries(sql_consumer + CURL::libcurl + rocketmq) + +add_executable(consumer_with_custom_executor PushConsumerWithCustomExecutor.cpp) +target_link_libraries(consumer_with_custom_executor + CURL::libcurl + rocketmq) + +add_executable(consumer_with_throttle PushConsumerWithThrottle.cpp) +target_link_libraries(consumer_with_throttle + CURL::libcurl + rocketmq) + +add_executable(pull_consumer ExamplePullConsumer.cpp) +target_link_libraries(pull_consumer + CURL::libcurl + rocketmq) \ No newline at end of file diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp new file mode 100644 index 000000000..b37bc740d --- /dev/null +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -0,0 +1,229 @@ +#include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace rocketmq; + +int getAndReset(std::atomic_int& counter) { + int current; + while (true) { + current = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(current, 0, std::memory_order_relaxed)) { + break; + } + } + return current; +} + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +template class RateLimiter { +public: + explicit RateLimiter(int permit) : permits_{0}, interval_(1000 / PARTITION), stopped_(false) { + int avg = permit / PARTITION; + for (auto& i : partition_) { + i = avg; + } + + int r = permit % PARTITION; + + if (r) { + int step = PARTITION / r; + for (int i = 0; i < r; ++i) { + partition_[i * step]++; + } + } + + auto lambda_tick = [this]() { + while (!stopped_) { + tick(); + } + }; + + thread_tick_ = std::thread(lambda_tick); + } + + ~RateLimiter() { + stopped_.store(true, std::memory_order_relaxed); + if (thread_tick_.joinable()) { + thread_tick_.join(); + } + } + + std::array& partition() { return permits_; } + + int slot() { + auto current = std::chrono::steady_clock::now(); + long ms = std::chrono::duration_cast(current.time_since_epoch()).count(); + return ms / interval_ % PARTITION; + } + + void acquire() { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] > 0) { + --permits_[idx]; + return; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + --permits_[index]; + return; + } + } + + cv_.wait(lk, [this]() { + int idx = slot(); + return permits_[idx] > 0; + }); + idx = slot(); + --permits_[idx]; + } + } + + void tick() { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / PARTITION)); + int idx = slot(); + { + std::unique_lock lk(mtx_); + permits_[idx] = partition_[idx]; + cv_.notify_all(); + } + } + +private: + std::array partition_; + std::array permits_; + int interval_; + std::mutex mtx_; + std::condition_variable cv_; + + std::atomic_bool stopped_; + std::thread thread_tick_; +}; + +class SampleSendCallback : public rocketmq::SendCallback { +public: + SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) {} + + void onSuccess(const SendResult& send_result) override { counter_.fetch_add(1, std::memory_order_relaxed); } + + void onException(const MQException& e) override { error_.fetch_add(1, std::memory_order_relaxed); } + +private: + std::atomic_int& counter_; + std::atomic_int& error_; +}; + +int main(int argc, char* argv[]) { + std::string::size_type body_size = 1024; + int duration = 300; + int qps = 1000; + + if (argc > 1) { + body_size = std::stoi(argv[1]); + } + + if (argc > 2) { + duration = std::stoi(argv[2]); + } + + if (argc > 3) { + qps = std::stoi(argv[3]); + } + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("TestGroup"); + producer.setNamesrvAddr("11.165.223.199:9876"); + producer.compressBodyThreshold(256); + const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + producer.setCredentialsProvider(std::make_shared()); + producer.arn(arn); + producer.setRegion("cn-hangzhou"); + MQMessage message; + message.setTopic("yc001"); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody(randomString(body_size)); + + std::cout << "Message Body: " << message.getBody() << std::endl; + + std::atomic_bool stopped(false); + + auto lambda = [&stopped, duration]() { + std::cout << "Benchmark will stop in " << duration << " seconds!" << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(duration)); + stopped.store(true); + }; + + std::thread t(lambda); + + std::atomic_int success(0); + std::atomic_int error(0); + + auto stats_lambda = [&stopped, &success, &error]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "Success:" << getAndReset(success) << ", Error: " << getAndReset(error) << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + RateLimiter<10> rate_limiter(qps); + SampleSendCallback send_callback(success, error); + + try { + producer.start(); + while (!stopped) { + rate_limiter.acquire(); + producer.send(message, &send_callback, true); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + if (stats_thread.joinable()) { + stats_thread.join(); + } + + if (t.joinable()) { + t.join(); + } + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp new file mode 100644 index 000000000..5b5c53687 --- /dev/null +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -0,0 +1,48 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#include "spdlog/spdlog.h" + +#include +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + for (const MQMessageExt& msg : msgs) { + SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + return ConsumeStatus::CONSUME_SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* cid = "GID_group003"; + const char *topic = "yc001"; + const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + + DefaultMQPushConsumer push_consumer(cid); + push_consumer.setMessageModel(MessageModel::BROADCASTING); + push_consumer.setArn(arn); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr("11.165.223.199:9876"); + MQMessageListener* listener = new SampleMQMessageListener; + push_consumer.setGroupName(cid); + push_consumer.setInstanceName("instance_0"); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(60)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp new file mode 100644 index 000000000..9d9d54062 --- /dev/null +++ b/example/rocketmq/ExampleProducer.cpp @@ -0,0 +1,87 @@ +#include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include + +using namespace rocketmq; + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("TestGroup"); + producer.setNamesrvAddr("11.165.223.199:9876"); + producer.compressBodyThreshold(256); + const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + producer.setRegion("cn-hangzhou"); + producer.arn(arn); + producer.setCredentialsProvider(std::make_shared()); + + MQMessage message; + message.setTopic("yc001"); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + + std::atomic_bool stopped; + std::atomic_long count(0); + + auto stats_lambda = [&] { + while (!stopped.load(std::memory_order_relaxed)) { + long cnt = count.load(std::memory_order_relaxed); + while (count.compare_exchange_weak(cnt, 0)) { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "QPS: " << cnt << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + std::string body = randomString(1024); + std::cout << "Message body: " << body << std::endl; + message.setBody(body); + + try { + producer.start(); + for (int i = 0; i < 102400; ++i) { + SendResult sendResult = producer.send(message); + std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + count++; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + stopped.store(true, std::memory_order_relaxed); + if (stats_thread.joinable()) { + stats_thread.join(); + } + + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp new file mode 100644 index 000000000..0858186df --- /dev/null +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -0,0 +1,36 @@ +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/DefaultMQPullConsumer.h" +#include "rocketmq/Logger.h" +#include +#include + +int main(int argc, char* argv[]) { + const char* group = "GID_group003"; + const char* topic = "yc001"; + const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* name_server_list = "ipv4:11.165.223.199:9876"; + + rocketmq::Logger& logger = rocketmq::getLogger(); + logger.setLevel(rocketmq::Level::Debug); + logger.init(); + + rocketmq::DefaultMQPullConsumer pull_consumer(group); + pull_consumer.setArn(arn); + pull_consumer.setCredentialsProvider(std::make_shared()); + pull_consumer.setNamesrvAddr(name_server_list); + pull_consumer.start(); + + std::future> future = pull_consumer.queuesFor(topic); + auto queues = future.get(); + + for (const auto& queue : queues) { + rocketmq::OffsetQuery offset_query; + offset_query.message_queue = queue; + offset_query.policy = rocketmq::QueryOffsetPolicy::BEGINNING; + auto offset_future = pull_consumer.queryOffset(offset_query); + int64_t offset = offset_future.get(); + } + + pull_consumer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp new file mode 100644 index 000000000..70858ecc6 --- /dev/null +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -0,0 +1,47 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#include "spdlog/spdlog.h" + +#include +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + for (const MQMessageExt& msg : msgs) { + SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + return ConsumeStatus::CONSUME_SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* cid = "GID_group003"; + const char *topic = "yc001"; + const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + + DefaultMQPushConsumer push_consumer(cid); + push_consumer.setArn(arn); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr("11.165.223.199:9876"); + MQMessageListener* listener = new SampleMQMessageListener; + push_consumer.setGroupName(cid); + push_consumer.setInstanceName("instance_0"); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::seconds(300)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp new file mode 100644 index 000000000..e913c34c3 --- /dev/null +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -0,0 +1,131 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/State.h" +#include +#include +#include +#include +#include + +using namespace rocketmq; + +namespace rocketmq { + +class ExecutorImpl { +public: + ExecutorImpl() : state_(State::CREATED) {} + + virtual ~ExecutorImpl() { + switch (state_.load(std::memory_order_relaxed)) { + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + if (worker_.joinable()) { + worker_.join(); + } + break; + } + } + + void submit(const std::function& task) { + if (State::STOPPED == state_.load(std::memory_order_relaxed)) { + return; + } + + { + std::unique_lock lock(task_mtx_); + tasks_.push_back(task); + } + cv_.notify_one(); + } + + void start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING)) { + worker_ = std::thread(std::bind(&ExecutorImpl::loop, this)); + state_.store(State::STARTED); + } + } + + void stop() { + state_.store(State::STOPPED); + if (worker_.joinable()) { + worker_.join(); + } + } + +private: + void loop() { + while (state_.load(std::memory_order_relaxed) != State::STOPPED) { + std::function func; + { + std::unique_lock lk(task_mtx_); + if (!tasks_.empty()) { + func = tasks_.back(); + } + } + + if (func) { + func(); + } else { + std::unique_lock lk(task_mtx_); + cv_.wait_for(lk, std::chrono::seconds(3), + [&]() { return state_.load(std::memory_order_relaxed) == State::STOPPED || !tasks_.empty(); }); + } + } + } + + std::atomic state_; + std::vector> tasks_; + std::mutex task_mtx_; + std::condition_variable cv_; + std::thread worker_; +}; + +} // namespace rocketmq + +class SampleMQMessageListener : public MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + std::lock_guard lk(console_mtx_); + for (const MQMessageExt& msg : msgs) { + std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Body=" << msg.getBody() + << std::endl; + } + return ConsumeStatus::CONSUME_SUCCESS; + } + +private: + std::mutex console_mtx_; +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MQMessageListener* listener = new SampleMQMessageListener; + + auto pool = new ExecutorImpl; + pool->start(); + push_consumer.setCustomExecutor(std::bind(&ExecutorImpl::submit, pool, std::placeholders::_1)); + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TestTopic", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + pool->stop(); + delete pool; + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp new file mode 100644 index 000000000..dc9e977f5 --- /dev/null +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -0,0 +1,66 @@ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include +#include + +using namespace rocketmq; + +class CounterMessageListener : public MessageListenerConcurrently { +public: + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} + + ConsumeStatus consumeMessage(const std::vector& msgs) override { + counter_.fetch_add(msgs.size()); + return ConsumeStatus::CONSUME_SUCCESS; + } + +private: + std::atomic_long& counter_; +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + std::atomic_long counter(0); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MQMessageListener* listener = new CounterMessageListener(counter); + + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + push_consumer.subscribe("TestTopic", "*"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.setThrottle("TestTopic", 20); + push_consumer.start(); + + std::atomic_bool stopped(false); + std::thread report_thread([&counter, &stopped]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps; + while (true) { + qps = counter.load(std::memory_order_relaxed); + if (counter.compare_exchange_weak(qps, 0, std::memory_order_relaxed)) { + break; + } + } + std::cout << "QPS: " << qps << std::endl; + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(10)); + stopped.store(true); + + if (report_thread.joinable()) { + report_thread.join(); + } + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp new file mode 100644 index 000000000..d2eddd819 --- /dev/null +++ b/example/rocketmq/SqlConsumer.cpp @@ -0,0 +1,44 @@ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + std::lock_guard lk(console_mtx_); + for (const MQMessageExt& msg : msgs) { + std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Tag=" << msg.getTags() + << ", a=" << msg.getProperty("a") << ", Body=" << msg.getBody() << std::endl; + } + return ConsumeStatus::CONSUME_SUCCESS; + } + +private: + std::mutex console_mtx_; +}; + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQPushConsumer push_consumer("TestGroup"); + MQMessageListener* listener = new SampleMQMessageListener; + + push_consumer.setGroupName("TestGroup"); + push_consumer.setInstanceName("CID_sample_member_0"); + std::string sql_filter("(TAGS is not null and TAGS in ('TagA', 'TagB')) and (a is not null and a between 0 and 3)"); + push_consumer.subscribe("TestTopic", sql_filter, ExpressionType::SQL92); + // push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.registerMessageListener(listener); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/example/rocketmq/SqlProducer.cpp b/example/rocketmq/SqlProducer.cpp new file mode 100644 index 000000000..024b6de7b --- /dev/null +++ b/example/rocketmq/SqlProducer.cpp @@ -0,0 +1,44 @@ +#include "rocketmq/DefaultMQProducer.h" +#include + +using namespace rocketmq; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("PID_sample"); + producer.setNamesrvAddr("11.167.164.105:9876"); + + MQMessage message; + message.setTopic("TestTopic"); + try { + producer.start(); + for (int i = 0; i < 8; ++i) { + std::string body = std::to_string(i); + message.setBody(body); + message.setProperty("a", std::to_string(i % 5)); + switch (i % 3) { + case 0: + message.setTags("TagA"); + break; + case 1: + message.setTags("TagB"); + break; + case 2: + message.setTags("TagC"); + break; + } + SendResult sendResult = producer.send(message); + std::cout << "Message sent with msgId=" << sendResult.getMsgId() + << ", Queue=" << sendResult.getMessageQueue().simpleName() << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel new file mode 100644 index 000000000..b99691133 --- /dev/null +++ b/proto/BUILD.bazel @@ -0,0 +1,111 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library", "cpp_grpc_compile") + +proto_library( + name = "google_rpc_code", + srcs = [ + "google/rpc/code.proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "google_rpc_error_details", + srcs = [ + "google/rpc/error_details.proto", + ], + deps = [ + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:timestamp_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "google_rpc_status", + srcs = [ + "google/rpc/status.proto", + ], + deps = [ + "@com_google_protobuf//:any_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "apache_rocketmq_definition", + srcs = [ + "apache/rocketmq/v1/definition.proto", + ], + deps = [ + "@com_google_protobuf//:empty_proto", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:timestamp_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "apache_rocketmq_admin", + srcs = [ + "apache/rocketmq/v1/admin.proto", + ], + deps = [ + ":apache_rocketmq_definition", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:empty_proto", + ], + strip_import_prefix = "/proto", +) + +proto_library( + name = "apache_rocketmq_service", + srcs = [ + "apache/rocketmq/v1/service.proto", + ], + deps = [ + ":apache_rocketmq_definition", + ":google_rpc_code", + ":google_rpc_error_details", + ":google_rpc_status", + "@com_google_protobuf//:any_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:timestamp_proto", + ], + strip_import_prefix = "/proto", +) + +cc_proto_library( + name = "rocketmq_proto_cc", + deps = [ + ":apache_rocketmq_service", + ], + visibility = ["//visibility:public"] +) + +cpp_grpc_compile( + name = "rocketmq_grpc_compile", + protos = [ + ":google_rpc_code", + ":google_rpc_error_details", + ":google_rpc_status", + ":apache_rocketmq_definition", + ":apache_rocketmq_admin", + ":apache_rocketmq_service", + ], +) + +cpp_grpc_library( + name = "rocketmq_grpc_library", + protos = [ + ":google_rpc_code", + ":google_rpc_error_details", + ":google_rpc_status", + ":apache_rocketmq_definition", + ":apache_rocketmq_admin", + ":apache_rocketmq_service", + ], + visibility = ["//visibility:public"] +) \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v1/admin.proto new file mode 100644 index 000000000..4358c4bd8 --- /dev/null +++ b/proto/apache/rocketmq/v1/admin.proto @@ -0,0 +1,145 @@ +syntax = "proto3"; + +import "apache/rocketmq/v1/definition.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/empty.proto"; + +package apache.rocketmq.v1; + +option cc_enable_arenas = true; +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; + +// Ali Cloud Service +option java_outer_classname = "ACS"; + +message ChangeLogLevelRequest { + enum Level { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; + } + Level level = 1; +} + +message ChangeLogLevelResponse { + string remark = 1; +} + +// Request for the UpdateTopic method. +message UpdateTopicRequest { + // Instance identifier + string instance_id = 1; + + // Required. The updated topic object. + Resource topic = 2; + + // Required. Indicates which fields in the provided topic to update. Must be + // specified and non-empty. Note that if `update_mask` contains + // "message_storage_policy" but the `message_storage_policy` is not set in + // the `topic` provided above, then the updated value is determined by the + // policy configured at the project or organization level. + google.protobuf.FieldMask update_mask = 3; +} + +// Request for the `ListTopics` method. +message ListTopicsRequest { + + string instance_id = 1; + + // Maximum number of topics to return. + int32 page_size = 2; + + // The value returned by the last `ListTopicsResponse`; indicates that this is + // a continuation of a prior `ListTopics` call, and that the system should + // return the next page of data. + string page_token = 3; +} + +// Response for the `ListTopics` method. +message ListTopicsResponse { + // The resulting topics. + repeated Resource topics = 1; + + // If not empty, indicates that there may be more topics that match the + // request; this value should be passed in a new `ListTopicsRequest`. + string next_page_token = 2; +} + +// Request for the `DeleteTopic` method. +message DeleteTopicRequest { + // Instance identifier + string instance_id = 1; + + // Required. Name of the topic to delete. + string topic = 2; +} + +// Request for GetTopic method +message GetTopicRequest { + Resource topic = 1; +} + +message DeleteConsumerGroupRequest { + string instance_id = 1; + string group_id = 2; +} + +message UpdateConsumerGroupRequest { + ConsumerGroup consumer_group = 1; + google.protobuf.FieldMask update_mask = 2; +} + +// Request for the `ListConsumerGroups` method. +message ListConsumerGroupsRequest { + + string instance_id = 1; + + // Maximum number of topics to return. + int32 page_size = 2; + + // The value returned by the last `ListTopicsResponse`; indicates that this is + // a continuation of a prior `ListTopics` call, and that the system should + // return the next page of data. + string page_token = 3; +} + +// Response for the `ListConsumerGroups` method. +message ListConsumerGroupsResponse { + // The resulting consumer groups. + repeated Resource consumer_group = 1; + + // If not empty, indicates that there may be more topics that match the + // request; this value should be passed in a new `ListTopicsRequest`. + string next_page_token = 2; +} + +service Admin { + rpc ChangeLogLevel(ChangeLogLevelRequest) returns (ChangeLogLevelResponse) { + } + + rpc GetTopic(GetTopicRequest) returns (Resource) { + } + + rpc CreateTopic(Resource) returns (Resource) { + } + + rpc UpdateTopic(UpdateTopicRequest) returns (Resource) { + } + + rpc DeleteTopic(DeleteTopicRequest) returns (google.protobuf.Empty) { + } + + rpc CreateConsumerGroup(Resource) returns (Resource) { + } + + rpc DeleteConsumerGroup(DeleteConsumerGroupRequest) returns (google.protobuf.Empty) { + } + + rpc UpdateConsumerGroup(UpdateConsumerGroupRequest) returns (Resource) { + } +} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto new file mode 100644 index 000000000..b4879d01d --- /dev/null +++ b/proto/apache/rocketmq/v1/definition.proto @@ -0,0 +1,292 @@ +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +package apache.rocketmq.v1; + +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; + +// Ali Cloud Service +option java_outer_classname = "MQDomain"; + +enum Permission { + NONE = 0; + READ = 1; + WRITE = 2; + READ_WRITE = 3; +} + +enum FilterType { + TAG = 0; + SQL = 1; +} + +message FilterExpression { + FilterType type = 1; + string expression = 2; +} + +// Dead lettering is done on a best effort basis. The same message might be +// dead lettered multiple times. +// +// If validation on any of the fields fails at subscription creation/update, +// the create/update subscription request will fail. +message DeadLetterPolicy { + // The maximum number of delivery attempts for any message. The value must be + // between 5 and 100. + // + // This field will be honored on a best effort basis. + // + // If this parameter is 0, a default value of 5 is used. + int32 max_delivery_attempts = 1; +} + +message Resource { + // Abstract resource namespace + string arn = 1; + + // Resource name identifier, which remains unique within the abstract resource namespace. + string name = 2; +} + +enum ConsumeModel { + CLUSTERING = 0; + BROADCASTING = 1; +} + +message ProducerGroup { + Resource group = 1; +} + +enum ConsumePolicy { + RESUME = 0; + PLAYBACK = 1; + DISCARD = 2; + TARGET_TIMESTAMP = 3; +} + +message ConsumerGroup { + Resource group = 1; + + repeated SubscriptionEntry subscriptions = 2; + + ConsumeModel consume_model = 3; + + ConsumePolicy consume_policy = 4; + + DeadLetterPolicy dead_letter_policy = 5; + + ConsumeMessageType consume_type = 6; +} + +message SubscriptionEntry { + Resource topic = 1; + FilterExpression expression = 2; +} + +message HeartbeatEntry { + string client_id = 1; + oneof client_data { + ProducerGroup producer_group = 2; + ConsumerGroup consumer_group = 3; + } +} + +enum AddressScheme { + IPv4 = 0; + IPv6 = 1; + DOMAIN_NAME = 2; +} + +message Address { + string host = 1; + int32 port = 2; +} + +message Endpoints { + AddressScheme scheme = 1; + repeated Address addresses = 2; +} + +message Broker { + // Name of the broker + string name = 1; + + // Broker index. Canonically, index = 0 implies that the broker is playing leader role while brokers with index > 0 + // play follower role. + int32 id = 2; + + // Address of the broker, complying with the following scheme + // 1. dns:[//authority/]host[:port] + // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses + // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses + Endpoints endpoints = 3; +} + +message Partition { + Resource topic = 1; + int32 id = 2; + Permission permission = 3; + Broker broker = 4; +} + +// Language of the client +enum Language { + JAVA = 0; + CPP = 1; + C_SHARP = 2; + PYTHON = 3; + DELPHI = 4; + ERLANG = 5; + RUBY = 6; + GO = 7; + PHP = 8; + RUST = 9; + C = 10; + PERL = 11; + OBJECTIVE_C = 12; + DART = 13; + KOTLIN = 14; + NODE_JS = 15; +} + +enum MessageType { + NORMAL = 0; + + // Sequenced message + FIFO = 1; + + // Messages that are delivered after the specified duration. + DELAY = 2; + + // Messages that are transactional. Only committed messages are delivered to subscribers. + TRANSACTION = 3; +} + +enum DigestType { + // CRC algorithm achieves goal of detecting random data error with lowest computation overhead. + CRC32 = 0; + + // MD5 algorithm achieves good balance between collision rate and computation overhead. + MD5 = 1; + + // SHA-family has substantially fewer collision with fair amount of computation. + SHA1 = 2; +} + +message Digest { + DigestType type = 1; + string checksum = 2; +} + +enum Encoding { + IDENTITY = 0; + GZIP = 1; + SNAPPY = 2; +} + +message SystemAttribute { + // Tag + string tag = 1; + + // Message keys + repeated string keys = 2; + + // Message identifier, client-side generated, remains unique. + string message_id = 3; + + // Message body digest + Digest body_digest = 4; + + // Message body encoding. Candidate options are identity, gzip, snappy etc. + Encoding body_encoding = 5; + + // Message type, normal, FIFO or transactional. + MessageType message_type = 6; + + // Message born time-point. + google.protobuf.Timestamp born_timestamp = 7; + + // Message born host. Valid options are IPv4, IPv6 or client host domain name. + string born_host = 8; + + // Time-point at which the message is stored in the broker. + google.protobuf.Timestamp store_timestamp = 9; + + // The broker that stores this message. It may be name, IP or arbitrary identifier that uniquely identify the broker. + string store_host = 10; + + oneof timed_delivery { + // Time-point at which broker delivers to clients. + google.protobuf.Timestamp delivery_timestamp = 11; + + // Level-based delay strategy. + int32 delay_level = 12; + } + + // If a message is acquired by way of POP, this field holds the receipt. Clients use the receipt to + // acknowledge or negatively acknowledge the message. + string receipt_handle = 13; + + // Partition identifier in which a message is physically stored. + int32 partition_id = 14; + + // Partition offset at which a message is stored. + int64 partition_offset = 15; + + // Period of time servers would remain invisible once a message is acquired. + google.protobuf.Duration invisible_period = 16; + + // Business code may failed to process messages for the moment. Hence, clients may request servers to deliver them + // again using certain back-off strategy. + int32 delivery_count = 17; + + // Message producer load-balance group if applicable. + Resource producer_group = 18; + + string message_group = 19; + + // Trace context. + string trace_context = 20; + + // Correlated transaction + string transaction_id = 21; +} + +message Message { + Resource topic = 1; + // User defined key-value pairs. + map user_attribute = 2; + SystemAttribute system_attribute = 3; + bytes body = 4; +} + +enum ConsumeMessageType { + // Partition-based pull. Client is responsible to first seek to a position in the partition and then + // initiate a sequence of pull request to fetch messages within given range. + PULL = 0; + + // Offsets are implicitly managed by brokers. Clients generally perform stateless pop requests from + // logical queues pertaining to the specified topic. + POP = 1; +} + +message Assignment { + Partition Partition = 1; + ConsumeMessageType mode = 2; +} + +enum QueryOffsetPolicy { + // Use this option if client wishes to playback all existing messages. + BEGINNING = 0; + + // Use this option if client wishes to skip all existing messages. + END = 1; + + // Use this option if time-based seek is targeted. + TIME_POINT = 2; +} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto new file mode 100644 index 000000000..6e079015d --- /dev/null +++ b/proto/apache/rocketmq/v1/service.proto @@ -0,0 +1,266 @@ +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/error_details.proto"; +import "google/rpc/code.proto"; +import "google/rpc/status.proto"; + +import "apache/rocketmq/v1/definition.proto"; + +package apache.rocketmq.v1; + +option java_multiple_files = true; +option java_package = "apache.rocketmq.v1"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; + +// Ali Cloud Service +option java_outer_classname = "MQService"; + +message ResponseCommon { + google.rpc.Status status = 1; + google.rpc.RequestInfo request_info = 2; + google.rpc.Help help = 3; + google.rpc.RetryInfo retry_info = 4; + google.rpc.DebugInfo debug_info = 5; + google.rpc.ErrorInfo error_info = 6; +} + +message QueryRouteRequest { + Resource topic = 1; +} + +message QueryRouteResponse { + ResponseCommon common = 1; + repeated Partition partitions = 2; +} + +message SendMessageRequest { + Message message = 1; +} + +message SendMessageResponse { + ResponseCommon common = 1; + string message_id = 2; + string transaction_id = 3; +} + +message QueryAssignmentRequest { + Resource topic = 1; + Resource group = 2; + string client_id = 3; +} + +message QueryAssignmentResponse { + ResponseCommon common = 1; + repeated Assignment assignments = 2; +} + +message ReceiveMessageRequest { + Resource group = 1; + string client_id = 2; + Partition partition = 3; + FilterExpression filter_expression = 4; + ConsumePolicy consume_policy = 5; + google.protobuf.Timestamp initialization_timestamp = 6; + int32 batch_size = 7; + google.protobuf.Duration invisible_duration = 8; + google.protobuf.Duration await_time = 9; +} + +message ReceiveMessageResponse { + ResponseCommon common = 1; + repeated Message messages = 2; + google.protobuf.Timestamp delivery_timestamp = 3; + google.protobuf.Duration invisible_duration = 4; +} + +message AckMessageRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + oneof handle { + string receipt_handle = 4; + int64 offset = 5; + } + string message_id = 6; +} + +message AckMessageResponse { + ResponseCommon common = 1; +} + +message NackMessageRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + string receipt_handle = 4; + string message_id = 5; + int32 reconsume_times = 6; + int32 max_reconsume_times = 7; + ConsumeModel consume_model = 8; +} + +message NackMessageResponse { + ResponseCommon common = 1; +} + +message HeartbeatRequest { + repeated HeartbeatEntry heartbeats = 1; +} + +message HeartbeatResponse { + ResponseCommon common = 1; +} + +message HealthCheckRequest { + string client_host = 1; +} + +message HealthCheckResponse { + ResponseCommon common = 1; +} + +message EndTransactionRequest { + Resource producer_group = 1; + string message_id = 2; + string transaction_id = 3; + enum TransactionResolution { + COMMIT = 0; + ROLLBACK = 1; + } + TransactionResolution resolution = 4; + enum Source { + CLIENT = 0; + SERVER_CHECK = 1; + } + Source source = 5; + string trace_context = 6; +} + +message EndTransactionResponse { + ResponseCommon common = 1; +} + +message QueryOffsetRequest { + Partition partition = 1; + QueryOffsetPolicy policy = 2; + google.protobuf.Timestamp time_point = 3; +} + +message QueryOffsetResponse { + ResponseCommon common = 1; + int64 offset = 2; +} + +message PullMessageRequest { + Resource group = 1; + Partition partition = 2; + int64 offset = 3; + int32 batch_size = 4; + google.protobuf.Duration await_time = 5; + FilterExpression filter_expression = 6; + string client_id = 7; +} + +message PullMessageResponse { + ResponseCommon common = 1; + int64 min_offset = 2; + int64 next_offset = 3; + int64 max_offset = 4; + repeated Message messages = 5; +} + +message ClientResourceBundle { + string client_id = 1; + repeated Resource topics = 2; + oneof group { + Resource producer_group = 3; + Resource consumer_group = 4; + } +} + +message GenericPollingRequest { + repeated ClientResourceBundle bundles = 1; +} + +message GenericPollingResponse { + ResponseCommon common = 1; +} + +message PrintThreadStackRequest { +} + +message PrintThreadStackResponse { + ResponseCommon common = 1; + string stack_trace = 2; +} + +message VerifyMessageConsumptionRequest { + Message message = 1; +} + +message VerifyMessageConsumptionResponse { + ResponseCommon common = 1; +} + +message ResolveOrphanedTransactionRequest { + repeated Message orphaned_transactional_messages = 1; +} + +message MultiplexingRequest { + oneof type { + GenericPollingRequest polling_request = 1; + PrintThreadStackResponse print_thread_stack_response = 2; + VerifyMessageConsumptionResponse verify_message_consumption_response = 3; + } +} + +message MultiplexingResponse { + oneof type { + GenericPollingResponse polling_response = 1; + PrintThreadStackRequest print_thread_stack_request = 2; + VerifyMessageConsumptionRequest verify_message_consumption_request = 3; + ResolveOrphanedTransactionRequest resolve_orphaned_transaction_request = 4; + } +} + +service MessagingService { + rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { + } + + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { + } + + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { + } + + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { + } + + rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { + } + + rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { + } + + rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { + } + + rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { + } + + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { + } + + rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { + } + + rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { + } + + rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) { + } +} \ No newline at end of file diff --git a/proto/google/protobuf/any.proto b/proto/google/protobuf/any.proto new file mode 100644 index 000000000..6ed8a23cf --- /dev/null +++ b/proto/google/protobuf/any.proto @@ -0,0 +1,158 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/proto/google/protobuf/duration.proto b/proto/google/protobuf/duration.proto new file mode 100644 index 000000000..81c3e369f --- /dev/null +++ b/proto/google/protobuf/duration.proto @@ -0,0 +1,116 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/durationpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (duration.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +// +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/proto/google/protobuf/empty.proto b/proto/google/protobuf/empty.proto new file mode 100644 index 000000000..5f992de94 --- /dev/null +++ b/proto/google/protobuf/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/proto/google/protobuf/field_mask.proto b/proto/google/protobuf/field_mask.proto new file mode 100644 index 000000000..6b5104f18 --- /dev/null +++ b/proto/google/protobuf/field_mask.proto @@ -0,0 +1,245 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "FieldMaskProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb"; +option cc_enable_arenas = true; + +// `FieldMask` represents a set of symbolic field paths, for example: +// +// paths: "f.a" +// paths: "f.b.d" +// +// Here `f` represents a field in some root message, `a` and `b` +// fields in the message found in `f`, and `d` a field found in the +// message in `f.b`. +// +// Field masks are used to specify a subset of fields that should be +// returned by a get operation or modified by an update operation. +// Field masks also have a custom JSON encoding (see below). +// +// # Field Masks in Projections +// +// When used in the context of a projection, a response message or +// sub-message is filtered by the API to only contain those fields as +// specified in the mask. For example, if the mask in the previous +// example is applied to a response message as follows: +// +// f { +// a : 22 +// b { +// d : 1 +// x : 2 +// } +// y : 13 +// } +// z: 8 +// +// The result will not contain specific values for fields x,y and z +// (their value will be set to the default, and omitted in proto text +// output): +// +// +// f { +// a : 22 +// b { +// d : 1 +// } +// } +// +// A repeated field is not allowed except at the last position of a +// paths string. +// +// If a FieldMask object is not present in a get operation, the +// operation applies to all fields (as if a FieldMask of all fields +// had been specified). +// +// Note that a field mask does not necessarily apply to the +// top-level response message. In case of a REST get operation, the +// field mask applies directly to the response, but in case of a REST +// list operation, the mask instead applies to each individual message +// in the returned resource list. In case of a REST custom method, +// other definitions may be used. Where the mask applies will be +// clearly documented together with its declaration in the API. In +// any case, the effect on the returned resource/resources is required +// behavior for APIs. +// +// # Field Masks in Update Operations +// +// A field mask in update operations specifies which fields of the +// targeted resource are going to be updated. The API is required +// to only change the values of the fields as specified in the mask +// and leave the others untouched. If a resource is passed in to +// describe the updated values, the API ignores the values of all +// fields not covered by the mask. +// +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. +// +// If a sub-message is specified in the last position of the field mask for an +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: +// +// f { +// b { +// d: 1 +// x: 2 +// } +// c: [1] +// } +// +// And an update message: +// +// f { +// b { +// d: 10 +// } +// c: [2] +// } +// +// then if the field mask is: +// +// paths: ["f.b", "f.c"] +// +// then the result will be: +// +// f { +// b { +// d: 10 +// x: 2 +// } +// c: [1, 2] +// } +// +// An implementation may provide options to override this default behavior for +// repeated and message fields. +// +// In order to reset a field's value to the default, the field must +// be in the mask and set to the default value in the provided resource. +// Hence, in order to reset all fields of a resource, provide a default +// instance of the resource and set all fields in the mask, or do +// not provide a mask as described below. +// +// If a field mask is not present on update, the operation applies to +// all fields (as if a field mask of all fields has been specified). +// Note that in the presence of schema evolution, this may mean that +// fields the client does not know and has therefore not filled into +// the request will be reset to their default. If this is unwanted +// behavior, a specific service may require a client to always specify +// a field mask, producing an error if not. +// +// As with get operations, the location of the resource which +// describes the updated values in the request message depends on the +// operation kind. In any case, the effect of the field mask is +// required to be honored by the API. +// +// ## Considerations for HTTP REST +// +// The HTTP kind of an update operation which uses a field mask must +// be set to PATCH instead of PUT in order to satisfy HTTP semantics +// (PUT must only be used for full updates). +// +// # JSON Encoding of Field Masks +// +// In JSON, a field mask is encoded as a single string where paths are +// separated by a comma. Fields name in each path are converted +// to/from lower-camel naming conventions. +// +// As an example, consider the following message declarations: +// +// message Profile { +// User user = 1; +// Photo photo = 2; +// } +// message User { +// string display_name = 1; +// string address = 2; +// } +// +// In proto a field mask for `Profile` may look as such: +// +// mask { +// paths: "user.display_name" +// paths: "photo" +// } +// +// In JSON, the same mask is represented as below: +// +// { +// mask: "user.displayName,photo" +// } +// +// # Field Masks and Oneof Fields +// +// Field masks treat fields in oneofs just as regular fields. Consider the +// following message: +// +// message SampleMessage { +// oneof test_oneof { +// string name = 4; +// SubMessage sub_message = 9; +// } +// } +// +// The field mask can be: +// +// mask { +// paths: "name" +// } +// +// Or: +// +// mask { +// paths: "sub_message" +// } +// +// Note that oneof type names ("test_oneof" in this case) cannot be used in +// paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is unmappable. +message FieldMask { + // The set of field mask paths. + repeated string paths = 1; +} diff --git a/proto/google/protobuf/timestamp.proto b/proto/google/protobuf/timestamp.proto new file mode 100644 index 000000000..3b2df6d91 --- /dev/null +++ b/proto/google/protobuf/timestamp.proto @@ -0,0 +1,147 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. +// +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/proto/google/rpc/code.proto b/proto/google/rpc/code.proto new file mode 100644 index 000000000..98ae0ac18 --- /dev/null +++ b/proto/google/rpc/code.proto @@ -0,0 +1,186 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; +option java_multiple_files = true; +option java_outer_classname = "CodeProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The canonical error codes for gRPC APIs. +// +// +// Sometimes multiple error codes may apply. Services should return +// the most specific error code that applies. For example, prefer +// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. +// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. +enum Code { + // Not an error; returned on success + // + // HTTP Mapping: 200 OK + OK = 0; + + // The operation was cancelled, typically by the caller. + // + // HTTP Mapping: 499 Client Closed Request + CANCELLED = 1; + + // Unknown error. For example, this error may be returned when + // a `Status` value received from another address space belongs to + // an error space that is not known in this address space. Also + // errors raised by APIs that do not return enough error information + // may be converted to this error. + // + // HTTP Mapping: 500 Internal Server Error + UNKNOWN = 2; + + // The client specified an invalid argument. Note that this differs + // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + // + // HTTP Mapping: 400 Bad Request + INVALID_ARGUMENT = 3; + + // The deadline expired before the operation could complete. For operations + // that change the state of the system, this error may be returned + // even if the operation has completed successfully. For example, a + // successful response from a server could have been delayed long + // enough for the deadline to expire. + // + // HTTP Mapping: 504 Gateway Timeout + DEADLINE_EXCEEDED = 4; + + // Some requested entity (e.g., file or directory) was not found. + // + // Note to server developers: if a request is denied for an entire class + // of users, such as gradual feature rollout or undocumented whitelist, + // `NOT_FOUND` may be used. If a request is denied for some users within + // a class of users, such as user-based access control, `PERMISSION_DENIED` + // must be used. + // + // HTTP Mapping: 404 Not Found + NOT_FOUND = 5; + + // The entity that a client attempted to create (e.g., file or directory) + // already exists. + // + // HTTP Mapping: 409 Conflict + ALREADY_EXISTS = 6; + + // The caller does not have permission to execute the specified + // operation. `PERMISSION_DENIED` must not be used for rejections + // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` + // instead for those errors). `PERMISSION_DENIED` must not be + // used if the caller can not be identified (use `UNAUTHENTICATED` + // instead for those errors). This error code does not imply the + // request is valid or the requested entity exists or satisfies + // other pre-conditions. + // + // HTTP Mapping: 403 Forbidden + PERMISSION_DENIED = 7; + + // The request does not have valid authentication credentials for the + // operation. + // + // HTTP Mapping: 401 Unauthorized + UNAUTHENTICATED = 16; + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + // + // HTTP Mapping: 429 Too Many Requests + RESOURCE_EXHAUSTED = 8; + + // The operation was rejected because the system is not in a state + // required for the operation's execution. For example, the directory + // to be deleted is non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // Service implementors can use the following guidelines to decide + // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: + // (a) Use `UNAVAILABLE` if the client can retry just the failing call. + // (b) Use `ABORTED` if the client should retry at a higher level + // (e.g., when a client-specified test-and-set fails, indicating the + // client should restart a read-modify-write sequence). + // (c) Use `FAILED_PRECONDITION` if the client should not retry until + // the system state has been explicitly fixed. E.g., if an "rmdir" + // fails because the directory is non-empty, `FAILED_PRECONDITION` + // should be returned since the client should not retry unless + // the files are deleted from the directory. + // + // HTTP Mapping: 400 Bad Request + FAILED_PRECONDITION = 9; + + // The operation was aborted, typically due to a concurrency issue such as + // a sequencer check failure or transaction abort. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 409 Conflict + ABORTED = 10; + + // The operation was attempted past the valid range. E.g., seeking or + // reading past end-of-file. + // + // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `INVALID_ARGUMENT` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `OUT_OF_RANGE` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `FAILED_PRECONDITION` and + // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `OUT_OF_RANGE` error to detect when + // they are done. + // + // HTTP Mapping: 400 Bad Request + OUT_OF_RANGE = 11; + + // The operation is not implemented or is not supported/enabled in this + // service. + // + // HTTP Mapping: 501 Not Implemented + UNIMPLEMENTED = 12; + + // Internal errors. This means that some invariants expected by the + // underlying system have been broken. This error code is reserved + // for serious errors. + // + // HTTP Mapping: 500 Internal Server Error + INTERNAL = 13; + + // The service is currently unavailable. This is most likely a + // transient condition, which can be corrected by retrying with + // a backoff. Note that it is not always safe to retry + // non-idempotent operations. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 503 Service Unavailable + UNAVAILABLE = 14; + + // Unrecoverable data loss or corruption. + // + // HTTP Mapping: 500 Internal Server Error + DATA_LOSS = 15; +} diff --git a/proto/google/rpc/error_details.proto b/proto/google/rpc/error_details.proto new file mode 100644 index 000000000..c4d6c4b78 --- /dev/null +++ b/proto/google/rpc/error_details.proto @@ -0,0 +1,249 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; +option java_multiple_files = true; +option java_outer_classname = "ErrorDetailsProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Describes when the clients can retry a failed request. Clients could ignore +// the recommendation here or retry when this information is missing from error +// responses. +// +// It's always recommended that clients should use exponential backoff when +// retrying. +// +// Clients should wait until `retry_delay` amount of time has passed since +// receiving the error response before retrying. If retrying requests also +// fail, clients should use an exponential backoff scheme to gradually increase +// the delay between retries based on `retry_delay`, until either a maximum +// number of retries have been reached or a maximum retry delay cap has been +// reached. +message RetryInfo { + // Clients should wait at least this long between retrying the same request. + google.protobuf.Duration retry_delay = 1; +} + +// Describes additional debugging info. +message DebugInfo { + // The stack trace entries indicating where the error occurred. + repeated string stack_entries = 1; + + // Additional debugging information provided by the server. + string detail = 2; +} + +// Describes how a quota check failed. +// +// For example if a daily limit was exceeded for the calling project, +// a service could respond with a QuotaFailure detail containing the project +// id and the description of the quota limit that was exceeded. If the +// calling project hasn't enabled the service in the developer console, then +// a service could respond with the project id and set `service_disabled` +// to true. +// +// Also see RetryInfo and Help types for other details about handling a +// quota failure. +message QuotaFailure { + // A message type used to describe a single quota violation. For example, a + // daily quota or a custom quota that was exceeded. + message Violation { + // The subject on which the quota check failed. + // For example, "clientip:" or "project:". + string subject = 1; + + // A description of how the quota check failed. Clients can use this + // description to find more about the quota configuration in the service's + // public documentation, or find the relevant quota limit to adjust through + // developer console. + // + // For example: "Service disabled" or "Daily Limit for read operations + // exceeded". + string description = 2; + } + + // Describes all quota violations. + repeated Violation violations = 1; +} + +// Describes the cause of the error with structured details. +// +// Example of an error when contacting the "pubsub.googleapis.com" API when it +// is not enabled: +// +// { "reason": "API_DISABLED" +// "domain": "googleapis.com" +// "metadata": { +// "resource": "projects/123", +// "service": "pubsub.googleapis.com" +// } +// } +// +// This response indicates that the pubsub.googleapis.com API is not enabled. +// +// Example of an error that is returned when attempting to create a Spanner +// instance in a region that is out of stock: +// +// { "reason": "STOCKOUT" +// "domain": "spanner.googleapis.com", +// "metadata": { +// "availableRegions": "us-central1,us-east2" +// } +// } +message ErrorInfo { + // The reason of the error. This is a constant value that identifies the + // proximate cause of the error. Error reasons are unique within a particular + // domain of errors. This should be at most 63 characters and match + // /[A-Z0-9_]+/. + string reason = 1; + + // The logical grouping to which the "reason" belongs. The error domain + // is typically the registered service name of the tool or product that + // generates the error. Example: "pubsub.googleapis.com". If the error is + // generated by some common infrastructure, the error domain must be a + // globally unique value that identifies the infrastructure. For Google API + // infrastructure, the error domain is "googleapis.com". + string domain = 2; + + // Additional structured details about this error. + // + // Keys should match /[a-zA-Z0-9-_]/ and be limited to 64 characters in + // length. When identifying the current value of an exceeded limit, the units + // should be contained in the key, not the value. For example, rather than + // {"instanceLimit": "100/request"}, should be returned as, + // {"instanceLimitPerRequest": "100"}, if the client exceeds the number of + // instances that can be created in a single (batch) request. + map metadata = 3; +} + +// Describes what preconditions have failed. +// +// For example, if an RPC failed because it required the Terms of Service to be +// acknowledged, it could list the terms of service violation in the +// PreconditionFailure message. +message PreconditionFailure { + // A message type used to describe a single precondition failure. + message Violation { + // The type of PreconditionFailure. We recommend using a service-specific + // enum type to define the supported precondition violation subjects. For + // example, "TOS" for "Terms of Service violation". + string type = 1; + + // The subject, relative to the type, that failed. + // For example, "google.com/cloud" relative to the "TOS" type would indicate + // which terms of service is being referenced. + string subject = 2; + + // A description of how the precondition failed. Developers can use this + // description to understand how to fix the failure. + // + // For example: "Terms of service not accepted". + string description = 3; + } + + // Describes all precondition violations. + repeated Violation violations = 1; +} + +// Describes violations in a client request. This error type focuses on the +// syntactic aspects of the request. +message BadRequest { + // A message type used to describe a single bad request field. + message FieldViolation { + // A path leading to a field in the request body. The value will be a + // sequence of dot-separated identifiers that identify a protocol buffer + // field. E.g., "field_violations.field" would identify this field. + string field = 1; + + // A description of why the request element is bad. + string description = 2; + } + + // Describes all violations in a client request. + repeated FieldViolation field_violations = 1; +} + +// Contains metadata about the request that clients can attach when filing a bug +// or providing other forms of feedback. +message RequestInfo { + // An opaque string that should only be interpreted by the service generating + // it. For example, it can be used to identify requests in the service's logs. + string request_id = 1; + + // Any data that was used to serve this request. For example, an encrypted + // stack trace that can be sent back to the service provider for debugging. + string serving_data = 2; +} + +// Describes the resource that is being accessed. +message ResourceInfo { + // A name for the type of resource being accessed, e.g. "sql table", + // "cloud storage bucket", "file", "Google calendar"; or the type URL + // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". + string resource_type = 1; + + // The name of the resource being accessed. For example, a shared calendar + // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current + // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + string resource_name = 2; + + // The owner of the resource (optional). + // For example, "user:" or "project:". + string owner = 3; + + // Describes what error is encountered when accessing this resource. + // For example, updating a cloud project may require the `writer` permission + // on the developer console project. + string description = 4; +} + +// Provides links to documentation or for performing an out of band action. +// +// For example, if a quota check failed with an error indicating the calling +// project hasn't enabled the accessed service, this can contain a URL pointing +// directly to the right place in the developer console to flip the bit. +message Help { + // Describes a URL link. + message Link { + // Describes what the link offers. + string description = 1; + + // The URL of the link. + string url = 2; + } + + // URL(s) pointing to additional information on handling the current error. + repeated Link links = 1; +} + +// Provides a localized error message that is safe to return to the user +// which can be attached to an RPC error. +message LocalizedMessage { + // The locale used following the specification defined at + // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. + // Examples are: "en-US", "fr-CH", "es-MX" + string locale = 1; + + // The localized error message in the above locale. + string message = 2; +} diff --git a/proto/google/rpc/status.proto b/proto/google/rpc/status.proto new file mode 100644 index 000000000..3b1f7a932 --- /dev/null +++ b/proto/google/rpc/status.proto @@ -0,0 +1,47 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; +option java_multiple_files = true; +option java_outer_classname = "StatusProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). Each `Status` message contains +// three pieces of data: error code, error message, and error details. +// +// You can find out more about this error model and how to work with it in the +// [API Design Guide](https://cloud.google.com/apis/design/errors). +message Status { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + int32 code = 1; + + // A developer-facing error message, which should be in English. Any + // user-facing error message should be localized and sent in the + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + string message = 2; + + // A list of messages that carry the error details. There is a common set of + // message types for APIs to use. + repeated google.protobuf.Any details = 3; +} diff --git a/rpm/rocketmq-cpp.sh b/rpm/rocketmq-cpp.sh new file mode 100644 index 000000000..41516e14b --- /dev/null +++ b/rpm/rocketmq-cpp.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +export CC=/usr/local/gcc-4.9.2/bin/gcc +export CXX=/usr/local/gcc-4.9.2/bin/g++ +export LD_LIBRARY_PATH=/usr/local/gcc-4.9.2/lib64/ +export http_proxy=http://30.57.177.111:3128 +export https_proxy=http://30.57.177.111:3128 \ No newline at end of file diff --git a/rpm/rocketmq-cpp.spec b/rpm/rocketmq-cpp.spec new file mode 100644 index 000000000..5c4721042 --- /dev/null +++ b/rpm/rocketmq-cpp.spec @@ -0,0 +1,75 @@ +Name: rocketmq-cpp +Version: 1.0.0 +Release: %(echo $RELEASE)%{?dist} +Summary: Pop-based RocketMQ C++ SDK +Group: alibaba/application +License: Commercial +%define _prefix /home/admin/rocketmq-cpp + +# uncomment below, if your building depend on other packages. + +BuildRequires: metaq-cmake = 3.18.3 +BuildRequires: gcc49 = 4.9.2 + +# uncomment below, if depend on other packages + +#Requires: package_name = 1.0.0 + +%description +#descript your rpm package here +This SDK implements pop-based message consumption and acknowledgement. Aka, SDK leaves most of the complexities to +brokers to enjoy client simplicity and robustness. + +#%%debug_package +# support debuginfo package, to reduce runtime package size + +# prepare your files +%install +# OLDPWD is the dir of rpm_create running +# _prefix is an inner var of rpmbuild, +# can set by rpm_create, default is "/home/a" +# _lib is an inner var, maybe "lib" or "lib64" depend on OS + +export CC=/usr/local/gcc-4.9.2/bin/gcc +export CXX=/usr/local/gcc-4.9.2/bin/g++ +export http_proxy=http://30.57.177.111:3128 +export https_proxy=http://30.57.177.111:3128 +export ALL_PROXY=30.57.177.111:3128 +export LD_LIBRARY_PATH=/usr/local/gcc-4.9.2/lib64:${LD_LIBRARY_PATH} +# Build +cd $OLDPWD/../; /usr/local/bin/cmake -DCMAKE_INSTALL_PREFIX=${RPM_BUILD_ROOT}/%{_prefix} .; make -j $(nproc); make install + +# create a crontab of the package +#echo " +#* * * * * root /home/a/bin/every_min +#3 * * * * ads /home/a/bin/every_hour +#" > %{_crontab} + +# package infomation +%files +# set file attribute here +%defattr(-,root,root) +# need not list every file here, keep it as this +%{_prefix} +## create an empy dir + +# %dir %{_prefix}/var/log + +## need bakup old config file, so indicate here + +# %config %{_prefix}/etc/sample.conf + +## or need keep old config file, so indicate with "noreplace" + +# %config(noreplace) %{_prefix}/etc/sample.conf + +## indicate the dir for crontab + +#%attr(644,root,root) %{_crondir}/* + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%changelog +* Mon Aug 10 2020 Release version 1.0.0 +- add spec of %name diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..eef284298 --- /dev/null +++ b/src/main/cpp/CMakeLists.txt @@ -0,0 +1,74 @@ +add_subdirectory(base) +add_subdirectory(log) +add_subdirectory(admin) +add_subdirectory(concurrent) +add_subdirectory(scheduler) +add_subdirectory(client) +add_subdirectory(rocketmq) + +add_library(rocketmq_static + STATIC + $ + $ + $ + $ + $ + $ + $) +target_link_libraries(rocketmq_static + PUBLIC + absl::flat_hash_set + absl::flat_hash_map + absl::random_random + absl::synchronization + absl::strings + CURL::libcurl + fmt + mq_proto_library + re2::re2 + rocketmq_interface + uv_a + zlib) +if (ENABLE_TRACING) + target_link_libraries(rocketmq_static + PUBLIC + opentelemetry_trace + opentelemetry_exporter_otprotocol) +endif () + +add_library(rocketmq_shared + SHARED + $ + $ + $ + $ + $ + $ + $) +target_link_libraries(rocketmq_shared + PUBLIC + absl::flat_hash_set + absl::flat_hash_map + absl::random_random + absl::synchronization + absl::strings + CURL::libcurl + fmt + mq_proto_library + re2::re2 + rocketmq_interface + uv_a + zlib) +if (ENABLE_TRACING) + target_link_libraries(rocketmq_shared + PUBLIC + opentelemetry_trace + opentelemetry_exporter_otprotocol) +endif () + +set_target_properties(rocketmq_static rocketmq_shared + PROPERTIES + ARCHIVE_OUTPUT_NAME rocketmq + LIBRARY_OUTPUT_NAME rocketmq) + +add_library(rocketmq ALIAS rocketmq_static) \ No newline at end of file diff --git a/src/main/cpp/admin/AdminClient.cpp b/src/main/cpp/admin/AdminClient.cpp new file mode 100644 index 000000000..9c4d4b712 --- /dev/null +++ b/src/main/cpp/admin/AdminClient.cpp @@ -0,0 +1,95 @@ +#include "AdminClient.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { + +Status AdminClient::changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response) { + ClientContext context; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); + context.set_deadline(deadline); + Status status = stub_->ChangeLogLevel(&context, request, &response); + return status; +} + +} // namespace admin + +ROCKETMQ_NAMESPACE_END + +void wrapChangeLogLevelRequest(rmq::ChangeLogLevelRequest& request, const std::string& level) { + if (level.empty()) { + return; + } + + if ("trace" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_TRACE); + return; + } + + if ("debug" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_DEBUG); + return; + } + + if ("info" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_INFO); + return; + } + + if ("warn" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_WARN); + return; + } + + if ("error" == level) { + request.set_level(rmq::ChangeLogLevelRequest_Level_ERROR); + return; + } +} + +int main(int argc, char* argv[]) { + if (argc <= 1) { + std::cerr << "Usage: 'admin_client level [port]' where level is among trace/debug/info/warn/error.\n"; + return EXIT_SUCCESS; + } + + int port = 9877; + + char* env_port = getenv("ROCKETMQ_ADMIN_PORT"); + if (nullptr != env_port) { + try { + port = std::stoi(env_port); + } catch (...) { + std::cerr << "Failed to parse port from environment variable ROCKETMQ_ADMIN_PORT" << env_port << "\n"; + } + } + + if (argc > 2) { + try { + port = std::stoi(argv[2]); + } catch (...) { + std::cerr << "Failed to parse port from command line:" << argv[2] << "\n"; + } + } + + std::string target("127.0.0.1:"); + target.append(std::to_string(port)); + + std::cout << "Target address: " << target << "\n"; + auto channel = grpc::CreateChannel(target, grpc::InsecureChannelCredentials()); + rocketmq::admin::AdminClient client(channel); + + rmq::ChangeLogLevelRequest request; + wrapChangeLogLevelRequest(request, argv[1]); + rmq::ChangeLogLevelResponse response; + auto status = client.changeLogLevel(request, response); + if (status.ok()) { + std::cout << "Log level changed OK" << std::endl; + } else { + std::cerr << "Failed to change log level. GRPC error code: " << status.error_code() + << ", GRPC error message: " << status.error_message() << "\n"; + std::cerr << "Server remark: " << response.remark() << std::endl; + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/main/cpp/admin/AdminFacade.cpp b/src/main/cpp/admin/AdminFacade.cpp new file mode 100644 index 000000000..314b59b73 --- /dev/null +++ b/src/main/cpp/admin/AdminFacade.cpp @@ -0,0 +1,8 @@ +#include "AdminServerImpl.h" + +using namespace ROCKETMQ_NAMESPACE::admin; + +AdminServer& AdminFacade::getServer() { + static AdminServerImpl admin_server; + return admin_server; +} \ No newline at end of file diff --git a/src/main/cpp/admin/AdminServerImpl.cpp b/src/main/cpp/admin/AdminServerImpl.cpp new file mode 100644 index 000000000..d77b4fb01 --- /dev/null +++ b/src/main/cpp/admin/AdminServerImpl.cpp @@ -0,0 +1,102 @@ +#include "AdminServerImpl.h" + +#include "AdminServiceImpl.h" +#include "ServerCall.h" +#include "spdlog/spdlog.h" +#include + +using grpc::ServerBuilder; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +bool AdminServerImpl::start() { + if (State::CREATED != state_) { + return false; + } + State current = State::CREATED; + if (state_.compare_exchange_weak(current, State::STARTING, std::memory_order_relaxed)) { + std::string server_address("127.0.0.1:"); + server_address.append(std::to_string(port_)); + + grpc::EnableDefaultHealthCheckService(true); + + ServerBuilder server_builder; + server_builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), &port_); + server_builder.RegisterService(async_stub_.get()); + // A gRPC server may have multiple per-thread CompletionQueue + completion_queue_ = server_builder.AddCompletionQueue(); + server_ = server_builder.BuildAndStart(); + { + loop(); + std::unique_lock lk(loop_mtx_); + loop_cv_.wait(lk, [this]() { return State::STARTED == state_.load(); }); + } + SPDLOG_INFO("Admin server started and listening 127.0.0.1:{}", port_); + return true; + } + return false; +} + +void AdminServerImpl::loop() { + auto loop_lambda = [this] { + state_.store(State::STARTED); + { + std::unique_lock lk(loop_mtx_); + loop_cv_.notify_all(); + } + + SPDLOG_DEBUG("Prepare initial ServerCall"); + new ServerCall(async_stub_.get(), service_.get(), completion_queue_.get()); + void* tag; + bool ok; + + while (completion_queue_->Next(&tag, &ok)) { + if (!ok) { + break; + } + static_cast(tag)->proceed(); + } + }; + loop_thread_ = std::thread(loop_lambda); +} + +bool AdminServerImpl::stop() { + if (State::STARTED != state_.load()) { + return false; + } + + if (server_) { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + SPDLOG_INFO("Stopping admin server"); + server_->Shutdown(); + + if (completion_queue_) { + completion_queue_->Shutdown(); + // Drain completion_queue_ + { + void* tag; + bool ignore_ok; + while (completion_queue_->Next(&tag, &ignore_ok)) { + } + } + } + + if (loop_thread_.joinable()) { + loop_thread_.join(); + } + + state_.store(State::STOPPED); + SPDLOG_INFO("Admin server stopped"); + return true; + } + return false; + } else { + SPDLOG_ERROR("Admin server is unexpected nullptr"); + } + return false; +} +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/AdminServiceImpl.cpp b/src/main/cpp/admin/AdminServiceImpl.cpp new file mode 100644 index 000000000..b1d1ce93e --- /dev/null +++ b/src/main/cpp/admin/AdminServiceImpl.cpp @@ -0,0 +1,49 @@ +#include "AdminServiceImpl.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { + +Status applyChange(const rmq::ChangeLogLevelRequest* request) { + auto logger = spdlog::get("rocketmq_logger"); + if (!logger) { + return Status(grpc::StatusCode::INTERNAL, "rocketmq_logger is not registered"); + } + + auto level = request->level(); + switch (level) { + case rmq::ChangeLogLevelRequest_Level_TRACE: + logger->set_level(spdlog::level::trace); + break; + case rmq::ChangeLogLevelRequest_Level_DEBUG: + logger->set_level(spdlog::level::debug); + break; + case rmq::ChangeLogLevelRequest_Level_INFO: + logger->set_level(spdlog::level::info); + break; + case rmq::ChangeLogLevelRequest_Level_WARN: + logger->set_level(spdlog::level::warn); + break; + case rmq::ChangeLogLevelRequest_Level_ERROR: + logger->set_level(spdlog::level::err); + break; + default: + logger->set_level(spdlog::level::info); + break; + } + return grpc::Status::OK; +} + +Status AdminServiceImpl::ChangeLogLevel(ServerContext* context, const rmq::ChangeLogLevelRequest* request, + rmq::ChangeLogLevelResponse* reply) { + Status status = applyChange(request); + if (status.ok()) { + reply->set_remark("OK"); + } + return status; +} + +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/BUILD.bazel b/src/main/cpp/admin/BUILD.bazel new file mode 100644 index 000000000..1e18e46bf --- /dev/null +++ b/src/main/cpp/admin/BUILD.bazel @@ -0,0 +1,43 @@ +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_binary") +cc_library( + name = "admin_server_library", + hdrs = [ + "include/AdminServiceImpl.h", + "include/AdminServerImpl.h", + "include/ServerCall.h", + ], + srcs = [ + "AdminFacade.cpp", + "AdminServerImpl.cpp", + "AdminServiceImpl.cpp", + ], + strip_include_prefix = "//src/main/cpp/admin/include", + deps = [ + "//api:rocketmq_interface", + "//proto:rocketmq_grpc_library", + "@com_github_gabime_spdlog//:spdlog", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "admin_client_interface", + hdrs = [ + "include/AdminClient.h", + ], + strip_include_prefix = "//src/main/cpp/admin/include", + deps = [ + "//api:rocketmq_interface", + ], +) + +cc_binary( + name = "admin_client", + srcs = [ + "AdminClient.cpp", + ], + deps = [ + ":admin_client_interface", + "//proto:rocketmq_grpc_library", + ], +) \ No newline at end of file diff --git a/src/main/cpp/admin/CMakeLists.txt b/src/main/cpp/admin/CMakeLists.txt new file mode 100644 index 000000000..c05093fbf --- /dev/null +++ b/src/main/cpp/admin/CMakeLists.txt @@ -0,0 +1,44 @@ +add_library(admin_obj OBJECT) + +target_include_directories(admin_obj + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_sources(admin_obj + PRIVATE + AdminServiceImpl.cpp + AdminServerImpl.cpp + AdminFacade.cpp) + +target_link_libraries(admin_obj + PUBLIC + rocketmq_interface + spdlog::spdlog_header_only + mq_proto_library + grpc++) + +add_library(admin_library + STATIC + $) +target_include_directories(admin_library + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(admin_library + PUBLIC + mq_proto_library + grpc++ + rocketmq_interface + spdlog::spdlog_header_only) + + +add_executable(admin_client AdminClient.cpp) +target_include_directories(admin_client + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(admin_client + PRIVATE + grpc++ + mq_proto_library + rocketmq_interface + $<$:-static-libgcc> + $<$:-static-libstdc++>) \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h new file mode 100644 index 000000000..7954c958d --- /dev/null +++ b/src/main/cpp/admin/include/AdminClient.h @@ -0,0 +1,29 @@ +#pragma once +#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include +#include + +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; + +namespace admin { + +class AdminClient { +public: + explicit AdminClient(std::shared_ptr& channel) : stub_(rmq::Admin::NewStub(channel)) {} + + Status changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response); + +private: + std::unique_ptr stub_; +}; + +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h new file mode 100644 index 000000000..b50aaf51a --- /dev/null +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -0,0 +1,43 @@ +#pragma once + +#include "AdminServiceImpl.h" +#include "rocketmq/AdminServer.h" +#include +#include +#include +#include +#include +#include + +using grpc::Server; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +class AdminServerImpl : public AdminServer { +public: + AdminServerImpl() + : port_(0), state_(State::CREATED), async_stub_(new rmq::Admin::AsyncService), service_(new AdminServiceImpl) {} + + bool start() override; + + bool stop() override; + + int port() const override { return port_; } + +private: + int port_; + std::unique_ptr server_; + std::atomic state_; + std::unique_ptr async_stub_; + std::unique_ptr service_; + std::unique_ptr completion_queue_; + std::thread loop_thread_; + + std::mutex loop_mtx_; + std::condition_variable loop_cv_; + void loop(); +}; +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminServiceImpl.h b/src/main/cpp/admin/include/AdminServiceImpl.h new file mode 100644 index 000000000..2fa865af0 --- /dev/null +++ b/src/main/cpp/admin/include/AdminServiceImpl.h @@ -0,0 +1,20 @@ +#pragma once +#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include + +using grpc::ServerContext; +using grpc::Status; +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +namespace admin { +class AdminServiceImpl final : public rmq::Admin::Service { +public: + Status ChangeLogLevel(ServerContext* context, const rmq::ChangeLogLevelRequest* request, + rmq::ChangeLogLevelResponse* reply) override; +}; +} // namespace admin + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h new file mode 100644 index 000000000..213cf3c17 --- /dev/null +++ b/src/main/cpp/admin/include/ServerCall.h @@ -0,0 +1,59 @@ +#pragma once + +#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include + +namespace rmq = apache::rocketmq::v1; + +ROCKETMQ_NAMESPACE_BEGIN + +enum ServerCallStatus : int8_t { CREATE = 0, PROCESS = 1, FINISH = 2 }; + +class ServerCall { +public: + ServerCall(rmq::Admin::AsyncService* async_service, rmq::Admin::Service* service, + grpc::ServerCompletionQueue* completion_queue) + : async_stub_(async_service), service_(service), completion_queue_(completion_queue), + response_observer_(&context_), status_(ServerCallStatus::CREATE) { + proceed(); + } + + void proceed() { + switch (status_) { + case CREATE: { + status_ = PROCESS; + async_stub_->RequestChangeLogLevel(&context_, &request_, &response_observer_, completion_queue_, + completion_queue_, this); + break; + } + case PROCESS: { + // Create a new ServerCall to serve the next incoming request. + new ServerCall(async_stub_, service_, completion_queue_); + + // Now that request_ is already filled with actual data from clients, invoke the actual process function + const grpc::Status rpc_status = service_->ChangeLogLevel(&context_, &request_, &response_); + + status_ = FINISH; + response_observer_.Finish(response_, rpc_status, this); + break; + } + default: { + assert(FINISH == status_); + delete this; + } + } + } + +private: + rmq::Admin::AsyncService* async_stub_; + rmq::Admin::Service* service_; + grpc::ServerCompletionQueue* completion_queue_; + grpc::ServerContext context_; + rmq::ChangeLogLevelRequest request_; + rmq::ChangeLogLevelResponse response_; + grpc::ServerAsyncResponseWriter response_observer_; + ServerCallStatus status_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel new file mode 100644 index 000000000..e72b38609 --- /dev/null +++ b/src/main/cpp/base/BUILD.bazel @@ -0,0 +1,65 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "http_client", + hdrs = [ + "include/HttpClient.h", + ], + srcs = [ + "HttpClient.cpp", + ], + strip_include_prefix = "//src/main/cpp/base/include", + deps = [ + "//api:rocketmq_interface", + "@com_google_absl//absl/time", + "@com_google_absl//absl/synchronization", + "@curl//:curl", + "@com_github_gabime_spdlog//:spdlog", + ], + visibility = [ + "//visibility:public", + ], +) + +cc_library( + name = "top_addressing", + hdrs = [ + "include/TopAddressing.h", + "include/HostInfo.h" + ], + srcs = [ + "TopAddressing.cpp", + "HostInfo.cpp", + ], + strip_include_prefix = "//src/main/cpp/base/include", + deps = [ + ":http_client", + ], + visibility = [ + "//visibility:public", + ], +) + +cc_library( + name = "base_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/base/include", + deps = [ + "//api:rocketmq_interface", + "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_github_gabime_spdlog//:spdlog", + "@com_github_grpc_grpc//:grpc_secure", + "//external:libuv", + "@com_googlesource_code_re2//:re2", + "@zlib//:zlib", + "@boringssl//:crypto", + "@boringssl//:ssl", + "@curl//:curl", + ], + visibility = [ + "//visibility:public", + ], +) \ No newline at end of file diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt new file mode 100644 index 000000000..8f5b1fbdd --- /dev/null +++ b/src/main/cpp/base/CMakeLists.txt @@ -0,0 +1,57 @@ +add_library(base_obj OBJECT) +target_sources(base_obj + PRIVATE + HttpClient.cpp + HostInfo.cpp + MixAll.cpp + MQMessage.cpp + PermissionHelper.cpp + Protocol.cpp + RateLimiter.cpp + RequestIdGenerator.cpp + TopAddressing.cpp + MessageSystemFlag.cpp + UtilAll.cpp) +target_include_directories(base_obj + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(base_obj + PUBLIC + absl::flat_hash_set + absl::flat_hash_map + absl::random_random + absl::synchronization + absl::strings + absl::time + CURL::libcurl + fmt + grpc++ + crypto + ssl + re2::re2 + rocketmq_interface + spdlog::spdlog_header_only + uv_a + zlib) + +add_library(base_library + STATIC + $ include/MessageAccessor.h) +target_include_directories(base_library + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(base_library + PUBLIC + absl::flat_hash_set + absl::flat_hash_map + absl::random_random + absl::synchronization + absl::strings + CURL::libcurl + fmt + grpc++ + re2::re2 + rocketmq_interface + spdlog::spdlog_header_only + uv_a + zlib) \ No newline at end of file diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp new file mode 100644 index 000000000..0987a20c6 --- /dev/null +++ b/src/main/cpp/base/HostInfo.cpp @@ -0,0 +1,59 @@ +#include "HostInfo.h" +#include "absl/strings/match.h" +#include +#include + +namespace rocketmq { + +const char* HostInfo::ENV_LABEL_SITE = "SIGMA_APP_SITE"; +const char* HostInfo::ENV_LABEL_UNIT = "SIGMA_APP_UNIT"; +const char* HostInfo::ENV_LABEL_APP = "SIGMA_APP_NAME"; +const char* HostInfo::ENV_LABEL_STAGE = "SIGMA_APP_STAGE"; + +HostInfo::HostInfo() { + getEnv(ENV_LABEL_SITE, site_); + getEnv(ENV_LABEL_UNIT, unit_); + getEnv(ENV_LABEL_APP, app_); + getEnv(ENV_LABEL_STAGE, stage_); +} + +void HostInfo::getEnv(const char* env, std::string& holder) { + if (!strlen(env)) { + return; + } + + char* value = getenv(env); + if (nullptr != value) { + holder.clear(); + holder.append(value); + } +} + +bool HostInfo::hasHostInfo() const { return !unit_.empty() && !stage_.empty(); } + +std::string HostInfo::queryString() const { + if (!hasHostInfo()) { + return std::string(); + } + + std::string query_string("labels="); + appendLabel(query_string, "site", site_); + appendLabel(query_string, "unit", unit_); + appendLabel(query_string, "app", app_); + appendLabel(query_string, "stage", stage_); + return query_string; +} + +void HostInfo::appendLabel(std::string& query_string, const char* key, const std::string& value) { + if (value.empty()) { + return; + } + + if (absl::EndsWith(query_string, "=")) { + query_string.append(key).append(":").append(value); + } else { + query_string.append(",").append(key).append(":").append(value); + } +} + +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/HttpClient.cpp b/src/main/cpp/base/HttpClient.cpp new file mode 100644 index 000000000..f66d95b03 --- /dev/null +++ b/src/main/cpp/base/HttpClient.cpp @@ -0,0 +1,67 @@ +#include "HttpClient.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +HttpClient::HttpClient() { + curl_ = curl_easy_init(); + if (!curl_) { + return; + } +} + +HttpClient::~HttpClient() { + if (curl_) { + curl_easy_cleanup(curl_); + curl_ = nullptr; + } +} + +std::size_t HttpClient::writeCallback(void* data, std::size_t size, std::size_t nmemb, void* param) { + auto response = reinterpret_cast(param); + response->reserve(size * nmemb); + response->append(reinterpret_cast(data), size * nmemb); + return size * nmemb; +} + +bool HttpClient::get(const std::string& query, std::string& response, absl::Duration timeout) { + SPDLOG_DEBUG("GET {}", query); + if (!curl_) { + SPDLOG_WARN("CURL session is not properly"); + return false; + } + CURLcode res; + + // CURL easy-interface is not thread-safe. + absl::MutexLock lk(&mtx_); + + SPDLOG_DEBUG("Reset CURL session"); + curl_easy_reset(curl_); + + curl_easy_setopt(curl_, CURLOPT_URL, query.c_str()); + SPDLOG_DEBUG("Set CURL url: {}", query); + + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, &HttpClient::writeCallback); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*)&response); + + SPDLOG_DEBUG("Set CURL IO timeout to {}ms", absl::ToInt64Milliseconds(timeout)); + curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS, absl::ToInt64Milliseconds(timeout)); + + SPDLOG_DEBUG("Set CURL connect timeout to {}ms", absl::ToInt64Milliseconds(timeout)); + curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT_MS, absl::ToInt64Milliseconds(timeout)); + + SPDLOG_DEBUG("Prepare to initiate HTTP/GET request"); + res = curl_easy_perform(curl_); + SPDLOG_DEBUG("CURL completes request/response"); + + if (CURLE_OK == res) { + SPDLOG_DEBUG("Response code: {}, text: {}", res, response); + return true; + } else { + SPDLOG_WARN("Response code: {}, text: {}", res, response); + return false; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp new file mode 100644 index 000000000..3d062584f --- /dev/null +++ b/src/main/cpp/base/MQMessage.cpp @@ -0,0 +1,212 @@ +#include "rocketmq/MQMessage.h" +#include "MessageAccessor.h" +#include "Protocol.h" +#include "rocketmq/MQMessageExt.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageImpl { +private: + Resource topic_; + std::map user_attribute_map_; + SystemAttribute system_attribute_; + std::string body_; + friend class MQMessage; + friend class MQMessageExt; + friend class MessageAccessor; +}; + +MQMessage::MQMessage() : MQMessage("", "", "", "") {} + +MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) {} + +MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& body) + : MQMessage(topic, tags, "", body) {} + +MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, + const std::string& body) + : impl_(new MessageImpl) { + impl_->topic_.name = topic; + impl_->system_attribute_.tag = tags; + impl_->system_attribute_.keys.emplace_back(keys); + impl_->body_.clear(); + impl_->body_.reserve(body.length()); + impl_->body_.append(body.data(), body.length()); +} + +MQMessage::~MQMessage() { delete impl_; } + +MQMessage::MQMessage(const MQMessage& other) { impl_ = new MessageImpl(*other.impl_); } + +MQMessage& MQMessage::operator=(const MQMessage& other) { + if (this == &other) { + return *this; + } + *impl_ = *(other.impl_); + return *this; +} + +void MQMessage::setProperty(const std::string& name, const std::string& value) { + impl_->user_attribute_map_[name] = value; +} + +std::string MQMessage::getProperty(const std::string& name) const { + auto it = impl_->user_attribute_map_.find(name); + if (impl_->user_attribute_map_.end() == it) { + return ""; + } + return it->second; +} + +const std::string& MQMessage::getTopic() const { return impl_->topic_.name; } + +void MQMessage::setTopic(const std::string& topic) { impl_->topic_.name = topic; } + +void MQMessage::setTopic(const char* data, int len) { impl_->topic_.name = std::string(data, len); } + +std::string MQMessage::getTags() const { return impl_->system_attribute_.tag; } + +void MQMessage::setTags(const std::string& tags) { impl_->system_attribute_.tag = tags; } + +const std::vector& MQMessage::getKeys() const { return impl_->system_attribute_.keys; } + +void MQMessage::setKey(const std::string& key) { impl_->system_attribute_.keys.push_back(key); } + +void MQMessage::setKeys(const std::vector& keys) { impl_->system_attribute_.keys = keys; } + +int MQMessage::getDelayTimeLevel() const { return impl_->system_attribute_.delay_level; } + +void MQMessage::setDelayTimeLevel(int level) { impl_->system_attribute_.delay_level = level; } + +const std::string& MQMessage::getBody() const { return impl_->body_; } + +void MQMessage::setBody(const char* body, int len) { + impl_->body_.clear(); + impl_->body_.reserve(len); + impl_->body_.append(body, len); +} + +void MQMessage::setBody(const std::string& body) { impl_->body_ = body; } + +int32_t MQMessage::bodyLength() const { return static_cast(impl_->body_.length()); } + +const std::map& MQMessage::getProperties() const { return impl_->user_attribute_map_; } + +void MQMessage::setProperties(const std::map& properties) { + for (const auto& it : properties) { + impl_->user_attribute_map_.insert({it.first, it.second}); + } +} + +MQMessageExt::MQMessageExt() : MQMessage() {} + +MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) {} + +MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { + if (this == &other) { + return *this; + } + + *impl_ = *(other.impl_); + return *this; +} + +int32_t MQMessageExt::getQueueId() const { return impl_->system_attribute_.partition_id; } + +std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); +} + +int64_t MQMessageExt::getBornTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); } + +std::string MQMessageExt::getBornHost() const { return impl_->system_attribute_.born_host; } + +std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); +} + +int64_t MQMessageExt::getStoreTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); } + +std::string MQMessageExt::getStoreHost() const { return impl_->system_attribute_.store_host; } + +const std::string& MQMessageExt::getMsgId() const { return impl_->system_attribute_.message_id; } + +int64_t MQMessageExt::getQueueOffset() const { return impl_->system_attribute_.partition_offset; } + +int32_t MQMessageExt::getReconsumeTimes() const { return impl_->system_attribute_.delivery_count; } + +const std::string& MQMessageExt::receiptHandle() const { return impl_->system_attribute_.receipt_handle; } + +const std::string& MQMessageExt::traceContext() const { return impl_->system_attribute_.trace_context; } + +bool MQMessageExt::operator==(const MQMessageExt& other) { + return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; +} + +void MessageAccessor::setMessageId(MQMessageExt& message, std::string message_id) { + message.impl_->system_attribute_.message_id = std::move(message_id); +} + +void MessageAccessor::setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp) { + message.impl_->system_attribute_.born_timestamp = born_timestamp; +} + +void MessageAccessor::setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp) { + message.impl_->system_attribute_.store_timestamp = store_timestamp; +} + +void MessageAccessor::setQueueId(MQMessageExt& message, int32_t queue_id) { + message.impl_->system_attribute_.partition_id = queue_id; +} + +void MessageAccessor::setQueueOffset(MQMessageExt& message, int64_t queue_offset) { + message.impl_->system_attribute_.partition_offset = queue_offset; +} + +void MessageAccessor::setBornHost(MQMessageExt& message, std::string born_host) { + message.impl_->system_attribute_.born_host = std::move(born_host); +} + +void MessageAccessor::setStoreHost(MQMessageExt& message, std::string store_host) { + message.impl_->system_attribute_.store_host = std::move(store_host); +} + +void MessageAccessor::setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp) { + message.impl_->system_attribute_.delivery_timestamp = delivery_timestamp; +} + +void MessageAccessor::setDeliveryCount(MQMessageExt& message, int32_t delivery_count) { + message.impl_->system_attribute_.delivery_count = delivery_count; +} + +void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp) { + message.impl_->system_attribute_.decode_timestamp = decode_timestamp; +} + +void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { + message.impl_->system_attribute_.invisible_period = invisible_period; +} + +void MessageAccessor::setReceiptHandle(MQMessageExt& message, std::string receipt_handle) { + message.impl_->system_attribute_.receipt_handle = std::move(receipt_handle); +} + +void MessageAccessor::setTraceContext(MQMessageExt& message, std::string trace_context) { + message.impl_->system_attribute_.trace_context = std::move(trace_context); +} + +void MessageAccessor::setMessageType(MQMessage& message, MessageType message_type) { + message.impl_->system_attribute_.message_type = message_type; +} + +void MessageAccessor::setTargetEndpoint(MQMessage& message, const std::string& target_endpoint) { + message.impl_->system_attribute_.target_endpoint = target_endpoint; +} + +const std::string& MessageAccessor::targetEndpoint(const MQMessage& message) { + return message.impl_->system_attribute_.target_endpoint; +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/MessageSystemFlag.cpp b/src/main/cpp/base/MessageSystemFlag.cpp new file mode 100644 index 000000000..a22e905e7 --- /dev/null +++ b/src/main/cpp/base/MessageSystemFlag.cpp @@ -0,0 +1,13 @@ +#include "MessageSystemFlag.h" + +namespace rocketmq { + +const uint32_t MessageSystemFlag::BODY_COMPRESS_TYPE = 0x1; +const uint32_t MessageSystemFlag::MULTI_TAG_TYPE = 0x1 << 1; +const uint32_t MessageSystemFlag::PREPARED_TYPE = 0x1 << 2; +const uint32_t MessageSystemFlag::COMMIT_TYPE = 0x1 << 3; +const uint32_t MessageSystemFlag::ROLLBACK_TYPE = 0x3 << 2; + +bool MessageSystemFlag::bodyCompressed(uint32_t flag) { return BODY_COMPRESS_TYPE == (flag & BODY_COMPRESS_TYPE); } + +} // namespace rocketmq diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp new file mode 100644 index 000000000..58e134fcd --- /dev/null +++ b/src/main/cpp/base/MixAll.cpp @@ -0,0 +1,183 @@ +#include "MixAll.h" + +#include + +#include "absl/random/random.h" +#include "absl/strings/str_split.h" + +#include "fmt/format.h" + +#include +#include + +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +const int32_t MixAll::MASTER_BROKER_ID = 0; + +const int32_t MixAll::MAX_MESSAGE_NUMBER_PER_BATCH = 32; + +const int32_t MixAll::MAX_MESSAGE_BODY_SIZE = 1024 * 1024 * 4; + +const char* MixAll::RETRY_GROUP_TOPIC_PREFIX = "%RETRY%"; + +const RE2 MixAll::TOPIC_REGEX("[a-zA-Z0-9\\-_]{3,64}"); + +const std::chrono::duration MixAll::DEFAULT_INVISIBLE_TIME_ = std::chrono::seconds(30); + +const std::chrono::duration MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ = std::chrono::seconds(20); + +const int32_t MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_ = 3; + +const std::string MixAll::PROPERTY_TRANSACTION_PREPARED_ = "TRAN_MSG"; + +const std::string MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_ = "AVG"; + +const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; + +const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; + +bool MixAll::validate(const MQMessage& message) { + if (message.getTopic().empty()) { + return false; + } + const std::string& topic = message.getTopic(); + // Topic should not start with "CID" or "GID" which are reserved prefix + if (absl::StartsWith(topic, "CID") || absl::StartsWith(topic, "GID")) { + return false; + } + + // Legal topic characters are a-z, A-Z, 0-9, hyphen('-') and underline('_') + if (!RE2::FullMatch(topic, TOPIC_REGEX)) { + return false; + } + + int32_t body_length = message.bodyLength(); + if (!body_length || body_length > MAX_MESSAGE_BODY_SIZE) { + return false; + } + return true; +} + +uint32_t MixAll::random(uint32_t left, uint32_t right) { + static absl::BitGen gen; + return absl::Uniform(gen, left, right); +} + +bool MixAll::md5(const std::string& data, std::string& digest) { + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, data.data(), data.length()); + unsigned char md[MD5_DIGEST_LENGTH + 1]; + int success = MD5_Final(md, &ctx); + if (!success) { + return false; + } + digest.clear(); + digest.append(hex(md, MD5_DIGEST_LENGTH)); + return true; +} + +bool MixAll::sha1(const std::string& data, std::string& digest) { + unsigned char out[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, data.data(), data.length()); + SHA1_Final(out, &ctx); + digest.clear(); + digest.append(reinterpret_cast(out), SHA_DIGEST_LENGTH); + return true; +} + +std::string MixAll::format(std::chrono::system_clock::time_point time_point) { + std::time_t creation_time_t = std::chrono::system_clock::to_time_t(time_point); + auto fraction = std::chrono::duration_cast(time_point.time_since_epoch()).count() % 1000; + char fmt_date_time[128]; + + /** + * TODO: std::localtime is not thread-safe, output, as a result, may be less reliable in highly contending + * scenario + */ + std::strftime(fmt_date_time, sizeof(fmt_date_time), "%Y-%m-%d %H:%M:%S", std::localtime(&creation_time_t)); + return fmt::format("{}.{}", fmt_date_time, fraction); +} + +std::string MixAll::hex(const void* data, std::size_t len) { + const char dict[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + std::string s; + const uint8_t* ptr = reinterpret_cast(data); + for (std::size_t i = 0; i < len; i++) { + unsigned char c = *(ptr + i); + s.append(&dict[(0xF0 & c) >> 4], 1); + s.append(&dict[(0x0F & c)], 1); + } + return s; +} + +bool MixAll::hexToBinary(const std::string& hex, std::vector& bin) { + // Length of valid Hex string should always be even. + if (hex.length() % 2) { + return false; + } + for (std::string::size_type i = 0; i < hex.length(); i += 2) { + char c1 = hex.at(i); + char c2 = hex.at(i + 1); + + uint8_t value = 0; + uint8_t tmp; + if (hexCharValue(c1, tmp)) { + value = tmp << 4; + } else { + return false; + } + + if (hexCharValue(c2, tmp)) { + value |= tmp; + bin.push_back(value); + } else { + return false; + } + } + return true; +} + +bool MixAll::hexCharValue(char c, uint8_t& value) { + if ('0' <= c && c <= '9') { + value = c - '0'; + return true; + } + + if ('a' <= c && c <= 'f') { + value = c - 'a' + 10; + return true; + } + + if ('A' <= c && c <= 'F') { + value = c - 'A' + 10; + return true; + } + + return false; +} + +bool MixAll::homeDirectory(std::string& home_dir) { +#ifndef _WIN32 + char* home = getenv(HOME_PROFILE_ENV_); + if (home) { + home_dir.append(home, strlen(home)); + return true; + } else { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) { + home_dir.clear(); + home_dir.append(pwd->pw_dir, strlen(pwd->pw_dir)); + return true; + } + } + return false; +#endif +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/PermissionHelper.cpp b/src/main/cpp/base/PermissionHelper.cpp new file mode 100644 index 000000000..70328d19b --- /dev/null +++ b/src/main/cpp/base/PermissionHelper.cpp @@ -0,0 +1,15 @@ +#include "PermissionHelper.h" + +using namespace rocketmq; + +const unsigned int PermissionHelper::READ_ = 0x1u << 2u; + +const unsigned int PermissionHelper::WRITE_ = 0x1u << 1u; + +const unsigned int PermissionHelper::INHERIT_ = 0x1u << 0u; + +bool PermissionHelper::canRead(unsigned int mode) { return (mode & READ_) == READ_; } + +bool PermissionHelper::canWrite(unsigned int mode) { return (mode & WRITE_) == WRITE_; } + +bool PermissionHelper::canInherit(unsigned int mode) { return (mode & INHERIT_) == INHERIT_; } \ No newline at end of file diff --git a/src/main/cpp/base/Protocol.cpp b/src/main/cpp/base/Protocol.cpp new file mode 100644 index 000000000..563ef3eea --- /dev/null +++ b/src/main/cpp/base/Protocol.cpp @@ -0,0 +1,11 @@ +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +#ifndef CLIENT_PROTOCOL_VERSION +#define CLIENT_PROTOCOL_VERSION "v1" +#endif + +const char* Protocol::PROTOCOL_VERSION = CLIENT_PROTOCOL_VERSION; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/RateLimiter.cpp b/src/main/cpp/base/RateLimiter.cpp new file mode 100644 index 000000000..e53bfb873 --- /dev/null +++ b/src/main/cpp/base/RateLimiter.cpp @@ -0,0 +1,38 @@ +#include "RateLimiter.h" + +ROCKETMQ_NAMESPACE_BEGIN + +RateLimiterObserver::RateLimiterObserver() : stopped_(false) { + tick_thread_ = std::thread([this] { + while (!stopped_.load(std::memory_order_relaxed)) { + { + std::lock_guard lk(members_mtx_); + for (auto it = members_.begin(); it != members_.end();) { + std::shared_ptr tick = it->lock(); + if (!tick) { + it = members_.erase(it); + continue; + } else { + ++it; + } + tick->tick(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); +} + +void RateLimiterObserver::subscribe(const std::shared_ptr& tick) { + std::lock_guard lk(members_mtx_); + members_.emplace_back(tick); +} + +void RateLimiterObserver::stop() { + stopped_.store(true, std::memory_order_relaxed); + if (tick_thread_.joinable()) { + tick_thread_.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/RequestIdGenerator.cpp b/src/main/cpp/base/RequestIdGenerator.cpp new file mode 100644 index 000000000..05f7e5e80 --- /dev/null +++ b/src/main/cpp/base/RequestIdGenerator.cpp @@ -0,0 +1,44 @@ +#include "RequestIdGenerator.h" + +#include "absl/base/internal/sysinfo.h" +#include "MixAll.h" +#include "UtilAll.h" +#include "spdlog/spdlog.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +std::atomic_long RequestIdGenerator::sequence_(0); + +std::string RequestIdGenerator::nextRequestId() { + int32_t numeric[4]; + std::string request_id; + const std::string& ip = UtilAll::getHostIPv4(); + struct in_addr addr = {}; + int ret = inet_aton(ip.data(), &addr); + + // ip is supposed to be legal and parsable. + assert(ret == 1); + numeric[0] = htonl(addr.s_addr); + numeric[1] = absl::base_internal::GetCachedTID(); + *(reinterpret_cast(numeric) + 1) = ++sequence_; + return MixAll::hex(numeric, sizeof(numeric)); +} + +int64_t RequestIdGenerator::sequenceOf(const std::string& request_id) { + if (request_id.empty()) { + SPDLOG_WARN("Invalid empty request_id"); + return 0; + } + std::vector bin; + if (!MixAll::hexToBinary(request_id, bin)) { + SPDLOG_WARN("Invalid request_id: {}", request_id); + return 0; + } + // IP(int32_t) + PID(int32_t) + term_id(int64_t) + assert(bin.size() == 4 + 4 + 8); + auto ptr = reinterpret_cast(bin.data()); + return *(ptr + 1); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp new file mode 100644 index 000000000..344d358dd --- /dev/null +++ b/src/main/cpp/base/TopAddressing.cpp @@ -0,0 +1,58 @@ +#include "TopAddressing.h" + +#include "absl/strings/match.h" +#include "absl/strings/str_split.h" +#include +#include + +#include "HttpClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TopAddressing::TopAddressing(std::string host, int port, std::string path) + : host_(std::move(host)), port_(port), path_(std::move(path)) {} + +bool TopAddressing::fetchNameServerAddresses(std::vector& list) { + SPDLOG_DEBUG("Prepare to send HTTP request, timeout=3s"); + std::string base(fmt::format("http://{}:{}", host_, port_)); + // Append host info if necessary. + std::string query_string(path_); + + if (!unit_name_.empty()) { + query_string.append("-").append(unit_name_); + } + + if (absl::StrContains(query_string, "?")) { + query_string.append("&"); + } else { + query_string.append("?"); + } + + if (host_info_.hasHostInfo()) { + query_string.append(host_info_.queryString()); + } else { + query_string.append("nofix=1"); + } + + std::string request(fmt::format("{}{}", base, query_string)); + std::string response; + + HttpClient http_client; + if (!http_client) { + SPDLOG_WARN("Failed to create CURL session. OS probably runs out of resources. Check out memory and FD usage"); + return false; + } + + if (http_client.get(request, response, absl::Seconds(3))) { + SPDLOG_INFO("Received name server list: {}", response); + std::vector splits = absl::StrSplit(response, ";"); + for (const auto& item : splits) { + list.push_back(item); + } + return true; + } + + return false; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp new file mode 100644 index 000000000..d44a46137 --- /dev/null +++ b/src/main/cpp/base/UtilAll.cpp @@ -0,0 +1,219 @@ +#include "UtilAll.h" + +#include "absl/strings/str_split.h" +#include "include/LoggerImpl.h" +#include "uv.h" +#include "zlib.h" + +namespace rocketmq { + +std::string UtilAll::getHostIPv4() { + static std::string host_ip; + if (host_ip.empty()) { + std::vector addresses = getIPv4Addresses(); + if (!pickIPv4Address(addresses, host_ip)) { + return LOOP_BACK_IP; + } + } + return host_ip; +} + +std::vector UtilAll::getIPv4Addresses() { + std::vector addresses; + char buf[512]; + uv_interface_address_t* info; + int count, i; + + uv_interface_addresses(&info, &count); + i = count; + while (i--) { + uv_interface_address_t interface = info[i]; + if (interface.address.address4.sin_family == AF_INET) { + uv_ip4_name(&interface.address.address4, buf, sizeof(buf)); + if (interface.is_internal) { + continue; + } + addresses.emplace_back(std::string(buf)); + } else if (interface.address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interface.address.address6, buf, sizeof(buf)); + } + } + uv_free_interface_addresses(info, count); + return addresses; +} + +/** + * Select IP from provided lists. The selecting criteria is first WAN then LAN. + * For case of LAN, the following order is followed: + * 1, 10.0.0.0/8 (255.0.0.0), 10.0.0.0 – 10.255.255.255 + * 2, 100.64.0.0/10(255.192.0.0), 100.64.0.0 - 100.127.255.255 + * 3, 172.16.0.0/12 (255.240.0.0) 172.16.0.0 – 172.31.255.255 + * 4, 192.168.0.0/16 (255.255.0.0), 192.168.0.0 – 192.168.255.255 + */ +std::vector> UtilAll::Ranges = {{"10.0.0.0", "10.255.255.255"}, + {"100.64.0.0", "100.127.255.255"}, + {"172.16.0.0", "172.31.255.255"}, + {"192.168.0.0", "192.168.255.255"}}; + +int UtilAll::findCategory(const std::string& ip, const std::vector>& categories) { + struct in_addr addr = {}; + int status = inet_aton(ip.data(), &addr); + if (!status) { + SPDLOG_WARN("inet_aton failed. IPv4: {}", ip); + return -1; + } + uint32_t current = ntohl(addr.s_addr); + + int index = 1; + for (const auto& item : categories) { + const std::string& left = item.first; + const std::string& right = item.second; + struct in_addr low = {}; + struct in_addr high = {}; + status = inet_aton(left.data(), &low); + if (!status) { + SPDLOG_WARN("inet_aton failed. IPv4: {}", left); + return -1; + } + + status = inet_aton(right.data(), &high); + if (!status) { + SPDLOG_WARN("inet_aton failed. IPv4: {}", right); + return -1; + } + + uint32_t start = ntohl(low.s_addr); + uint32_t end = ntohl(high.s_addr); + if (current >= start && current <= end) { + return index; + } + index++; + } + return 0; +} + +bool UtilAll::pickIPv4Address(const std::vector& addresses, std::string& result) { + if (addresses.empty()) { + return false; + } + + if (addresses.size() == 1) { + result = addresses[0]; + } + + std::vector> categories; + categories.reserve(5); + categories.resize(5); + for (const auto& ip : addresses) { + int index = findCategory(ip, Ranges); + if (index >= 0) { + std::vector>::size_type category_index = index; + if (category_index < categories.size()) { + categories[category_index].push_back(ip); + } + } + } + for (const auto& category : categories) { + if (!category.empty()) { + result = category[0]; + return true; + } + } + result = addresses[0]; + return true; +} + +void UtilAll::int16tobyte(char* out_array, int16_t data) { + if (!out_array) { + return; + } + out_array[0] = static_cast(data & 0xff); + out_array[1] = static_cast((data >> 8) & 0xff); +} + +void UtilAll::int32tobyte(char* out_array, int32_t data) { + if (!out_array) { + return; + } + out_array[0] = static_cast(data & 0xff); + out_array[1] = static_cast((data >> 8) & 0xff); + out_array[2] = static_cast((data >> 16) & 0xff); + out_array[3] = static_cast((data >> 24) & 0xff); +} + +bool UtilAll::compress(const std::string& src, std::string& dst) { + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = reinterpret_cast(const_cast(src.c_str())); + stream.avail_in = src.length(); + + deflateInit(&stream, Z_DEFAULT_COMPRESSION); + uint32_t bound = deflateBound(&stream, src.length()); + std::vector buffer(bound); + stream.next_out = buffer.data(); + stream.avail_out = bound; + + int status = deflate(&stream, Z_FINISH); + + // zlib is unexpected wrong. + if (Z_STREAM_END != status) { + deflateEnd(&stream); + return false; + } + + dst.reserve(stream.total_out); + assert(stream.total_out == bound - stream.avail_out); + std::copy(buffer.data(), buffer.data() + stream.total_out, std::back_inserter(dst)); + deflateEnd(&stream); + return true; +} + +bool UtilAll::uncompress(const std::string& src, std::string& dst) { + int status; + uint32_t buffer_length = 4096; + std::vector buffer(buffer_length); + z_stream stream; + + // Use default malloc / free allocator. + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = reinterpret_cast(const_cast(src.c_str())); + stream.avail_in = src.length(); + + inflateInit(&stream); + stream.next_out = buffer.data(); + stream.avail_out = buffer_length; + + while (true) { + status = inflate(&stream, Z_SYNC_FLUSH); + + if (Z_STREAM_ERROR == status) { + return false; + } + std::copy(buffer.data(), buffer.data() + buffer_length - stream.avail_out, std::back_inserter(dst)); + stream.avail_out = buffer_length; + stream.next_out = buffer.data(); + + // inflation completed OK + if (Z_STREAM_END == status) { + inflateEnd(&stream); + return true; + } + + // inflation made some progress + if (Z_OK == status) { + continue; + } + + // Something is wrong + inflateEnd(&stream); + return false; + } +} + +std::string UtilAll::LOOP_BACK_IP("127.0.0.1"); + +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/Functional.h b/src/main/cpp/base/include/Functional.h new file mode 100644 index 000000000..d06e05a73 --- /dev/null +++ b/src/main/cpp/base/include/Functional.h @@ -0,0 +1,28 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Functional { +public: + Functional() = default; + + explicit Functional(std::function* function) : function_(function) {} + + ~Functional() = default; + + void setFunction(std::function* function) { function_ = function; } + + std::function* getFunction() { return function_; } + +private: + std::function* function_; +}; + +using FunctionalSharePtr = std::shared_ptr; +using FunctionalWeakPtr = std::weak_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h new file mode 100644 index 000000000..8a81bba7a --- /dev/null +++ b/src/main/cpp/base/include/Histogram.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace rocketmq { +class Histogram { +public: + Histogram(std::string title, int32_t capacity) : title_(std::move(title)), capacity_(capacity) { + data_.reserve(capacity); + for (int i = 0; i < capacity_; ++i) { + data_.push_back(std::unique_ptr>(new std::atomic(0))); + } + } + + void countIn(int grade) { + if (grade < 0) { + return; + } + + if (grade >= capacity_) { + data_[capacity_ - 1]->fetch_add(1, std::memory_order_relaxed); + return; + } + + data_[grade]->fetch_add(1, std::memory_order_relaxed); + } + + /** + * Change labels of histogram duration the initialization phase only. + * @return + */ + std::vector& labels() { return labels_; } + + void reportAndReset(std::string& result) { + assert(labels_.size() == capacity_); + std::vector values; + values.reserve(capacity_); + for (auto& item : data_) { + int value = item->load(std::memory_order_relaxed); + values.push_back(value); + item->fetch_sub(value, std::memory_order_relaxed); + } + result.clear(); + result.append(title_).append(":"); + for (int i = 0; i < labels_.size(); ++i) { + if (i) { + result.append(", "); + } + result.append(labels_[i]).append(std::to_string(values[i])); + } + } + +private: + std::string title_; + std::vector>> data_; + int32_t capacity_; + std::vector labels_; +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h new file mode 100644 index 000000000..4f65a110d --- /dev/null +++ b/src/main/cpp/base/include/HostInfo.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace rocketmq { + +struct HostInfo { + std::string site_; + std::string unit_; + std::string app_; + std::string stage_; + + explicit HostInfo(); + + bool hasHostInfo() const; + + std::string queryString() const; + + static const char* ENV_LABEL_SITE; + static const char* ENV_LABEL_UNIT; + static const char* ENV_LABEL_APP; + static const char* ENV_LABEL_STAGE; + +private: + static void getEnv(const char* env, std::string& holder); + + static void appendLabel(std::string& query_string, const char* key, const std::string& value); +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h new file mode 100644 index 000000000..096abfaee --- /dev/null +++ b/src/main/cpp/base/include/HttpClient.h @@ -0,0 +1,29 @@ +#pragma once + +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClient { +public: + HttpClient(); + + virtual ~HttpClient(); + + bool get(const std::string& query, std::string& response, absl::Duration timeout); + + explicit operator bool() const { return curl_ != nullptr; } + + static std::size_t writeCallback(void* data, std::size_t size, std::size_t nmemb, void* param); + +private: + CURL* curl_; + absl::Mutex mtx_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h new file mode 100644 index 000000000..7a08113e7 --- /dev/null +++ b/src/main/cpp/base/include/InvocationContext.h @@ -0,0 +1,90 @@ +#pragma once + +#include "LoggerImpl.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * Note: + * Before modifying anything in this file, ensure you have read all comments in completion_queue_impl.h and + * async_stream.h + */ +struct BaseInvocationContext { + virtual ~BaseInvocationContext() = default; + virtual void onCompletion(bool ok) = 0; + + grpc::ClientContext context_; + grpc::Status status_; + std::chrono::system_clock::time_point created_time_{std::chrono::system_clock::now()}; + std::chrono::steady_clock::time_point start_time_{std::chrono::steady_clock::now()}; +}; + +template +struct InvocationContext : public BaseInvocationContext { + + void onCompletion(bool ok) override { + /// Client-side Read, Server-side Read, Client-side + /// RecvInitialMetadata (which is typically included in Read if not + /// done explicitly): ok indicates whether there is a valid message + /// that got read. If not, you know that there are certainly no more + /// messages that can ever be read from this stream. For the client-side + /// operations, this only happens because the call is dead. For the + /// server-side operation, though, this could happen because the client + /// has done a WritesDone already. + if (!ok) { + SPDLOG_WARN("One async call is already dead"); + + if (callback_) { + callback_(grpc::Status::CANCELLED, context_, response_); + } + + delete this; + return; + } + + if (!status_.ok() && grpc::StatusCode::DEADLINE_EXCEEDED == status_.error_code()) { + std::time_t creation_time_t = std::chrono::system_clock::to_time_t(created_time_); + auto fraction = + std::chrono::duration_cast(created_time_.time_since_epoch()).count() % 1000; + char fmt_date_time[128]; + + /** + * TODO: std::localtime is not thread-safe, output, as a result, may be less reliable in highly contending + * scenario + */ + std::strftime(fmt_date_time, sizeof(fmt_date_time), "%Y-%m-%d %H:%M:%S", std::localtime(&creation_time_t)); + + auto elapsed = + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time_).count(); + auto diff = + std::chrono::duration_cast(std::chrono::system_clock::now() - context_.deadline()) + .count(); + SPDLOG_WARN("Asynchronous RPC[{}.{}] timed out, elapsing {}ms, deadline-over-due: {}ms", fmt_date_time, fraction, + elapsed, diff); + } + try { + if (callback_) { + callback_(status_, context_, response_); + } + } catch (const std::exception& e) { + SPDLOG_WARN("Unexpected error while invoking user-defined callback. Reason: {}", e.what()); + } catch (...) { + SPDLOG_WARN("Unexpected error while invoking user-defined callback"); + } + delete this; + } + + T response_; + std::function callback_; + std::unique_ptr> response_reader_; +}; +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/LoggerImpl.h b/src/main/cpp/base/include/LoggerImpl.h new file mode 100644 index 000000000..a695bd923 --- /dev/null +++ b/src/main/cpp/base/include/LoggerImpl.h @@ -0,0 +1,3 @@ +#pragma once +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h new file mode 100644 index 000000000..a88114857 --- /dev/null +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -0,0 +1,44 @@ +#pragma once + +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/RocketMQ.h" + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageAccessor { + +public: + static void setMessageId(MQMessageExt& message, std::string message_id); + + static void setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp); + + static void setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp); + + static void setQueueId(MQMessageExt& message, int32_t queue_id); + + static void setQueueOffset(MQMessageExt& message, int64_t queue_offset); + + static void setBornHost(MQMessageExt& message, std::string born_host); + + static void setStoreHost(MQMessageExt& message, std::string store_host); + + static void setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp); + + static void setDeliveryCount(MQMessageExt& message, int32_t delivery_count); + + static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); + static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); + + static void setReceiptHandle(MQMessageExt& message, std::string receipt_handle); + static void setTraceContext(MQMessageExt& message, std::string trace_context); + + static void setMessageType(MQMessage& message, MessageType message_type); + + static void setTargetEndpoint(MQMessage& message, const std::string& target_endpoint); + + static const std::string& targetEndpoint(const MQMessage& message); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageSystemFlag.h b/src/main/cpp/base/include/MessageSystemFlag.h new file mode 100644 index 000000000..2df7a4204 --- /dev/null +++ b/src/main/cpp/base/include/MessageSystemFlag.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace rocketmq { +class MessageSystemFlag { +public: + static const uint32_t BODY_COMPRESS_TYPE; + static const uint32_t MULTI_TAG_TYPE; + static const uint32_t PREPARED_TYPE; + static const uint32_t COMMIT_TYPE; + static const uint32_t ROLLBACK_TYPE; + + static bool bodyCompressed(uint32_t flag); + + template static void clearFlag(T& value, T flag) { value &= ~flag; } + + template static void tagFlag(T& value, T flag) { + +#ifdef DEBUG + // Ensure existing flags are orthogonal to the flag to tag + T check = value & flag; + if (0 != check && flag != check) { + abort(); + } +#endif + + value |= flag; + } +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h new file mode 100644 index 000000000..01f497adf --- /dev/null +++ b/src/main/cpp/base/include/MixAll.h @@ -0,0 +1,72 @@ +#pragma once + +#include "re2/re2.h" +#include +#include +#include +#include + +#include "rocketmq/MQMessage.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MixAll { +public: + static const int32_t MASTER_BROKER_ID; + static const int32_t MAX_MESSAGE_NUMBER_PER_BATCH; + static const int32_t MAX_MESSAGE_BODY_SIZE; + static const char* RETRY_GROUP_TOPIC_PREFIX; + + static const RE2 TOPIC_REGEX; + + /** + * The amount of time required before a popped message is eligible to be consumed again. By default, 30s. + */ + static const std::chrono::duration DEFAULT_INVISIBLE_TIME_; + + static const std::chrono::duration PROCESS_QUEUE_EXPIRATION_THRESHOLD_; + + static const int32_t MAX_SEND_MESSAGE_ATTEMPT_TIMES_; + + static const std::string PROPERTY_TRANSACTION_PREPARED_; + + static const std::string DEFAULT_LOAD_BALANCER_STRATEGY_NAME_; + + static const uint32_t DEFAULT_COMPRESS_BODY_THRESHOLD_; + + static const char* HOME_PROFILE_ENV_; + + template static int64_t millisecondsOf(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); + } + + template static int64_t microsecondsOf(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); + } + + /** + * Validate message is legal. Aka, topic + * @param message + * @return + */ + static bool validate(const MQMessage& message); + + static uint32_t random(uint32_t left, uint32_t right); + + static bool md5(const std::string& data, std::string& digest); + + static bool sha1(const std::string& data, std::string& digest); + + static std::string format(std::chrono::system_clock::time_point time_point); + + static std::string hex(const void* data, std::size_t len); + + static bool hexToBinary(const std::string& hex, std::vector& bin); + + static bool homeDirectory(std::string& home); +private: + static bool hexCharValue(char c, uint8_t& value); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/PermissionHelper.h b/src/main/cpp/base/include/PermissionHelper.h new file mode 100644 index 000000000..1756a7e5d --- /dev/null +++ b/src/main/cpp/base/include/PermissionHelper.h @@ -0,0 +1,18 @@ +#pragma once + +namespace rocketmq { +class PermissionHelper { +public: + static bool canRead(unsigned int mode); + + static bool canWrite(unsigned int mode); + + static bool canInherit(unsigned int mode); + +private: + static const unsigned int READ_; + static const unsigned int WRITE_; + static const unsigned int INHERIT_; +}; + +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h new file mode 100644 index 000000000..65dfb2c70 --- /dev/null +++ b/src/main/cpp/base/include/Protocol.h @@ -0,0 +1,78 @@ +#pragma once + +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Protocol { +public: + static const char* PROTOCOL_VERSION; +}; + +enum MessageType : int8_t { + NORMAL = 0, + FIFO = 1, + DELAY = 2, + TRANSACTION = 3, +}; + +enum DigestType : int8_t { + CRC32 = 0, + MD5 = 1, + SHA1 = 2, +}; + +struct Digest { + DigestType digest_type{DigestType::MD5}; + std::string checksum; + Digest() = default; +}; + +enum Encoding : int8_t { + IDENTITY = 0, + GZIP = 1, + SNAPPY = 2, +}; + +struct Resource { + /** + * Abstract resource namespace + */ + std::string arn; + + /** + * Resource name, which remains unique within given abstract resource namespace. + */ + std::string name; +}; + +struct SystemAttribute { + std::string tag; + std::vector keys; + std::string message_id; + Digest digest; + Encoding body_encoding; + MessageType message_type; + absl::Time born_timestamp{absl::UnixEpoch()}; + std::string born_host; + absl::Time store_timestamp{absl::UnixEpoch()}; + std::string store_host; + absl::Time delivery_timestamp{absl::UnixEpoch()}; + absl::Time decode_timestamp{absl::UnixEpoch()}; + int32_t delay_level{0}; + std::string receipt_handle; + int32_t partition_id{0}; + int64_t partition_offset{0}; + absl::Duration invisible_period; + int32_t delivery_count{0}; + Resource publisher_group; + std::string trace_context; + std::string target_endpoint; +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h new file mode 100644 index 000000000..805939891 --- /dev/null +++ b/src/main/cpp/base/include/RateLimiter.h @@ -0,0 +1,164 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Tick { +public: + virtual ~Tick() = default; + + virtual void tick() = 0; +}; + +class RateLimiterObserver { +public: + RateLimiterObserver(); + + void subscribe(const std::shared_ptr& tick); + + void stop(); + +private: + std::vector> members_; + std::mutex members_mtx_; + + std::atomic_bool stopped_; + + std::thread tick_thread_; +}; + +template class RateLimiter : public Tick { +public: + explicit RateLimiter(uint32_t permit) : permits_{0}, interval_(1000 / PARTITION) { + uint32_t avg = permit / PARTITION; + for (auto& i : partition_) { + i = avg; + } + + uint32_t r = permit % PARTITION; + + if (r) { + uint32_t step = PARTITION / r; + for (uint32_t i = 0; i < r; ++i) { + partition_[i * step]++; + } + } + } + + ~RateLimiter() override = default; + + std::array& partition() { return permits_; } + + int slot() { + auto current = std::chrono::steady_clock::now(); + long ms = std::chrono::duration_cast(current.time_since_epoch()).count(); + return ms / interval_ % PARTITION; + } + + uint32_t available() { + uint32_t available_permit = 0; + int idx = slot(); + // Reuse quota of the past half of the cycle + for (int j = 0; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + available_permit += permits_[index]; + } + } + + return available_permit; + } + + uint32_t acquire(uint32_t permit) { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] >= permit) { + permits_[idx] -= permit; + return permit; + } + + uint32_t acquired = 0; + if (permits_[idx] > 0) { + acquired += permits_[idx]; + permits_[idx] = 0; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + while (permits_[index] > 0) { + --permits_[index]; + if (++acquired >= permit) { + break; + } + } + } + return acquired; + } + } + + void acquire() { + int idx = slot(); + { + std::unique_lock lk(mtx_); + if (permits_[idx] > 0) { + --permits_[idx]; + return; + } + + // Reuse quota of the past half of the cycle + for (int j = 1; j <= PARTITION / 2; ++j) { + int index = idx - j; + if (index < 0) { + index += PARTITION; + } + if (permits_[index] > 0) { + --permits_[index]; + return; + } + } + + cv_.wait(lk, [this]() { + int idx = slot(); + return permits_[idx] > 0; + }); + idx = slot(); + --permits_[idx]; + } + } + + void tick() override { + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / PARTITION)); + int idx = slot(); + { + std::unique_lock lk(mtx_); + permits_[idx] = partition_[idx]; + cv_.notify_all(); + } + } + +private: + std::array partition_; + std::array permits_; + int interval_; + std::mutex mtx_; + std::condition_variable cv_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/RequestIdGenerator.h b/src/main/cpp/base/include/RequestIdGenerator.h new file mode 100644 index 000000000..048bbe23c --- /dev/null +++ b/src/main/cpp/base/include/RequestIdGenerator.h @@ -0,0 +1,20 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class RequestIdGenerator { +public: + static std::string nextRequestId(); + + static int64_t sequenceOf(const std::string& request_id); + +private: + static std::atomic_long sequence_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h new file mode 100644 index 000000000..71309417c --- /dev/null +++ b/src/main/cpp/base/include/TopAddressing.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "HostInfo.h" +#include "HttpClient.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressing { +public: + explicit TopAddressing() : host_("jmenv.tbsite.net"), path_("/rocketmq/nsaddr") {} + + TopAddressing(std::string host, int port, std::string path); + + bool fetchNameServerAddresses(std::vector& list); + + void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } + +private: + std::string host_; + int port_{8080}; + std::string path_; + HostInfo host_info_; + + /** + * Allow use to override unit name. + */ + std::string unit_name_; +}; + +using TopAddressingPtr = std::unique_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/UtilAll.h b/src/main/cpp/base/include/UtilAll.h new file mode 100644 index 000000000..40a32070c --- /dev/null +++ b/src/main/cpp/base/include/UtilAll.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const std::string DEFAULT_CONSUMER_GROUP = "DEFAULT_CONSUMER"; + +class UtilAll { +public: + static std::string getHostIPv4(); + + /** + * Walk through all network interfaces and return IPv4 addresses. + */ + static std::vector getIPv4Addresses(); + + /** + * Select IP from provided lists. The selecting criteria is first WAN then LAN. + * For case of LAN, the following order is followed: + * 1, 10.0.0.0/8 (255.0.0.0), 10.0.0.0 – 10.255.255.255 + * 2, 100.64.0.0/10(255.192.0.0), 100.64.0.0 - 100.127.255.255 + * 3, 172.16.0.0/12 (255.240.0.0) 172.16.0.0 – 172.31.255.255 + * 4, 192.168.0.0/16 (255.255.0.0), 192.168.0.0 – 192.168.255.255 + */ + static bool pickIPv4Address(const std::vector& addresses, std::string& result); + + static std::string LOOP_BACK_IP; + + static void int16tobyte(char* out_array, int16_t data); + + static void int32tobyte(char* out_array, int32_t data); + + /** + * Compress + * @param src + * @param dst + * @return + */ + static bool compress(const std::string& src, std::string& dst); + + static bool uncompress(const std::string& src, std::string& dst); + +private: + static int findCategory(const std::string& ip, const std::vector>& ranges); + + static std::vector> Ranges; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel new file mode 100644 index 000000000..bef671ab2 --- /dev/null +++ b/src/main/cpp/client/BUILD.bazel @@ -0,0 +1,42 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "client_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/client/include", + deps = [ + "//api:rocketmq_interface", + "//proto:rocketmq_grpc_library", + "//src/main/cpp/base:base_library", + "//src/main/cpp/scheduler:scheduler_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_github_grpc_grpc//:grpc_secure", + "@com_github_grpc_grpc//:grpc++", + "@com_github_opentelemetry//api", + "@boringssl//:ssl", + "//external:gtest", + "//third_party/crc:crc_interface", + ], + visibility = ["//visibility:public"], + defines = [ + ], +) + +cc_library( + name = "rpc_client_mock", + hdrs = [ + "mocks/include/RpcClientMock.h", + "mocks/include/IdentifiableMock.h", + ], + srcs = [ + "mocks/RpcClientMock.cpp", + ], + strip_include_prefix = "//src/main/cpp/client/mocks/include", + deps = [ + ":client_library", + "//external:gtest", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt new file mode 100644 index 000000000..73ce038f7 --- /dev/null +++ b/src/main/cpp/client/CMakeLists.txt @@ -0,0 +1,66 @@ +add_library(client_obj OBJECT) +target_sources(client_obj + PRIVATE + ClientApi.cpp + ClientConfig.cpp + ClientInstance.cpp + ClientManager.cpp + LogInterceptorFactory.cpp + LogInterceptor.cpp + Metadata.cpp + TopicAssignmentInfo.cpp + RpcClientImpl.cpp + Signature.cpp + TlsHelper.cpp + TopicPublishInfo.cpp + TracingUtility.cpp) +target_include_directories(client_obj + PUBLIC + ${CMAKE_BINARY_DIR}/generated/third_party/opentelemetry-proto + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(client_obj + PUBLIC + admin_library + crc_library + crypto + date::date + scheduler_library + ssl + PRIVATE + gtest) +if (ENABLE_TRACING) + target_link_libraries(client_obj + PUBLIC + opentelemetry_trace + opentelemetry_exporter_otprotocol) +endif () + +add_library(client_library + STATIC + $) + +target_include_directories(client_library + PUBLIC + ${CMAKE_BINARY_DIR}/generated/third_party/opentelemetry-proto + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_link_libraries(client_library + PUBLIC + admin_library + crc_library + crypto + CURL::libcurl + date::date + mq_proto_library + scheduler_library + ssl) +if (ENABLE_TRACING) + target_link_libraries(client_library + PUBLIC + opentelemetry_trace + opentelemetry_exporter_otprotocol) +endif () + +if (BUILD_TESTS) + add_subdirectory(mocks) +endif() diff --git a/src/main/cpp/client/ClientApi.cpp b/src/main/cpp/client/ClientApi.cpp new file mode 100644 index 000000000..300a6cbfd --- /dev/null +++ b/src/main/cpp/client/ClientApi.cpp @@ -0,0 +1,15 @@ +#include "ClientApi.h" + +using namespace rocketmq; + +void ClientApi::update_name_server_list(const std::vector& list) { + if (list.empty()) { + return; + } + + { + absl::MutexLock lock(&name_server_list_mtx_); + name_server_list_.clear(); + name_server_list_.insert(name_server_list_.begin(), list.begin(), list.end()); + } +} \ No newline at end of file diff --git a/src/main/cpp/client/ClientConfig.cpp b/src/main/cpp/client/ClientConfig.cpp new file mode 100644 index 000000000..261894661 --- /dev/null +++ b/src/main/cpp/client/ClientConfig.cpp @@ -0,0 +1,57 @@ +#include "ClientConfig.h" +#include "UtilAll.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +#ifndef CLIENT_VERSION_MAJOR +#define CLIENT_VERSION_MAJOR "1" +#endif + +#ifndef CLIENT_VERSION_MINOR +#define CLIENT_VERSION_MINOR "0" +#endif + +#ifndef CLIENT_VERSION_PATCH +#define CLIENT_VERSION_PATCH "0" +#endif + +const char* ClientConfig::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; + +ClientConfig::ClientConfig() : ClientConfig(std::string()) {} + +ClientConfig::ClientConfig(std::string group_name) : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)) { + instance_name_ = "DEFAULT"; +} + +std::string ClientConfig::clientId() const { + std::stringstream ss; + ss << UtilAll::getHostIPv4(); + ss << "@"; + std::string processID = std::to_string(getpid()); + ss << (instance_name_ == "DEFAULT" ? processID : instance_name_); + return ss.str(); +} + +const std::string& ClientConfig::getInstanceName() const { return instance_name_; } + +void ClientConfig::setInstanceName(std::string instance_name) { instance_name_ = std::move(instance_name); } + +const std::string& ClientConfig::getGroupName() const { return group_name_; } + +void ClientConfig::setGroupName(std::string group_name) { group_name_ = std::move(group_name); } + +void ClientConfig::setIoTimeout(absl::Duration timeout) { io_timeout_ = timeout; } + +void ClientConfig::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + credentials_provider_ = std::move(credentials_provider); +} + +CredentialsProviderPtr ClientConfig::credentialsProvider() { + return credentials_provider_; +} + +absl::Duration ClientConfig::getIoTimeout() const { return io_timeout_; } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientInstance.cpp b/src/main/cpp/client/ClientInstance.cpp new file mode 100644 index 000000000..00899de40 --- /dev/null +++ b/src/main/cpp/client/ClientInstance.cpp @@ -0,0 +1,1275 @@ +#include "ClientInstance.h" + +#include +#include + +#include "CRC.h" +#include "InvocationContext.h" +#include "LogInterceptor.h" +#include "LogInterceptorFactory.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "MessageSystemFlag.h" +#include "Metadata.h" +#include "MixAll.h" +#include "Partition.h" +#include "Protocol.h" +#include "RpcClientImpl.h" +#include "TlsHelper.h" +#include "UtilAll.h" +#include "grpcpp/create_channel.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessageExt.h" + +#ifdef ENABLE_TRACING +#include "TracingUtility.h" +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +ClientInstance::ClientInstance(std::string arn) + : arn_(std::move(arn)), state_(State::CREATED), completion_queue_(std::make_shared()), + callback_thread_pool_(std::make_shared(std::thread::hardware_concurrency())), + latency_histogram_("Message-Latency", 11) { + spdlog::set_level(spdlog::level::trace); + + inactive_rpc_client_detector_ = std::bind(&ClientInstance::doHealthCheck, this); + inactive_rpc_client_detector_function_ = std::make_shared(&inactive_rpc_client_detector_); + + heartbeat_loop_ = std::bind(&ClientInstance::doHeartbeat, this); + heartbeat_loop_function_ = std::make_shared(&heartbeat_loop_); + + assignLabels(latency_histogram_); + + stats_functor_ = std::bind(&ClientInstance::logStats, this); + stats_function_ = std::make_shared(&stats_functor_); + + server_authorization_check_config_ = std::make_shared( + std::make_shared()); + + // Make use of encryption only at the moment. + std::vector identity_key_cert_list; + grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, + .certificate_chain = TlsHelper::client_certificate_chain}; + identity_key_cert_list.emplace_back(pair); + certificate_provider_ = + std::make_shared(TlsHelper::CA, identity_key_cert_list); + tls_channel_credential_options_.set_server_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION); + tls_channel_credential_options_.set_certificate_provider(certificate_provider_); + tls_channel_credential_options_.set_server_authorization_check_config(server_authorization_check_config_); + tls_channel_credential_options_.watch_root_certs(); + tls_channel_credential_options_.watch_identity_key_cert_pairs(); + channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); + SPDLOG_INFO("ClientInstance[ARN={}] created", arn_); +} + +ClientInstance::~ClientInstance() { + shutdown(); + SPDLOG_INFO("ClientInstance[ARN={}] destructed", arn_); +} + +void ClientInstance::start() { + if (State::CREATED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); + return; + } + state_.store(State::STARTING, std::memory_order_relaxed); + + scheduler_.start(); + + bool scheduled = + scheduler_.schedule(inactive_rpc_client_detector_function_, std::chrono::seconds(5), std::chrono::seconds(15)); + if (scheduled) { + SPDLOG_INFO("inactive client detector scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule inactive client detector"); + } + + scheduled = scheduler_.schedule(heartbeat_loop_function_, std::chrono::seconds(1), std::chrono::seconds(10)); + if (scheduled) { + SPDLOG_INFO("Heartbeat loop scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule consumer heartbeat loop"); + } + + completion_queue_thread_ = std::thread(std::bind(&ClientInstance::pollCompletionQueue, this)); + + scheduled = scheduler_.schedule(stats_function_, std::chrono::seconds(0), std::chrono::seconds(10)); + if (scheduled) { + SPDLOG_INFO("logStats loop scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule logStats loop"); + } + + state_.store(State::STARTED, std::memory_order_relaxed); +} + +void ClientInstance::shutdown() { + SPDLOG_DEBUG("Client instance shutdown"); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); + return; + } + + state_.store(STOPPING, std::memory_order_relaxed); + { + absl::MutexLock lk(&inactive_rpc_clients_mtx_); + inactive_rpc_clients_.clear(); + SPDLOG_DEBUG("CompletionQueue of inactive clients stopped"); + } + + { + absl::MutexLock lk(&rpc_clients_mtx_); + rpc_clients_.clear(); + SPDLOG_DEBUG("CompletionQueue of active clients stopped"); + } + + scheduler_.shutdown(); + + completion_queue_->Shutdown(); + if (completion_queue_thread_.joinable()) { + completion_queue_thread_.join(); + } + SPDLOG_DEBUG("Completion queue thread completes OK"); + + state_.store(State::STOPPED, std::memory_order_relaxed); + SPDLOG_DEBUG("Client instance stopped"); +} + +void ClientInstance::assignLabels(Histogram& histogram) { + histogram.labels().emplace_back("[000ms~020ms): "); + histogram.labels().emplace_back("[020ms~040ms): "); + histogram.labels().emplace_back("[040ms~060ms): "); + histogram.labels().emplace_back("[060ms~080ms): "); + histogram.labels().emplace_back("[080ms~100ms): "); + histogram.labels().emplace_back("[100ms~120ms): "); + histogram.labels().emplace_back("[120ms~140ms): "); + histogram.labels().emplace_back("[140ms~160ms): "); + histogram.labels().emplace_back("[160ms~180ms): "); + histogram.labels().emplace_back("[180ms~200ms): "); + histogram.labels().emplace_back("[200ms~inf): "); +} + +bool ClientInstance::addInactiveRpcClient(const std::string& target_host) { + std::shared_ptr client; + // get the inactive rpc client and remove from rpc_clients_(active client) + { + absl::MutexLock lock(&rpc_clients_mtx_); + if (!rpc_clients_.contains(target_host)) { + SPDLOG_ERROR("RpcClient {} can not be found in rpc_clients_, remove error", target_host); + return false; + } else { + client = rpc_clients_[target_host]; + rpc_clients_.erase(target_host); + } + } + // add to inactive_rpc_clients_ + { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + inactive_rpc_clients_.insert(std::make_pair(target_host, client)); + } + return true; +} + +bool ClientInstance::removeInactiveRpcClient(const std::string& target_host) { + std::shared_ptr client; + { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + if (!inactive_rpc_clients_.contains(target_host)) { + SPDLOG_ERROR("RpcClient {} can not be found in inactive_rpc_clients_, remove error", target_host); + return false; + } else { + client = inactive_rpc_clients_[target_host]; + inactive_rpc_clients_.erase(target_host); + } + } + + { + absl::MutexLock lock(&rpc_clients_mtx_); + rpc_clients_.insert(std::make_pair(target_host, client)); + } + SPDLOG_DEBUG("Migrated {} from inactive to active list", target_host); + return true; +} + +bool ClientInstance::getInactiveRpcClient(absl::string_view target_host, RpcClientSharedPtr& client) { + std::string target(target_host.data(), target_host.length()); + { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + if (!inactive_rpc_clients_.contains(target)) { + SPDLOG_DEBUG("RpcClient {} can not be found in inactive_rpc_clients_. Host:{}", target); + return false; + } else { + client = inactive_rpc_clients_[target]; + } + } + return true; +} + +void ClientInstance::doHealthCheck() { + SPDLOG_DEBUG("Start to perform health check for inactive clients"); + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + cleanOfflineRpcClients(); + clientHealthCheck(); + SPDLOG_DEBUG("Health check completed"); +} + +/** + * TODO:(lingchu) optimize implementation. Client may just select one broker by random. + */ +#ifdef ENABLE_TRACING +void ClientInstance::updateTraceProvider() { + SPDLOG_DEBUG("Start to update global trace provider"); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + absl::flat_hash_set exporter_endpoint_set; + { + absl::MutexLock lock(&topic_route_table_mtx_); + for (const auto& route_entry : topic_route_table_) { + for (const auto& partition : route_entry.second->partitions()) { + const std::string& broker_name = partition.broker().name(); + if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { + continue; + } + exporter_endpoint_set.insert(partition.broker().serviceAddress()); + } + } + } + { + absl::MutexLock lock(&exporter_endpoint_set_mtx_); + // Available endpoint was not changed, no need to change global trace provider. + if (exporter_endpoint_set == exporter_endpoint_set_) { + return; + } + + // TODO: support ipv6. + std::string exporter_endpoint; + if (!exporter_endpoint_set.empty()) { + exporter_endpoint = absl::StrJoin(exporter_endpoint_set.begin(), exporter_endpoint_set.end(), ","); + } + + opentelemetry::exporter::otlp::OtlpExporterOptions exporter_options; + // If no available export, use default export here. + if (!exporter_endpoint_set.empty()) { + exporter_options.endpoint = exporter_endpoint; + } + + auto exporter = std::unique_ptr(new otlp::OtlpExporter(exporter_options)); + + sdktrace::BatchSpanProcessorOptions options{}; + options.max_queue_size = 32768; + options.max_export_batch_size = 16384; + options.schedule_delay_millis = std::chrono::milliseconds(1000); + + auto processor = + std::shared_ptr(new sdktrace::BatchSpanProcessor(std::move(exporter), options)); + auto provider = nostd::shared_ptr(new sdktrace::TracerProvider(processor)); + // Set the global trace provider + trace::Provider::SetTracerProvider(provider); + + exporter_endpoint_set_.clear(); + exporter_endpoint_set_.merge(exporter_endpoint_set); + } +} + +nostd::shared_ptr ClientInstance::getTracer() { + { + absl::MutexLock lock(&exporter_endpoint_set_mtx_); + if (exporter_endpoint_set_.empty()) { + return nostd::shared_ptr(nullptr); + } + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("RocketmqClient"); + } +} + +#endif + +void ClientInstance::cleanOfflineRpcClients() { + absl::flat_hash_set hosts; + { + absl::MutexLock lk(&clients_mtx_); + for (const auto& item : clients_) { + std::shared_ptr client = item.lock(); + if (!client) { + continue; + } + client->activeHosts(hosts); + } + } + + { + absl::MutexLock lk(&rpc_clients_mtx_); + for (auto it = rpc_clients_.begin(); it != rpc_clients_.end();) { + std::string host = it->first; + if (it->second->needHeartbeat() && !hosts.contains(host)) { + SPDLOG_INFO("Removed RPC client whose peer is offline. RemoteHost={}", host); + rpc_clients_.erase(it++); + } else { + it++; + } + } + } + + { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + for (auto it = inactive_rpc_clients_.begin(); it != inactive_rpc_clients_.end();) { + std::string host = it->first; + if (it->second->needHeartbeat() && !hosts.contains(host)) { + inactive_rpc_clients_.erase(it++); + SPDLOG_INFO("Removed RPC client whose peer is offline. RemoteHost={}", host); + } else { + it++; + } + } + } +} + +bool ClientInstance::isRpcClientActive(absl::string_view target_host) { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + return !inactive_rpc_clients_.contains(target_host); +} + +void ClientInstance::getAllInactiveRpcClientHost(absl::flat_hash_set& client_hosts) { + client_hosts.clear(); + { + absl::MutexLock lock(&inactive_rpc_clients_mtx_); + for (const auto& entry : inactive_rpc_clients_) { + client_hosts.insert(entry.first); + } + } +} + +void ClientInstance::clientHealthCheck() { + absl::flat_hash_set clientsHosts; + getAllInactiveRpcClientHost(clientsHosts); + for (const auto& clientHost : clientsHosts) { + RpcClientSharedPtr client; + if (!getInactiveRpcClient(clientHost, client)) { + SPDLOG_ERROR("get inactive client error, client Host :{}", clientHost); + } + SPDLOG_DEBUG("check client(host: {}) health status", clientHost); + // detect if the rpc client is healthy + HealthCheckRequest request; + HealthCheckResponse response; + request.set_client_host(clientHost); + auto invocation_context = new InvocationContext(); + + // TODO: Acquire metadata from ProducerImpl/ConsumerImpl. + absl::flat_hash_map metadata; + + // TODO: Sign metadata + + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [this, clientHost](const grpc::Status& status, const grpc::ClientContext& context, + const HealthCheckResponse& response) { + if (status.ok() && response.common().status().code() == google::rpc::Code::OK) { + SPDLOG_INFO("Inactive client {} passes health check, thus, is considered active", clientHost); + if (!removeInactiveRpcClient(clientHost)) { + SPDLOG_ERROR("Failed to move the previous inactive client to active list", clientHost); + } + } else { + SPDLOG_INFO("client for host={} failed to pass health check again. Reason: {}", clientHost, + response.common().DebugString()); + } + }; + invocation_context->callback_ = callback; + + client->asyncHealthCheck(request, invocation_context); + } // end for +} + +void ClientInstance::heartbeat(const std::string& target_host, + const absl::flat_hash_map& metadata, + const HeartbeatRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + auto client = getRpcClient(target_host, true); + if (!client) { + return; + } + + auto invocation_context = new InvocationContext(); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, + const HeartbeatResponse& response) { + if (status.ok()) { + if (google::rpc::Code::OK == response.common().status().code()) { + SPDLOG_DEBUG("Send heartbeat to target_host={}, gRPC status OK", target_host); + cb(true, response); + } else { + SPDLOG_WARN("Server[{}] failed to process heartbeat. Reason: {}", target_host, response.common().DebugString()); + cb(false, response); + } + } else { + SPDLOG_WARN("Failed to send heartbeat to target_host={}. GRPC code: {}, message : {}", target_host, + status.error_code(), status.error_message()); + cb(false, response); + } + }; + invocation_context->callback_ = callback; + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + client->asyncHeartbeat(request, invocation_context); +} + +void ClientInstance::doHeartbeat() { + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + + { + absl::MutexLock lk(&clients_mtx_); + for (const auto& item : clients_) { + auto client = item.lock(); + if (client) { + client->heartbeat(); + } + } + } +} + +void ClientInstance::pollCompletionQueue() { + + while (State::STARTED == state_.load(std::memory_order_relaxed) || + State::STARTING == state_.load(std::memory_order_relaxed)) { + bool ok = false; + void* opaque_invocation_context; + while (completion_queue_->Next(&opaque_invocation_context, &ok)) { + auto invocation_context = static_cast(opaque_invocation_context); + if (!ok) { + // the call is dead + SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); + } + auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; + callback_thread_pool_->enqueue(callback); + } + SPDLOG_INFO("CompletionQueue is fully drained and shut down"); + } + SPDLOG_INFO("pollCompletionQueue completed and quit"); +} + +bool ClientInstance::send(const std::string& target, const absl::flat_hash_map& metadata, + SendMessageRequest& request, SendCallback* callback) { + assert(callback); + std::string target_host(target.data(), target.length()); + SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); + RpcClientSharedPtr client = getRpcClient(target_host); + +#ifdef ENABLE_TRACING + nostd::shared_ptr span = nostd::shared_ptr(nullptr); + if (trace_) { + span = getTracer()->StartSpan("SendMessageAsync"); + + MQMessage message = context->getMessage(); + span->SetAttribute(TracingUtility::get().topic_, message.getTopic()); + span->SetAttribute(TracingUtility::get().tags_, message.getTags()); + span->SetAttribute(TracingUtility::get().msg_id_, context->getMessageId()); + + const std::string& serialized_span_context = TracingUtility::injectSpanContextToTraceParent(span->GetContext()); + request.mutable_message()->mutable_system_attribute()->set_trace_context(serialized_span_context); + } +#else + bool span = false; +#endif + + // Invocation context will be deleted in its onComplete() method. + auto invocation_context = new InvocationContext(); + for (const auto& entry : metadata) { + invocation_context->context_.AddMetadata(entry.first, entry.second); + } + + const std::string& topic = request.message().topic().name(); + auto completion_callback = [topic, callback, target_host, span, this](const grpc::Status& status, + const grpc::ClientContext&, + const SendMessageResponse& response) { + if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { +#ifdef ENABLE_TRACING + if (span) { + span->SetAttribute(TracingUtility::get().success_, true); + span->End(); + } +#endif + SendResult send_result; + send_result.setMsgId(response.message_id()); + send_result.setQueueOffset(-1); + MQMessageQueue message_queue; + message_queue.setQueueId(-1); + message_queue.setTopic(topic); + send_result.setMessageQueue(message_queue); + if (State::STARTED == state_.load(std::memory_order_relaxed)) { + callback->onSuccess(send_result); + } else { + SPDLOG_INFO("Client instance has stopped, state={}. Ignore send result {}", + state_.load(std::memory_order_relaxed), send_result.getMsgId()); + } + } else { + +#ifdef ENABLE_TRACING + if (span) { + span->SetAttribute(TracingUtility::get().success_, false); + span->End(); + } +#endif + + if (!status.ok()) { + SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", + target_host, status.error_code(), status.error_message()); + } + std::string msg; + msg.append("gRPC code: ") + .append(std::to_string(status.error_code())) + .append(", gRPC message: ") + .append(status.error_message()) + .append(", code: ") + .append(std::to_string(response.common().status().code())) + .append(", remark: ") + .append(response.common().DebugString()); + MQException e(msg, FAILED_TO_SEND_MESSAGE, __FILE__, __LINE__); + if (State::STARTED == state_.load(std::memory_order_relaxed)) { + callback->onException(e); + } else { + SPDLOG_WARN("Client instance has stopped, state={}. Ignore exception raised while sending message: {}", + state_.load(std::memory_order_relaxed), e.what()); + } + } + }; + + invocation_context->callback_ = completion_callback; + client->asyncSend(request, invocation_context); + return true; +} + +RpcClientSharedPtr ClientInstance::getRpcClient(const std::string& target_host, bool need_heartbeat) { + std::shared_ptr client; + { + absl::MutexLock lock(&rpc_clients_mtx_); + auto search = rpc_clients_.find(target_host); + if (search == rpc_clients_.end() || !search->second->ok()) { + if (search == rpc_clients_.end()) { + SPDLOG_INFO("Create a RPC client to {}", target_host.data()); + } else if (!search->second->ok()) { + SPDLOG_INFO("Prior RPC client to {} is not OK. Re-create one", target_host); + } + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( + target_host, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + client = std::make_shared(completion_queue_, channel, need_heartbeat); + rpc_clients_.insert_or_assign(target_host, client); + } else { + client = search->second; + } + } + + return client; +} + +void ClientInstance::addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) { + { + absl::MutexLock lock(&rpc_clients_mtx_); + rpc_clients_.insert_or_assign(target_host, client); + } +} + +SendResult ClientInstance::processSendResponse(const MQMessageQueue& message_queue, + const SendMessageResponse& response) { + if (google::rpc::Code::OK != response.common().status().code()) { + THROW_MQ_EXCEPTION(MQClientException, response.common().DebugString(), response.common().status().code()); + } + SendResult send_result; + send_result.setSendStatus(SendStatus::SEND_OK); + send_result.setMsgId(response.message_id()); + send_result.setQueueOffset(-1); + send_result.setMessageQueue(message_queue); + send_result.setTransactionId(response.transaction_id()); + return send_result; +} + +void ClientInstance::addClientObserver(std::weak_ptr client) { + absl::MutexLock lk(&clients_mtx_); + clients_.emplace_back(std::move(client)); +} + +void ClientInstance::resolveRoute(const std::string& target_host, + const absl::flat_hash_map& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + + RpcClientSharedPtr client = getRpcClient(target_host, false); + if (!client) { + SPDLOG_WARN("Failed to create RPC client for name server[host={}]", target_host); + cb(false, nullptr); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& client_context, + const QueryRouteResponse& response) { + if (!status.ok()) { + SPDLOG_WARN("Failed to send query route request to server[host={}]. Reason: {}", target_host, + status.error_message()); + cb(false, nullptr); + return; + } + + if (google::rpc::Code::OK != response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query route request. Reason: {}", target_host, + response.common().DebugString()); + cb(false, nullptr); + return; + } + + auto& partitions = response.partitions(); + + std::vector topic_partitions; + for (const auto& partition : partitions) { + Topic t(partition.topic().arn(), partition.topic().name()); + + auto& broker = partition.broker(); + AddressScheme scheme = AddressScheme::IPv4; + switch (broker.endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + break; + } + + std::vector
addresses; + for (const auto& address : broker.endpoints().addresses()) { + addresses.emplace_back(Address{address.host(), address.port()}); + } + ServiceAddress service_address(scheme, addresses); + Broker b(partition.broker().name(), partition.broker().id(), service_address); + + Permission permission = Permission::READ_WRITE; + switch (partition.permission()) { + case rmq::Permission::READ: + permission = Permission::READ; + break; + + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + break; + } + Partition topic_partition(t, partition.id(), permission, std::move(b)); + topic_partitions.emplace_back(std::move(topic_partition)); + } + + auto ptr = std::make_shared(std::move(topic_partitions), response.DebugString()); + cb(true, ptr); + }; + invocation_context->callback_ = callback; + client->asyncQueryRoute(request, invocation_context); +} + +void ClientInstance::queryAssignment(const std::string& target, + const absl::flat_hash_map& metadata, + const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("Prepare to send query assignment request to broker[address={}]", target); + std::shared_ptr client = getRpcClient(target); + + auto callback = [&, target, cb](const grpc::Status& status, const grpc::ClientContext& context, + const QueryAssignmentResponse& response) { + if (!status.ok()) { + SPDLOG_WARN("Failed to query assignment. Reason: {}", status.error_message()); + cb(false, response); + return; + } + + if (google::rpc::Code::OK != response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query assignment request. Reason: {}", target, + response.common().DebugString()); + cb(false, response); + return; + } + + cb(true, response); + }; + + auto invocation_context = new InvocationContext(); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->callback_ = callback; + client->asyncQueryAssignment(request, invocation_context); +} + +void ClientInstance::receiveMessage(absl::string_view target, + const absl::flat_hash_map& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + std::shared_ptr& cb) { + std::string target_host(target.data(), target.length()); + SPDLOG_DEBUG("Prepare to pop message from {} asynchronously. Request: {}", target_host, request.DebugString()); + RpcClientSharedPtr client = getRpcClient(target_host); + + auto invocation_context = new InvocationContext(); + + if (!metadata.empty()) { + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + } + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + + auto callback = [this, cb, target_host](const grpc::Status& status, const grpc::ClientContext& client_context, + const ReceiveMessageResponse& response) { + if (status.ok()) { + SPDLOG_DEBUG("Received pop response through gRPC from brokerAddress={}", target_host); + ReceiveMessageResult receive_result; + this->processPopResult(client_context, response, receive_result, target_host); + cb->onSuccess(receive_result); + } else { + SPDLOG_WARN("Failed to pop messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", target_host, + status.error_code(), status.error_message()); + MQException e(status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, __LINE__); + cb->onException(e); + } + }; + invocation_context->callback_ = callback; + client->asyncReceive(request, invocation_context); +} + +void ClientInstance::pullMessage(absl::string_view target, absl::flat_hash_map& metadata, + const PullMessageRequest& request, + std::shared_ptr& receive_callback) { + std::string target_host(target.data(), target.length()); + SPDLOG_DEBUG("Prepare to pull message from {} asynchronously", target_host); + RpcClientSharedPtr client = getRpcClient(target_host); + auto invocation_context = new InvocationContext(); + if (!metadata.empty()) { + for (const auto& entry : metadata) { + invocation_context->context_.AddMetadata(entry.first, entry.second); + } + } + + auto callback = [this, receive_callback, target_host](const grpc::Status& status, + const grpc::ClientContext& client_context, + const PullMessageResponse& response) { + if (status.ok()) { + SPDLOG_DEBUG("Received message response through gRPC from brokerAddress={}", target_host); + ReceiveMessageResult receive_result; + this->processPullResult(client_context, response, receive_result, target_host); + receive_callback->onSuccess(receive_result); + } else { + SPDLOG_WARN("Failed to pull messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", target_host, + status.error_code(), status.error_message()); + MQException e(status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, __LINE__); + receive_callback->onException(e); + } + }; + invocation_context->callback_ = callback; + client->asyncPull(request, invocation_context); +} + +void ClientInstance::processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, + ReceiveMessageResult& result, absl::string_view target_host) { + // process response to result + ReceiveMessageStatus status; + std::string broker_address = std::string(target_host.data(), target_host.length()); + switch (response.common().status().code()) { + case google::rpc::Code::OK: + status = ReceiveMessageStatus::OK; + break; + case google::rpc::Code::RESOURCE_EXHAUSTED: + status = ReceiveMessageStatus::RESOURCE_EXHAUSTED; + SPDLOG_WARN("Too many pop requests in broker. Long polling is full in the broker side. BrokerAddress={}", + broker_address); + break; + case google::rpc::Code::DEADLINE_EXCEEDED: + status = ReceiveMessageStatus::DEADLINE_EXCEEDED; + break; + case google::rpc::Code::NOT_FOUND: + status = ReceiveMessageStatus::NOT_FOUND; + break; + default: + SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", broker_address, + response.common().DebugString()); + status = ReceiveMessageStatus::INTERNAL; + break; + } + + const auto& metadata = client_context.GetServerInitialMetadata(); + auto search = metadata.find(Metadata::REQUEST_ID_KEY); + if (metadata.end() == search) { + SPDLOG_WARN("Expected header {} is missing", Metadata::AUTHORIZATION); + } else { + result.requestId(std::string(search->second.data(), search->second.length())); + } + + result.sourceHost(broker_address); + + std::vector msg_found_list; + if (ReceiveMessageStatus::OK == status) { + for (auto& item : response.messages()) { + MQMessageExt message_ext; + if (wrapMessage(item, message_ext)) { + MessageAccessor::setTargetEndpoint(message_ext, broker_address); + msg_found_list.emplace_back(message_ext); + } + } + } + + result.status(status); + auto& delivery_timestamp = response.delivery_timestamp(); + timeval tv{}; + tv.tv_sec = delivery_timestamp.seconds(); + tv.tv_usec = delivery_timestamp.nanos() / 1000; + result.setPopTime(absl::TimeFromTimeval(tv)); + + const auto& protobuf_invisible_duration = response.invisible_duration(); + absl::Duration invisible_period = + absl::Seconds(protobuf_invisible_duration.seconds()) + absl::Nanoseconds(protobuf_invisible_duration.nanos()); + result.invisibleTime(invisible_period); + result.setMsgFoundList(msg_found_list); +} + +void ClientInstance::processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, + ReceiveMessageResult& result, absl::string_view target_host) { + if (google::rpc::Code::OK != response.common().status().code()) { + result.status_ = ReceiveMessageStatus::INTERNAL; + return; + } + result.sourceHost(std::string(target_host.data(), target_host.length())); + switch (response.common().status().code()) { + case google::rpc::Code::OK: { + assert(!response.messages().empty()); + result.status_ = ReceiveMessageStatus::OK; + for (const auto& item : response.messages()) { + MQMessageExt message; + if (!wrapMessage(item, message)) { + result.status_ = ReceiveMessageStatus::DATA_CORRUPTED; + return; + } + result.messages_.emplace_back(message); + } + } break; + + case google::rpc::Code::DEADLINE_EXCEEDED: + result.status_ = ReceiveMessageStatus::DEADLINE_EXCEEDED; + break; + + case google::rpc::Code::RESOURCE_EXHAUSTED: + result.status_ = ReceiveMessageStatus::RESOURCE_EXHAUSTED; + break; + + case google::rpc::Code::OUT_OF_RANGE: + result.status_ = ReceiveMessageStatus::OUT_OF_RANGE; + result.next_offset_ = response.next_offset(); + break; + } +} + +bool ClientInstance::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { + assert(item.topic().arn() == arn_); + + // base + message_ext.setTopic(item.topic().name()); + + const auto& system_attributes = item.system_attribute(); + + // Tag + message_ext.setTags(system_attributes.tag()); + + // Keys + std::vector keys; + for (const auto& key : system_attributes.keys()) { + keys.push_back(key); + } + message_ext.setKeys(keys); + + // Message-Id + MessageAccessor::setMessageId(message_ext, system_attributes.message_id()); + + // Validate body digest + const rmq::Digest& digest = system_attributes.body_digest(); + bool body_digest_match = false; + if (item.body().empty()) { + SPDLOG_WARN("Body of message[topic={}, msgId={}] is empty", item.topic().name(), + item.system_attribute().message_id()); + body_digest_match = true; + } else { + switch (digest.type()) { + case rmq::DigestType::CRC32: { + std::uint32_t crc = CRC::Calculate(item.body().data(), item.body().length(), CRC::CRC_32()); + SPDLOG_DEBUG("Compute and compare CRC32 checksum. Actual: {}, expect: {}", crc, digest.checksum()); + body_digest_match = (std::stoul(digest.checksum()) == crc); + break; + } + case rmq::DigestType::MD5: { + std::string checksum; + bool success = MixAll::md5(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("MD5 checksum validation passed."); + } else { + SPDLOG_WARN("Body MD5 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + } + } else { + SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); + body_digest_match = true; + } + break; + } + case rmq::DigestType::SHA1: { + std::string checksum; + bool success = MixAll::sha1(item.body(), checksum); + if (success) { + body_digest_match = (checksum == digest.checksum()); + if (body_digest_match) { + SPDLOG_DEBUG("SHA1 checksum validation passed"); + } else { + SPDLOG_WARN("Body SHA1 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + } + } else { + SPDLOG_WARN("Failed to calculate SHA1 digest. Skip."); + } + break; + } + default: { + SPDLOG_WARN("Unsupported message body digest algorithm"); + body_digest_match = true; + break; + } + } + } + + if (!body_digest_match) { + SPDLOG_WARN("Message body checksum failed. MsgId={}", system_attributes.message_id()); + // TODO: NACK it immediately + return false; + } + + // Body encoding + switch (system_attributes.body_encoding()) { + case rmq::Encoding::GZIP: { + std::string uncompressed; + UtilAll::uncompress(item.body(), uncompressed); + message_ext.setBody(uncompressed); + break; + } + case rmq::Encoding::SNAPPY: { + SPDLOG_WARN("Snappy encoding is not supported yet"); + break; + } + case rmq::Encoding::IDENTITY: { + message_ext.setBody(item.body()); + break; + } + default: { + SPDLOG_WARN("Unsupported encoding algorithm"); + break; + } + } + + timeval tv{.tv_sec = 0, .tv_usec = 0}; + + // Message-type + MessageType message_type; + switch (system_attributes.message_type()) { + case rmq::MessageType::NORMAL: + message_type = MessageType::NORMAL; + break; + case rmq::MessageType::FIFO: + message_type = MessageType::FIFO; + break; + case rmq::MessageType::DELAY: + message_type = MessageType::DELAY; + break; + case rmq::MessageType::TRANSACTION: + message_type = MessageType::TRANSACTION; + break; + default: + SPDLOG_WARN("Unknown message type. Treat it as normal message"); + message_type = MessageType::NORMAL; + break; + } + MessageAccessor::setMessageType(message_ext, message_type); + + // Born-timestamp + if (system_attributes.has_born_timestamp()) { + tv.tv_sec = system_attributes.born_timestamp().seconds(); + tv.tv_usec = system_attributes.born_timestamp().nanos() / 1000; + auto born_timestamp = absl::TimeFromTimeval(tv); + MessageAccessor::setBornTimestamp(message_ext, born_timestamp); + } + + // Born-host + MessageAccessor::setBornHost(message_ext, system_attributes.born_host()); + + // Store-timestamp + if (system_attributes.has_store_timestamp()) { + tv.tv_sec = system_attributes.store_timestamp().seconds(); + tv.tv_usec = system_attributes.store_timestamp().nanos() / 1000; + MessageAccessor::setStoreTimestamp(message_ext, absl::TimeFromTimeval(tv)); + } + + // Store-host + MessageAccessor::setStoreHost(message_ext, system_attributes.store_host()); + + // Process one-of: delivery-timestamp and delay-level. + switch (system_attributes.timed_delivery_case()) { + case rmq::SystemAttribute::TimedDeliveryCase::kDelayLevel: { + message_ext.setDelayTimeLevel(system_attributes.delay_level()); + break; + } + + case rmq::SystemAttribute::TimedDeliveryCase::kDeliveryTimestamp: { + tv.tv_sec = system_attributes.delivery_timestamp().seconds(); + tv.tv_usec = system_attributes.delivery_timestamp().nanos(); + MessageAccessor::setDeliveryTimestamp(message_ext, absl::TimeFromTimeval(tv)); + break; + } + + default: + break; + } + + // Receipt-handle + MessageAccessor::setReceiptHandle(message_ext, system_attributes.receipt_handle()); + + // Partition-id + MessageAccessor::setQueueId(message_ext, system_attributes.partition_id()); + + // Partition-offset + MessageAccessor::setQueueOffset(message_ext, system_attributes.partition_offset()); + + // Invisible-period + if (system_attributes.has_invisible_period()) { + absl::Duration invisible_period = absl::Seconds(system_attributes.invisible_period().seconds()) + + absl::Nanoseconds(system_attributes.invisible_period().nanos()); + MessageAccessor::setInvisiblePeriod(message_ext, invisible_period); + } + + // Delivery Count + MessageAccessor::setDeliveryCount(message_ext, system_attributes.delivery_count()); + + // Trace-context + MessageAccessor::setTraceContext(message_ext, system_attributes.trace_context()); + + // Decoded Time-Point + MessageAccessor::setDecodedTimestamp(message_ext, absl::Now()); + + // User-properties + std::map properties; + for (const auto& it : item.user_attribute()) { + properties.insert(std::make_pair(it.first, it.second)); + } + message_ext.setProperties(properties); + + // Extension + { + auto elapsed = static_cast(absl::ToUnixMillis(absl::Now()) - message_ext.getStoreTimestamp()); + if (elapsed >= 0) { + latency_histogram_.countIn(elapsed / 20); + } + } + return true; +} + +Scheduler& ClientInstance::getScheduler() { return scheduler_; } + +void ClientInstance::ack(const std::string& target, const absl::flat_hash_map& metadata, + const AckMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& completion_callback) { + std::string target_host(target.data(), target.length()); + SPDLOG_DEBUG("Prepare to ack message against {} asynchronously. AckMessageRequest: {}", target_host, + request.DebugString()); + RpcClientSharedPtr client = getRpcClient(target_host); + + auto invocation_context = new InvocationContext(); + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + // TODO: Use capture by move and pass-by-value paradigm when C++ 14 is available. + auto callback = [target_host, request, completion_callback](const grpc::Status& status, + const grpc::ClientContext& client_context, + const AckMessageResponse& response) { + if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { + completion_callback(true); + } else { + completion_callback(false); + } + }; + invocation_context->callback_ = callback; + client->asyncAck(request, invocation_context); +} + +void ClientInstance::nack(const std::string& target_host, const absl::flat_hash_map& metadata, + const NackMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& completion_callback) { + RpcClientSharedPtr client = getRpcClient(target_host); + assert(client); + auto invocation_context = new InvocationContext(); + + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [completion_callback](const grpc::Status& status, const grpc::ClientContext& context, + const NackMessageResponse& response) { + if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { + completion_callback(true); + } else { + completion_callback(false); + } + }; + invocation_context->callback_ = callback; + client->asyncNack(request, invocation_context); +} + +void ClientInstance::endTransaction(const std::string& target_host, + const absl::flat_hash_map& metadata, + const EndTransactionRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + RpcClientSharedPtr client = getRpcClient(target_host); + if (!client) { + SPDLOG_WARN("No RPC client for {}", target_host); + EndTransactionResponse response; + cb(false, response); + return; + } + + SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); + + auto invocation_context = new InvocationContext(); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + // Set RPC deadline. + auto deadline = std::chrono::system_clock::now() + timeout; + invocation_context->context_.set_deadline(deadline); + + auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, + const EndTransactionResponse& response) { + if (!status.ok() || google::rpc::Code::OK != response.common().status().code()) { + SPDLOG_WARN("Failed to endTransaction. TargetHost={}, gRPC statusCode={}, errorMessage={}", target_host.data(), + status.error_message(), status.error_message()); + cb(false, response); + return; + } + + SPDLOG_DEBUG("endTransaction completed OK. Response: {}", response.DebugString()); + cb(true, response); + }; + invocation_context->callback_ = callback; + client->asyncEndTransaction(request, invocation_context); +} + +void ClientInstance::multiplexingCall(const std::string& target_host, + const absl::flat_hash_map& metadata, + const MultiplexingRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + RpcClientSharedPtr client = getRpcClient(target_host); + if (!client) { + SPDLOG_WARN("No RPC client for {}", target_host); + MultiplexingResponse response; + cb(false, response); + return; + } + + SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); + + auto invocation_context = new InvocationContext(); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + // Set RPC deadline. + auto deadline = std::chrono::system_clock::now() + timeout; + invocation_context->context_.set_deadline(deadline); + + auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, + const MultiplexingResponse& response) { + if (!status.ok()) { + SPDLOG_WARN("Failed to apply multiplexing-call. TargetHost={}, gRPC statusCode={}, errorMessage={}", + target_host.data(), status.error_message(), status.error_message()); + cb(false, response); + return; + } + + SPDLOG_DEBUG("endTransaction completed OK. Response: {}", response.DebugString()); + cb(true, response); + }; + invocation_context->callback_ = callback; + client->asyncMultiplexingCall(request, invocation_context); +} + +void ClientInstance::pullMessage(const std::string& target_host, + const absl::flat_hash_map& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + auto client = getRpcClient(target_host); + if (!client) { + PullMessageResponse response; + cb(false, response); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& client_context, + const PullMessageResponse& response) { + if (!status.ok()) { + SPDLOG_WARN("Failed to send pullMessage request to {}", target_host); + cb(false, response); + return; + } + + SPDLOG_DEBUG("Received pullMessage response from server[host={}]", target_host); + cb(true, response); + }; + invocation_context->callback_ = callback; + + client->asyncPull(request, invocation_context); +} + +void ClientInstance::logStats() { + std::string stats; + latency_histogram_.reportAndReset(stats); + SPDLOG_INFO("{}", stats); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManager.cpp b/src/main/cpp/client/ClientManager.cpp new file mode 100644 index 000000000..5ff9c7e9a --- /dev/null +++ b/src/main/cpp/client/ClientManager.cpp @@ -0,0 +1,40 @@ +#include "ClientManager.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ClientManager& ClientManager::getInstance() { + static ClientManager instance; + return instance; +} + +ClientInstancePtr ClientManager::getClientInstance(const ClientConfig& client_config) { + { + absl::MutexLock lock(&client_instance_table_mtx_); + auto search = client_instance_table_.find(client_config.arn()); + if (search != client_instance_table_.end()) { + ClientInstancePtr client_instance = search->second.lock(); + if (client_instance) { + SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.arn()); + return client_instance; + } else { + client_instance_table_.erase(client_config.arn()); + } + } + ClientInstancePtr client_instance = std::make_shared(client_config.arn()); + std::weak_ptr client_instance_weak_ptr(client_instance); + client_instance_table_.insert_or_assign(client_config.arn(), client_instance_weak_ptr); + SPDLOG_INFO("Created new client_instance[ARN={}]", client_config.arn()); + return client_instance; + } +} + +void ClientManager::addClientInstance(const std::string& arn, const ClientInstancePtr& client_instance) { + absl::MutexLock lk(&client_instance_table_mtx_); + client_instance_table_.insert_or_assign(arn, std::weak_ptr(client_instance)); +} + +ClientManager::ClientManager() : admin_server_(admin::AdminFacade::getServer()) { admin_server_.start(); } + +ClientManager::~ClientManager() { admin_server_.stop(); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp new file mode 100644 index 000000000..7238929b9 --- /dev/null +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -0,0 +1,52 @@ +#include "LogInterceptor.h" +#include "InterceptorContinuation.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" +#include "google/protobuf/message.h" +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* methods) { + InterceptorContinuation continuation(methods); + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { + std::multimap* metadata = methods->GetSendInitialMetadata(); + if (metadata) { + SPDLOG_DEBUG("Request Headers of {}:\n{}", client_rpc_info_->method(), + absl::StrJoin(*metadata, "\n", absl::PairFormatter(" --> "))); + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { + const void* message = methods->GetSendMessage(); + if (message) { + const auto* request = reinterpret_cast(message); + SPDLOG_DEBUG("[Outbound] {}\n{}", client_rpc_info_->method(), request->DebugString()); + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) { + std::multimap* metadata = methods->GetRecvInitialMetadata(); + if (metadata) { + absl::flat_hash_map response_headers; + for (const auto & it : *metadata) { + response_headers.insert({absl::string_view(it.first.data(), it.first.length()), + absl::string_view(it.second.data(), it.second.length())}); + } + SPDLOG_DEBUG("Response Headers of {}:\n{}", client_rpc_info_->method(), + absl::StrJoin(response_headers, "\n", absl::PairFormatter(" --> "))); + } + } + + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) { + void* message = methods->GetRecvMessage(); + if (message) { + auto* response = reinterpret_cast(message); + SPDLOG_DEBUG("[Inbound] {}\n{}", client_rpc_info_->method(), response->DebugString()); + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/LogInterceptorFactory.cpp b/src/main/cpp/client/LogInterceptorFactory.cpp new file mode 100644 index 000000000..23c8d8388 --- /dev/null +++ b/src/main/cpp/client/LogInterceptorFactory.cpp @@ -0,0 +1,11 @@ +#include "LogInterceptorFactory.h" +#include "LogInterceptor.h" + +ROCKETMQ_NAMESPACE_BEGIN + +grpc::experimental::Interceptor* +LogInterceptorFactory::CreateClientInterceptor(grpc::experimental::ClientRpcInfo* info) { + return new LogInterceptor(info); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Metadata.cpp b/src/main/cpp/client/Metadata.cpp new file mode 100644 index 000000000..2f97cef9f --- /dev/null +++ b/src/main/cpp/client/Metadata.cpp @@ -0,0 +1,19 @@ +#include "Metadata.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const char* Metadata::TENANT_ID_KEY = "x-mq-tenant-id"; +const char* Metadata::ARN_KEY = "x-mq-arn"; +const char* Metadata::AUTHORIZATION = "authorization"; +const char* Metadata::DATE_TIME_KEY = "x-mq-date-time"; +const char* Metadata::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; +const char* Metadata::CREDENTIAL_KEY = "Credential"; +const char* Metadata::SIGNED_HEADERS_KEY = "SignedHeaders"; +const char* Metadata::SIGNATURE_KEY = "Signature"; +const char* Metadata::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; +const char* Metadata::LANGUAGE_KEY = "x-mq-language"; +const char* Metadata::CLIENT_VERSION_KEY = "x-mq-client-version"; +const char* Metadata::PROTOCOL_VERSION_KEY = "x-mq-protocol-version"; +const char* Metadata::REQUEST_ID_KEY = "x-mq-request-id"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp new file mode 100644 index 000000000..b2311405e --- /dev/null +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -0,0 +1,143 @@ +#include "RpcClientImpl.h" + +#include + +#include "ClientConfig.h" +#include "TlsHelper.h" +#include "absl/time/time.h" + +using ClientContext = grpc::ClientContext; + +ROCKETMQ_NAMESPACE_BEGIN + +void RpcClientImpl::asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader_ = + stub_->PrepareAsyncQueryRoute(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader_ = + stub_->PrepareAsyncSendMessage(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader_ = + stub_->PrepareAsyncQueryAssignment(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { return completion_queue_; } + +void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader_ = + stub_->PrepareAsyncReceiveMessage(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncAck(const AckMessageRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncAckMessage(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncNackMessage(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncHeartbeat(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncHealthCheck(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncEndTransaction(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +bool RpcClientImpl::ok() const { + return channel_ && grpc_connectivity_state::GRPC_CHANNEL_SHUTDOWN != channel_->GetState(false); +} + +void RpcClientImpl::addMetadata(grpc::ClientContext& context, + const absl::flat_hash_map& metadata) { + for (const auto& entry : metadata) { + context.AddMetadata(entry.first, entry.second); + } +} + +bool RpcClientImpl::needHeartbeat() { return need_heartbeat_; } + +void RpcClientImpl::asyncMultiplexingCall(const MultiplexingRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncMultiplexingCall(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) { + assert(invocation_context); + invocation_context->response_reader_ = + stub_->PrepareAsyncQueryOffset(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +void RpcClientImpl::asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader_ = + stub_->PrepareAsyncPullMessage(&invocation_context->context_, request, completion_queue_.get()); + invocation_context->response_reader_->StartCall(); + invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, + invocation_context); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp new file mode 100644 index 000000000..e4569aff1 --- /dev/null +++ b/src/main/cpp/client/Signature.cpp @@ -0,0 +1,66 @@ +#include "Signature.h" +#include "Metadata.h" +#include "Protocol.h" +#include "RequestIdGenerator.h" +#include "TlsHelper.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void Signature::sign(ClientConfig* client, absl::flat_hash_map& metadata) { + assert(client); + + auto search = metadata.find(Metadata::REQUEST_ID_KEY); + if (search == metadata.end()) { + metadata.insert({Metadata::REQUEST_ID_KEY, RequestIdGenerator::nextRequestId()}); + } + + metadata.insert({Metadata::LANGUAGE_KEY, "CPP"}); + // Add common headers + metadata.insert({Metadata::CLIENT_VERSION_KEY, ClientConfig::CLIENT_VERSION}); + metadata.insert({Metadata::PROTOCOL_VERSION_KEY, Protocol::PROTOCOL_VERSION}); + + if (!client->tenantId().empty()) { + metadata.insert({Metadata::TENANT_ID_KEY, client->tenantId()}); + } + + if (!client->arn().empty()) { + metadata.insert({Metadata::ARN_KEY, client->arn()}); + } + + absl::Time now = absl::Now(); + absl::TimeZone utc_time_zone = absl::UTCTimeZone(); + const std::string request_date_time = absl::FormatTime(Metadata::DATE_TIME_FORMAT, now, utc_time_zone); + metadata.insert({Metadata::DATE_TIME_KEY, request_date_time}); + + if (client->credentialsProvider()) { + Credentials&& credentials = client->credentialsProvider()->getCredentials(); + if (credentials.accessKey().empty() || credentials.accessSecret().empty()) { + SPDLOG_WARN("Access credential is incomplete. Check your access key/secret."); + return; + } + + std::string authorization; + authorization.append(Metadata::ALGORITHM_KEY) + .append(" ") + .append(Metadata::CREDENTIAL_KEY) + .append("=") + .append(credentials.accessKey()) + .append("/") + .append(client->region()) + .append("/") + .append(client->serviceName()) + .append(", ") + .append(Metadata::SIGNED_HEADERS_KEY) + .append("=") + .append(Metadata::DATE_TIME_KEY) + .append(", ") + .append(Metadata::SIGNATURE_KEY) + .append("=") + .append(TlsHelper::sign(credentials.accessSecret(), request_date_time)); + SPDLOG_DEBUG("Add authorization header: {}", authorization); + metadata.insert({Metadata::AUTHORIZATION, authorization}); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TlsHelper.cpp b/src/main/cpp/client/TlsHelper.cpp new file mode 100644 index 000000000..aaa0cd6b8 --- /dev/null +++ b/src/main/cpp/client/TlsHelper.cpp @@ -0,0 +1,233 @@ +#include "TlsHelper.h" + +#include "MixAll.h" +#include "OpenSSLCompatible.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +std::string TlsHelper::sign(const std::string& access_secret, const std::string& content) { + HMAC_CTX* ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, access_secret.c_str(), access_secret.length(), EVP_sha1(), nullptr); + HMAC_Update(ctx, reinterpret_cast(content.c_str()), content.length()); + auto result = new unsigned char[EVP_MD_size(EVP_sha1())]; + unsigned int len; + HMAC_Final(ctx, result, &len); + HMAC_CTX_free(ctx); + + std::string hex_str = MixAll::hex(result, len); + delete[] result; + return hex_str; +} + +const char* TlsHelper::CA = R"( +-----BEGIN CERTIFICATE----- +MIIGCDCCA/CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAkNO +MRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UE +CgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEu +YXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcw +HhcNMjEwNDI1MDkwMjE5WhcNMzEwNDIzMDkwMjE5WjCBiDELMAkGA1UEBhMCQ04x +EjAQBgNVBAgMCVpoZSBKaWFuZzEPMA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhS +b2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3 +DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDLL1rQ3A6YnWwdAT3rcf7wXK2DN6AwkWfGexp5o8y+dPj49D5b +PhEVIDEjidtWASFfCunJq9Ku48K/U8tha58JPL2CJ3773Mw7CU3LwD46Ft32+kGM +3+rXuXi44Fs714U/jJkxA6AsJdl4VtgjfXGWhbTq7ZT//l3tECH7Q096cp4xXATx +0RuvlaE33k8SpOPB+oHyjdFTwSW9HuZx71dLNCoZ0b52teJDVJFMACz6R5t3ZglF +MUosUzEUKdCXqBDvjOzMI2GHr7NYKAWHnOPNcYbf8jKzQ5msW+1MOfacVX+YUmKr +4Tf8NZHTSA4MuSNZ46gVFK7tYau2FDAkBWSb4hnuqqoGn58QtwrRBWtCmnLc4133 +6sgEGoJJR5M7WF0usl7KAgqgwy6xdU1joHO4SzvpysQ9kHWr1hcTqpeLD/svxLpZ +3x0WrhIAQoyd4uIHv/501LgD2KE5YnBa4S10LYrZeQpoJO5YV56HJdyvqNgVh2fn +tIRJAKq/1Wvw6izK/zOC6ZC+ug3n9vPqCgpBI0NFm3aR4AcSx70FsmlfrjUAsluL +kIzQwuu3Ip7hUS9kBVUxmvaD2yy2HPc+UPiYm4Y2YxqTx2j11APbN32M8rj1mroE +d9JFUV3WjbY+1QEAQ9upWTJLnOzS4a8yNsXIdMoKeU68Lyi/F3wA9ciK9wIDAQAB +o2YwZDAdBgNVHQ4EFgQUebBfwZjpYrRBUX9XL89qA0LzaXEwHwYDVR0jBBgwFoAU +3NYFR3h8OuGYCHVl1gk2yFoM8LIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B +Af8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJdwY+mAgtdAkVB8gRwcql7YdmWM +vlc3OYr14DHQT8eN5ItGou8MvTUR3wxCz72788/GHG1dLl54cnxzWjLgZZdapfQL +hsl1Iz6JSzyMTrNZ0HiuRw9J5XnRUIhWAO9fpRV4f/ce9J4o9YMFt//GW9WJrL59 +860gNL+L1IaWDWdnPlTYnQc3ndhnStelURANtsDp808t5RO8w94/Tp/6+KgqXwSj +uAxeHNVoMcNcr/Cff/fkLXQjSlSnpX7+Yf4y9pPSq7z6QHdsfSVq9FL51cXUdK5W +XRzkWK2QKp82CRz2jbrXd9Q7rjrWtDq8vgpKsZg+myLd55wPjiscM/vZmf4LjjFC +ubfKrclMh9CFtSsk38p21l3MBl4hyULsqVHAvz4jMrTaNB+1vYQ4OhgiqxB1KJff +hw2sHZfthHLm8W+mOma/LwA+u4QFE3ihq6bjgAZxte56HUuQwa7y4FifFcZi05ih +CnXNRo8Q8napmPTwtG9XKk9HQwLSMH+wTMnrV9R8Nt4ZkPlsJV8uY/zKOfOGHsY/ ++nR5vQ046Y8MT9z26dZe5MUsGUf9706+yAQAQAwzZiYJ+HiWijAIVynITMAZuEh9 +JfwS66kGVNOlwqMkbAEO61tuGFmy5iRBnXOoUM2mbaGVvnbS5iBCkH6M69oq5vip +42zKYBLmpEvL4PdS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGIDCCBAigAwIBAgIJAN/jxbRuUfSjMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYDVQQHDAlIYW5nIFpob3Ux +DzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0TVExHDAaBgNVBAMME3Jv +Y2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEWFGxpemhhbmh1aUBhcGFj +aGUub3JnMB4XDTIxMDQyNTA4NTMyMVoXDTQxMDQyMDA4NTMyMVowgZwxCzAJBgNV +BAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEP +MA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9j +a2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNo +ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDWKlLdC6uqiPA9 +egWqBxAmKCB47ALsh7gO0nxa/0Mkr/QsRfvUtRHMoTqxLJ/IiFXmy5ZHtXq6VppL +CWE3cjN6nbYhxAyP5zmXYLQVf3BAwRqGl/ZVfZLEUvgLACMTpLTNbsqtF90UVF1K +txdsfpfKgyYp5Sff0rll94enuOfoAk18oI6ukxw5rMR3vqk7XYWdvqbEfPKr3Hiw +6r7B/JxFLv+FJIdmmHb4dgQ26Jcs9B6e2MKLXm1LRtNBI9ko6eE6UZmYkK22D/YZ +T65o8j+ZxMQPHEI/evNZp+5PfV+/061d+dtC+qgqmUTdYREmotfYtdFDNNYhZuou +GuvIG4yb02I8bYkz9ZjcWrSlLkjsODQGlF3iAy7k8HX7e952D7W/ZtXcU/1iQNVn +o0Q27QAyuTlWlBcsmSsWBiJWo6FXUDz4ODtPEXnNLNHVcRTq4EIxmMada+0nSSOu +S39XKVGiPpC/4nzxtL3kD/DAw1loTEyJxz5QNOKm34IacCbmDlJ0eqi9vPMy7Fl7 +VZ5hc/yfFh1FEUlkuc4oQJftbZbUnd6osZjX9AJVX3yW+NgYtezInMhma2sGy4wn +ie2x0C8TxXF5dRrgzCk/cka3rp/InFkWx7OlJ3QLkYI6YIzbSQuF9xzJK5zFoMGP +rBCylmS1f+Xf2EbXfpek/ZZLBnzmHwIDAQABo2MwYTAdBgNVHQ4EFgQU3NYFR3h8 +OuGYCHVl1gk2yFoM8LIwHwYDVR0jBBgwFoAU3NYFR3h8OuGYCHVl1gk2yFoM8LIw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggIBAAUCkPkzYeXNluXxrJnQowDsi9z8klpD1XkgB3e8j4eOMiqby1TfPIQVRZS9 +d18iENpMFszF8fwEp0mMYhH4amHvL12qjdiepeYV57HmYUWeJIxvp/6xaJjcYJLE +jJILlNdDvZdKDwJZ2iCvV95KCZ0sp3kFgI1ZHnbLPuGw79lNAwVIfp2+/2QEq6Fu +TYEeWAbTaIQaUpQr2DpJShKkri4crdTN780dsfeTV0behD9MFmJPtpxL678KbF8a +j/A6lFWuts7/uwNXV+SR4wJMJGcxU1cRCsB99NGc0KIEZR92OiOg7U9cDdw5s448 +yhm6+9zk2FKobXkBp25IaCyiwmHo8JP0Ht8sCKIORKGnXWKmwhQmZDIty+d+Jwi+ +EuLnYEmgepgm4PYfehLvPo6krJrsyqfytJRcEyvlnWWobYv2S7jvpEcii/7huFqc +tBaxBXqLiIYxCWnygImeViquYBTu8jv87QfA8zCLSYVWWMitNnZE/Cw6RiCdlXdf +SIxY0S364T4z3zoO4i2Tl3dtgMb8qf6plvVe2mgbZxkXvkeSzGUJlvpovV2gW8jD ++agCIhmMjuYuBTsfWyj5cAfibcZSKb0xzehRUTtzX+Uc7GCiX8P5u2tgDBnxQUzM +dpafYF4v5HJmOyUNoNWdX6DhysaUK/sq4so8fW9vjgTQ5o2r +-----END CERTIFICATE-----)"; + +const char* TlsHelper::client_certificate_chain = R"( +-----BEGIN CERTIFICATE----- +MIIGDjCCA/agAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAkNO +MRIwEAYDVQQIDAlaaGUgSmlhbmcxDzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwI +Um9ja2V0TVExHDAaBgNVBAMME3JvY2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG +9w0BCQEWFGxpemhhbmh1aUBhcGFjaGUub3JnMB4XDTIxMDQyNTEyMjIxOVoXDTMx +MDQyMzEyMjIxOVowgZ4xCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcx +EjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UECgwGQWxpeXVuMQswCQYDVQQLDAJN +UTEdMBsGA1UEAwwUY2xpZW50Lm1xLmFsaXl1bi5jb20xKjAoBgkqhkiG9w0BCQEW +G3NodXRpYW4ubHpoQGFsaWJhYmEtaW5jLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALNtRLFYGNpCzIPKC3wHMxxtPOWFl5Wl5wj9TZLYntyQ+h5C +x6OaZzdqCIconAqM+F+glShoe9y4MQn4tS4cg5UpwfJjAD2XJiri56S8L2tB5t45 +5mTUF3iMoncq07NQZdWwVJcrkdxawwNLmBJjdfnALrCLrK8gejtsa/eClLArR3Ep +WlgSchxTvCWduYmb8bJFfHP5dZ1aT4rXJkUA+lDvm73N2B06f5HDazvNSxXqQ8AY +DnqKXPkUkA53gOgNXRwmY6TFfQLp3qzxD13ueUuUC/zonn4EifTdcp6xsYLjv+Fd +Fd0feGTHfeGi1Pp9nvogYbGzPeC7CaSrLphuCl0CAwEAAaOCAWgwggFkMAkGA1Ud +EwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NM +IEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO40rO1cUjC4 +CvSeLx+79vnzlXVFMIHKBgNVHSMEgcIwgb+AFHmwX8GY6WK0QVF/Vy/PagNC82lx +oYGipIGfMIGcMQswCQYDVQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYD +VQQHDAlIYW5nIFpob3UxDzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0 +TVExHDAaBgNVBAMME3JvY2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEW +FGxpemhhbmh1aUBhcGFjaGUub3JnggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0l +BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBAJtAPRUp3pLU+u860tZR +315Kxv2UHf/Mz+hqAa5N7xuojVq61VkcK+Q5SkV88B1Male3aadQcRjddGdDPTMX +c/mw6mZ43xuu2uPB47PRkCEbmwIpAiaY9nAKo0fQeHaM1roYtf20A864C0u76YsK +Gl+j8Gry0kYFMLEoVLjbdr4XtqkJOZCaHErsVjf83YSFPiyQaq6GpuHpGEyMoj+h +MM7Pcb5aqpehs9cRgYoU2IS2VXQZ6W71z5IHzw9xJngI8oZ94hwWzWyRBWl/u3jb +eNfVdh5baC9JjjJBUFedj5J8ayPl7Fh9e7YQDPhe2og42D9fWr+SmgM06QnU6fAG +s+n1ocKjpT+Rm+0Q9+BkKKebISrgP92cyiVpb2m7kCH2m52pnvEQ4GGXt5gVAHgA +UTRu0hDrkJtouu8lh5wpiu1oskgUgsYTKEpQkVB00Ich1pVxNVROWmLRHAJHVskl +MYcNAYihk4RVmyGwIEX37SWyMRKIjHY3loKJIwRJoMPduGXPk6E5NmhTRYSwKigo +pHjfDLXeAlTSYIEHUZMpTHNjigkNq5wSYYWs9BYoFaMg4k+n5m88cCsbkb9m+0b0 +BBtZdWGVKbQK0YkyS+p9UULVhYg82JaF/IHLm/yR0LcmrCUp7qK6iLlXxSSD3OVP +79fXjr+aaE5Mskph9yNKFAKi +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCDCCA/CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAkNO +MRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UE +CgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEu +YXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcw +HhcNMjEwNDI1MDkwMjE5WhcNMzEwNDIzMDkwMjE5WjCBiDELMAkGA1UEBhMCQ04x +EjAQBgNVBAgMCVpoZSBKaWFuZzEPMA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhS +b2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3 +DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDLL1rQ3A6YnWwdAT3rcf7wXK2DN6AwkWfGexp5o8y+dPj49D5b +PhEVIDEjidtWASFfCunJq9Ku48K/U8tha58JPL2CJ3773Mw7CU3LwD46Ft32+kGM +3+rXuXi44Fs714U/jJkxA6AsJdl4VtgjfXGWhbTq7ZT//l3tECH7Q096cp4xXATx +0RuvlaE33k8SpOPB+oHyjdFTwSW9HuZx71dLNCoZ0b52teJDVJFMACz6R5t3ZglF +MUosUzEUKdCXqBDvjOzMI2GHr7NYKAWHnOPNcYbf8jKzQ5msW+1MOfacVX+YUmKr +4Tf8NZHTSA4MuSNZ46gVFK7tYau2FDAkBWSb4hnuqqoGn58QtwrRBWtCmnLc4133 +6sgEGoJJR5M7WF0usl7KAgqgwy6xdU1joHO4SzvpysQ9kHWr1hcTqpeLD/svxLpZ +3x0WrhIAQoyd4uIHv/501LgD2KE5YnBa4S10LYrZeQpoJO5YV56HJdyvqNgVh2fn +tIRJAKq/1Wvw6izK/zOC6ZC+ug3n9vPqCgpBI0NFm3aR4AcSx70FsmlfrjUAsluL +kIzQwuu3Ip7hUS9kBVUxmvaD2yy2HPc+UPiYm4Y2YxqTx2j11APbN32M8rj1mroE +d9JFUV3WjbY+1QEAQ9upWTJLnOzS4a8yNsXIdMoKeU68Lyi/F3wA9ciK9wIDAQAB +o2YwZDAdBgNVHQ4EFgQUebBfwZjpYrRBUX9XL89qA0LzaXEwHwYDVR0jBBgwFoAU +3NYFR3h8OuGYCHVl1gk2yFoM8LIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B +Af8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJdwY+mAgtdAkVB8gRwcql7YdmWM +vlc3OYr14DHQT8eN5ItGou8MvTUR3wxCz72788/GHG1dLl54cnxzWjLgZZdapfQL +hsl1Iz6JSzyMTrNZ0HiuRw9J5XnRUIhWAO9fpRV4f/ce9J4o9YMFt//GW9WJrL59 +860gNL+L1IaWDWdnPlTYnQc3ndhnStelURANtsDp808t5RO8w94/Tp/6+KgqXwSj +uAxeHNVoMcNcr/Cff/fkLXQjSlSnpX7+Yf4y9pPSq7z6QHdsfSVq9FL51cXUdK5W +XRzkWK2QKp82CRz2jbrXd9Q7rjrWtDq8vgpKsZg+myLd55wPjiscM/vZmf4LjjFC +ubfKrclMh9CFtSsk38p21l3MBl4hyULsqVHAvz4jMrTaNB+1vYQ4OhgiqxB1KJff +hw2sHZfthHLm8W+mOma/LwA+u4QFE3ihq6bjgAZxte56HUuQwa7y4FifFcZi05ih +CnXNRo8Q8napmPTwtG9XKk9HQwLSMH+wTMnrV9R8Nt4ZkPlsJV8uY/zKOfOGHsY/ ++nR5vQ046Y8MT9z26dZe5MUsGUf9706+yAQAQAwzZiYJ+HiWijAIVynITMAZuEh9 +JfwS66kGVNOlwqMkbAEO61tuGFmy5iRBnXOoUM2mbaGVvnbS5iBCkH6M69oq5vip +42zKYBLmpEvL4PdS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGIDCCBAigAwIBAgIJAN/jxbRuUfSjMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYDVQQHDAlIYW5nIFpob3Ux +DzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0TVExHDAaBgNVBAMME3Jv +Y2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEWFGxpemhhbmh1aUBhcGFj +aGUub3JnMB4XDTIxMDQyNTA4NTMyMVoXDTQxMDQyMDA4NTMyMVowgZwxCzAJBgNV +BAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEP +MA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9j +a2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNo +ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDWKlLdC6uqiPA9 +egWqBxAmKCB47ALsh7gO0nxa/0Mkr/QsRfvUtRHMoTqxLJ/IiFXmy5ZHtXq6VppL +CWE3cjN6nbYhxAyP5zmXYLQVf3BAwRqGl/ZVfZLEUvgLACMTpLTNbsqtF90UVF1K +txdsfpfKgyYp5Sff0rll94enuOfoAk18oI6ukxw5rMR3vqk7XYWdvqbEfPKr3Hiw +6r7B/JxFLv+FJIdmmHb4dgQ26Jcs9B6e2MKLXm1LRtNBI9ko6eE6UZmYkK22D/YZ +T65o8j+ZxMQPHEI/evNZp+5PfV+/061d+dtC+qgqmUTdYREmotfYtdFDNNYhZuou +GuvIG4yb02I8bYkz9ZjcWrSlLkjsODQGlF3iAy7k8HX7e952D7W/ZtXcU/1iQNVn +o0Q27QAyuTlWlBcsmSsWBiJWo6FXUDz4ODtPEXnNLNHVcRTq4EIxmMada+0nSSOu +S39XKVGiPpC/4nzxtL3kD/DAw1loTEyJxz5QNOKm34IacCbmDlJ0eqi9vPMy7Fl7 +VZ5hc/yfFh1FEUlkuc4oQJftbZbUnd6osZjX9AJVX3yW+NgYtezInMhma2sGy4wn +ie2x0C8TxXF5dRrgzCk/cka3rp/InFkWx7OlJ3QLkYI6YIzbSQuF9xzJK5zFoMGP +rBCylmS1f+Xf2EbXfpek/ZZLBnzmHwIDAQABo2MwYTAdBgNVHQ4EFgQU3NYFR3h8 +OuGYCHVl1gk2yFoM8LIwHwYDVR0jBBgwFoAU3NYFR3h8OuGYCHVl1gk2yFoM8LIw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggIBAAUCkPkzYeXNluXxrJnQowDsi9z8klpD1XkgB3e8j4eOMiqby1TfPIQVRZS9 +d18iENpMFszF8fwEp0mMYhH4amHvL12qjdiepeYV57HmYUWeJIxvp/6xaJjcYJLE +jJILlNdDvZdKDwJZ2iCvV95KCZ0sp3kFgI1ZHnbLPuGw79lNAwVIfp2+/2QEq6Fu +TYEeWAbTaIQaUpQr2DpJShKkri4crdTN780dsfeTV0behD9MFmJPtpxL678KbF8a +j/A6lFWuts7/uwNXV+SR4wJMJGcxU1cRCsB99NGc0KIEZR92OiOg7U9cDdw5s448 +yhm6+9zk2FKobXkBp25IaCyiwmHo8JP0Ht8sCKIORKGnXWKmwhQmZDIty+d+Jwi+ +EuLnYEmgepgm4PYfehLvPo6krJrsyqfytJRcEyvlnWWobYv2S7jvpEcii/7huFqc +tBaxBXqLiIYxCWnygImeViquYBTu8jv87QfA8zCLSYVWWMitNnZE/Cw6RiCdlXdf +SIxY0S364T4z3zoO4i2Tl3dtgMb8qf6plvVe2mgbZxkXvkeSzGUJlvpovV2gW8jD ++agCIhmMjuYuBTsfWyj5cAfibcZSKb0xzehRUTtzX+Uc7GCiX8P5u2tgDBnxQUzM +dpafYF4v5HJmOyUNoNWdX6DhysaUK/sq4so8fW9vjgTQ5o2r +-----END CERTIFICATE----- +)"; + +const char* TlsHelper::client_private_key = R"( +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAs21EsVgY2kLMg8oLfAczHG085YWXlaXnCP1Nktie3JD6HkLH +o5pnN2oIhyicCoz4X6CVKGh73LgxCfi1LhyDlSnB8mMAPZcmKuLnpLwva0Hm3jnm +ZNQXeIyidyrTs1Bl1bBUlyuR3FrDA0uYEmN1+cAusIusryB6O2xr94KUsCtHcSla +WBJyHFO8JZ25iZvxskV8c/l1nVpPitcmRQD6UO+bvc3YHTp/kcNrO81LFepDwBgO +eopc+RSQDneA6A1dHCZjpMV9AunerPEPXe55S5QL/OiefgSJ9N1ynrGxguO/4V0V +3R94ZMd94aLU+n2e+iBhsbM94LsJpKsumG4KXQIDAQABAoIBAQCQigN+QZOzr4AE +4mwh+8LGsZEO7HFSUhUtfrBRDUPcNsXmPrGIL8faMgawqpwZO7qRflDp/lFZWt/W +Qo3hmYK+VKcyVWJKO0q53ZPxds9UPgB3IHli3CAUMmZmTQ/BMZD3dVaPeL/FnS/f +pbPZjFnrWU/DVKFFaopM+Gh5OK4Cw2Be704vGdqoLLSAixEpAPh+U7UFyDO2Er6r +ZewpqKz6fgwzn/FIXIe+OSlIg+5IyDNBYJkXDtAAd4tCxAflnLbbcIoHjeASiAZy +qg9ax5s2+rm1yRM2Eds/jMX6vKqCTWeOq7T8Sz5FpgS9F6hL70LnBy6dxwK2MYKr +lGjNc4wZAoGBAOX51pit7YuMk1vPO5wV8TUzX7jVz9N1zuuIybpDuhZ0y7gdM8L4 +LP+gaQpsdoeFnKCYC7oGjqLLrfXVxARqq1FLIaMTpd4C3uHAyWQmoZjBpFiw5a9f +FhZ67uDMEYTWoegAkDKV4O/AnfLkoDSJexh4eZ7LnChoEtGth2C4uuTPAoGBAMe7 +E1hXctDszEDQCbfzkvz4zSJZ32CzauYFCVxLz+4xQVRDKQccMwVnbDSjA0nq1RmN +c7COrEGQ4EkTgy/KGfo+KeEGoDWq3UG/EO+6lBSTaWD1M0E1nqidasPx3CuhZ81d +dAaqo4Hgm+QZOFPx7bCdNAZw8u/RS+ELxanEwcETAoGBAK/DiqeWBn8pKVZ5w+0/ +N+KZKQdzIitTVpndKZAaQxb2nNxSNUVFt96HEsDMtKpPC64RhqTebhUt5f6FKhLW +E6lF6qdgVNyhBQc0+LZ6b/b3yEugp2qpGZCE1B0q2hjUkVlnVM3Ujt2uMQl6gpak +nFfPe+RJaUfYgsmN/nGFvhExAoGAA/4LTvLAisiQ9KKz7wdhYskeYJdnTamIc3ya +SUwCpgI6ChMlmMzJgEwm745qnGmjRNV9AIR5kP3yXcOUFbp2dngTojndrfTyY0QM +dJ5II3km3BdKpP0LZNdwsOX+vqiFSKebkB6NEiWJODAA3jyxwZwC1DYhxQVr/N2d +X91c+h0CgYEA4PnAEmTILyJ9OPwV2A1cQ/RPi4smxZ0IIXZt/pmiEV4MYR5ULT7g +XzK3CHvV0wghQdBIxfAs8H31Ayux6N0wnva0w3tHesw15ByiDeNeJKO+tjpc01Zq +3xU6hUkWYGclvlezeWPQFkJKWYCLX4uw8lWOLCGd8VY2OZXJkS6W608= +-----END RSA PRIVATE KEY-----)"; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp new file mode 100644 index 000000000..73704f84c --- /dev/null +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -0,0 +1,78 @@ +#include "TopicAssignmentInfo.h" +#include "spdlog/spdlog.h" + +namespace rocketmq { +thread_local uint32_t TopicAssignment::query_which_broker_ = 0; + +TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) + : debug_string_(response.DebugString()) { + if (response.common().status().code() != google::rpc::Code::OK) { + SPDLOG_WARN("QueryAssignmentResponse#code is not SUCCESS. Keep assignment info intact. QueryAssignmentResponse: {}", + response.DebugString()); + return; + } + + for (const auto& item : response.assignments()) { + const rmq::Partition& partition = item.partition(); + if (rmq::Permission::READ != partition.permission() && rmq::Permission::READ_WRITE != partition.permission()) { + continue; + } + + assert(partition.has_broker()); + const auto& broker = partition.broker(); + + if (broker.endpoints().addresses().empty()) { + SPDLOG_WARN("Broker[{}] is not addressable", broker.DebugString()); + continue; + } + + MQMessageQueue message_queue(partition.topic().name(), partition.broker().name(), partition.id()); + std::string service_address; + for (const auto& address : broker.endpoints().addresses()) { + if (service_address.empty()) { + switch (broker.endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + service_address.append("ipv4:"); + break; + case rmq::AddressScheme::IPv6: + service_address.append("ipv6:"); + break; + case rmq::AddressScheme::DOMAIN_NAME: + service_address.append("dns:"); + break; + default: + SPDLOG_WARN("Unsupported gRPC naming scheme"); + break; + } + } else { + service_address.append(","); + } + service_address.append(address.host()).append(":").append(std::to_string(address.port())); + + if (rmq::AddressScheme::DOMAIN_NAME == broker.endpoints().scheme()) { + break; + } + } + message_queue.serviceAddress(service_address); + + ConsumeMessageType mode; + switch (item.mode()) { + case rmq::ConsumeMessageType::PULL: { + mode = ConsumeMessageType::PULL; + break; + } + case rmq::ConsumeMessageType::POP: { + mode = ConsumeMessageType::POP; + break; + } + default: { + SPDLOG_WARN("Unknown message request mode: {}, default to pop", item.mode()); + mode = ConsumeMessageType::POP; + } + } + assignment_list_.emplace_back(Assignment(message_queue, mode)); + } + std::sort(assignment_list_.begin(), assignment_list_.end()); +} + +} // namespace rocketmq diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp new file mode 100644 index 000000000..6e6ba743d --- /dev/null +++ b/src/main/cpp/client/TopicPublishInfo.cpp @@ -0,0 +1,138 @@ +#include "TopicPublishInfo.h" +#include "TopicRouteData.h" + +#include "LoggerImpl.h" +#include "MixAll.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" + +ROCKETMQ_NAMESPACE_BEGIN + +thread_local uint32_t TopicPublishInfo::send_which_queue_ = MixAll::random(0, 100); + +TopicPublishInfo::TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data) + : topic_(topic.data(), topic.length()), topic_route_data_(std::move(topic_route_data)) { + updatePublishInfo(); +} + +bool TopicPublishInfo::selectOneMessageQueue(MQMessageQueue& message_queue) { + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + return false; + } + auto& partition = partition_list_[index % (partition_list_.size())]; + message_queue = partition.asMessageQueue(); + } + return true; +} + +bool TopicPublishInfo::selectOneActiveMessageQueue(absl::flat_hash_set& isolated, + MQMessageQueue& message_queue) { + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + SPDLOG_DEBUG("message queue list empty"); + return false; + } + + for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { + message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); + if (!isolated.contains(message_queue.serviceAddress())) { + SPDLOG_DEBUG("Selected host={}", message_queue.serviceAddress()); + return true; + } + } + } + return false; +} + +bool TopicPublishInfo::takeMessageQueues(absl::flat_hash_set& isolated, + std::vector& candidates, int count) { + + unsigned int index = ++send_which_queue_; + { + absl::MutexLock lock(&partition_list_mtx_); + if (partition_list_.empty()) { + SPDLOG_DEBUG("message queue list empty"); + return false; + } + + for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { + const MQMessageQueue& message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); + if (!isolated.contains(message_queue.serviceAddress())) { + auto search = std::find_if(candidates.begin(), candidates.end(), [&](const MQMessageQueue& item) { + return item.getBrokerName() == message_queue.getBrokerName(); + }); + if (std::end(candidates) == search) { + candidates.emplace_back(message_queue); + } + if (candidates.size() >= count) { + return true; + } + } + } + } + return !candidates.empty(); +} + +void TopicPublishInfo::updatePublishInfo() { + + std::vector writable_partition_list; + { + for (const auto& partition : topic_route_data_->partitions()) { + assert(partition.topic().name() == topic_); + if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { + continue; + } + if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { + continue; + } + writable_partition_list.push_back(partition); + } + } + + if (writable_partition_list.empty()) { + SPDLOG_WARN("No writable partition is current available. Skip updating publish table for topic={}", topic_); + return; + } + + { + absl::MutexLock lk(&partition_list_mtx_); + partition_list_.swap(writable_partition_list); + } +} + +void TopicPublishInfo::topicRouteData(TopicRouteDataPtr topic_route_data) { + + SPDLOG_DEBUG("Update publish info according to renewed route data of topic={}", topic_); + { topic_route_data_ = std::move(topic_route_data); } + updatePublishInfo(); +} + +std::vector TopicPublishInfo::getMessageQueueList() { + std::vector message_queue_list; + { + absl::MutexLock lock(&partition_list_mtx_); + for (const auto& partition : partition_list_) { + if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { + continue; + } + + MQMessageQueue message_queue(partition.asMessageQueue()); + if (message_queue.serviceAddress().empty()) { + SPDLOG_WARN("Failed to resolve service address for {}", message_queue.simpleName()); + continue; + } + message_queue_list.emplace_back(message_queue); + } + + std::sort(message_queue_list.begin(), message_queue_list.end()); + + return message_queue_list; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TracingUtility.cpp b/src/main/cpp/client/TracingUtility.cpp new file mode 100644 index 000000000..6f4af461e --- /dev/null +++ b/src/main/cpp/client/TracingUtility.cpp @@ -0,0 +1,138 @@ +#ifdef ENABLE_TRACING +#include "TracingUtility.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TracingUtility& TracingUtility::get() { + static TracingUtility tracing_utility; + return tracing_utility; +} + +const std::string TracingUtility::INVALID_TRACE_ID = "00000000000000000000000000000000"; +const std::string TracingUtility::INVALID_SPAN_ID = "0000000000000000"; + +uint8_t TracingUtility::hexToInt(char c) { + if (c >= '0' && c <= '9') { + return (int)(c - '0'); + } else if (c >= 'a' && c <= 'f') { + return (int)(c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + return (int)(c - 'A' + 10); + } else { + return -1; + } +} + +void TracingUtility::generateHexFromString(const std::string& string, int bytes, uint8_t* buf) { + const char* str_id = string.data(); + for (int i = 0; i < bytes; i++) { + int tmp = hexToInt(str_id[i]); + if (tmp < 0) { + for (int j = 0; j < bytes / 2; j++) { + buf[j] = 0; + } + return; + } + if (i % 2 == 0) { + buf[i / 2] = tmp * 16; + } else { + buf[i / 2] += tmp; + } + } +} + +trace::TraceId TracingUtility::generateTraceIdFromString(const std::string& trace_id) { + int trace_id_len = kHeaderElementLengths[1]; + uint8_t buf[kTraceIdBytes / 2]; + uint8_t* b_ptr = buf; + TracingUtility::generateHexFromString(trace_id, trace_id_len, b_ptr); + return trace::TraceId(buf); +} + +bool TracingUtility::isValidHex(const std::string& str) { + for (char i : str) { + if (!(i >= '0' && i <= '9') && !(i >= 'a' && i <= 'f')) { + return false; + } + } + return true; +} + +trace::SpanId TracingUtility::generateSpanIdFromString(const std::string& span_id) { + int span_id_len = kHeaderElementLengths[2]; + uint8_t buf[kSpanIdBytes / 2]; + uint8_t* b_ptr = buf; + generateHexFromString(span_id, span_id_len, b_ptr); + return trace::SpanId(buf); +} + +trace::TraceFlags TracingUtility::generateTraceFlagsFromString(std::string trace_flags) { + if (trace_flags.length() > 2) { + return trace::TraceFlags(0); // check for invalid length of flags + } + int tmp1 = hexToInt(trace_flags[0]); + int tmp2 = hexToInt(trace_flags[1]); + if (tmp1 < 0 || tmp2 < 0) { + return trace::TraceFlags(0); // check for invalid char + } + uint8_t buf = tmp1 * 16 + tmp2; + return trace::TraceFlags(buf); +} + +std::string TracingUtility::injectSpanContextToTraceParent(const trace::SpanContext& span_context) { + char trace_id[32]; + trace::TraceId(span_context.trace_id()).ToLowerBase16(trace_id); + char span_id[16]; + trace::SpanId(span_context.span_id()).ToLowerBase16(span_id); + char trace_flags[2]; + trace::TraceFlags(span_context.trace_flags()).ToLowerBase16(trace_flags); + // Note: This is only temporary replacement for appendable string + std::string hex_string = "00-"; + for (char i : trace_id) { + hex_string.push_back(i); + } + hex_string.push_back('-'); + for (char i : span_id) { + hex_string.push_back(i); + } + hex_string.push_back('-'); + for (char trace_flag : trace_flags) { + hex_string.push_back(trace_flag); + } + return hex_string; +} + +// Assumed that all span context is remote. +trace::SpanContext TracingUtility::extractContextFromTraceParent(const std::string& trace_parent) { + if (trace_parent.length() != kHeaderSize || trace_parent[kHeaderElementLengths[0]] != '-' || + trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + 1] != '-' || + trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + 2] != '-') { + // Unresolvable trace_parent header. Returning INVALID span context. + return trace::SpanContext(false, false); + } + std::string version = trace_parent.substr(0, kHeaderElementLengths[0]); + std::string trace_id = trace_parent.substr(kHeaderElementLengths[0] + 1, kHeaderElementLengths[1]); + std::string span_id = + trace_parent.substr(kHeaderElementLengths[0] + kHeaderElementLengths[1] + 2, kHeaderElementLengths[2]); + std::string trace_flags = + trace_parent.substr(kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + 3); + + if (version == "ff" || trace_id == INVALID_TRACE_ID || span_id == INVALID_SPAN_ID) { + return trace::SpanContext(false, false); + } + + // validate ids + if (!TracingUtility::isValidHex(version) || !TracingUtility::isValidHex(trace_id) || + !TracingUtility::isValidHex(span_id) || !TracingUtility::isValidHex(trace_flags)) { + return trace::SpanContext(false, false); + } + + trace::TraceId trace_id_obj = TracingUtility::generateTraceIdFromString(trace_id); + trace::SpanId span_id_obj = TracingUtility::generateSpanIdFromString(span_id); + trace::TraceFlags trace_flags_obj = generateTraceFlagsFromString(trace_flags); + return trace::SpanContext(trace_id_obj, span_id_obj, trace_flags_obj, true); +} + +ROCKETMQ_NAMESPACE_END +#endif \ No newline at end of file diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h new file mode 100644 index 000000000..2dd624f33 --- /dev/null +++ b/src/main/cpp/client/include/Assignment.h @@ -0,0 +1,48 @@ +#pragma once + +#include "rocketmq/MQMessageQueue.h" +#include +#include + +namespace rocketmq { + +enum ConsumeMessageType { + PULL = 0, + POP = 1, +}; + +class Assignment { +public: + Assignment(MQMessageQueue message_queue, ConsumeMessageType consume_type) + : message_queue_(std::move(message_queue)), consume_type_(consume_type) {} + + bool operator==(const Assignment& rhs) const { + if (this == &rhs) { + return true; + } + + return message_queue_ == rhs.message_queue_ && consume_type_ == rhs.consume_type_; + } + + bool operator<(const Assignment& rhs) const { + if (message_queue_ != rhs.message_queue_) { + return message_queue_ < rhs.message_queue_; + } + + if (consume_type_ != rhs.consume_type_) { + return consume_type_ < rhs.consume_type_; + } + + return false; + } + + const MQMessageQueue& messageQueue() const { return message_queue_; } + + ConsumeMessageType consumeType() const { return consume_type_; } + +private: + MQMessageQueue message_queue_; + ConsumeMessageType consume_type_; +}; + +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h new file mode 100644 index 000000000..5c40c480a --- /dev/null +++ b/src/main/cpp/client/include/Broker.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ServiceAddress.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Broker { +public: + Broker(std::string name, int id, ServiceAddress service_address) + : name_(std::move(name)), id_(id), service_address_(std::move(service_address)) {} + + const std::string& name() const { return name_; } + + int32_t id() const { return id_; } + + explicit operator bool() const { return service_address_.operator bool(); } + + bool operator==(const Broker& other) const { return name_ == other.name_; } + + bool operator<(const Broker& other) const { return name_ < other.name_; } + + std::string serviceAddress() const { return service_address_.address(); } + +private: + std::string name_; + int32_t id_; + ServiceAddress service_address_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/BrokerData.h b/src/main/cpp/client/include/BrokerData.h new file mode 100644 index 000000000..2817efc97 --- /dev/null +++ b/src/main/cpp/client/include/BrokerData.h @@ -0,0 +1,52 @@ +#pragma once + +#include "RpcClient.h" +#include "apache/rocketmq/v1/definition.pb.h" + +#include +#include +#include + +namespace rocketmq { + +namespace rmq = apache::rocketmq::v1; + +class BrokerData { +public: + explicit BrokerData(const rmq::BrokerData& broker_data) + : cluster_(broker_data.cluster()), broker_name_(broker_data.broker_name()), + debug_string_(broker_data.DebugString()) { + for (const auto& entry : broker_data.addresses()) { + addresses_.insert(std::make_pair(entry.first, entry.second)); + } + } + + bool operator==(const BrokerData& rhs) const { + return cluster_ == rhs.cluster_ && broker_name_ == rhs.broker_name_ && addresses_ == rhs.addresses_; + } + + bool operator<(const BrokerData& rhs) const { + if (cluster_ != rhs.cluster_) { + return cluster_ < rhs.cluster_; + } + + return broker_name_ < rhs.broker_name_; + } + + const std::string& cluster() const { return cluster_; } + + const std::string& brokerName() const { return broker_name_; } + + const std::map& addresses() const { return addresses_; } + + const std::string& debugString() const { return debug_string_; } + +private: + std::string cluster_; + std::string broker_name_; + std::map addresses_; + + std::string debug_string_; +}; + +} // namespace rocketmq diff --git a/src/main/cpp/client/include/ClientApi.h b/src/main/cpp/client/include/ClientApi.h new file mode 100644 index 000000000..d39402315 --- /dev/null +++ b/src/main/cpp/client/include/ClientApi.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "RpcClient.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +namespace rocketmq { +class ClientApi { +public: + ClientApi() = default; + + void update_name_server_list(const std::vector& list) LOCKS_EXCLUDED(name_server_list_mtx_); + +private: + std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); + absl::Mutex name_server_list_mtx_; // Protects name_server_list_ +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientCallback.h b/src/main/cpp/client/include/ClientCallback.h new file mode 100644 index 000000000..75df022e0 --- /dev/null +++ b/src/main/cpp/client/include/ClientCallback.h @@ -0,0 +1,15 @@ +#include "rocketmq/RocketMQ.h" +#include "absl/container/flat_hash_set.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientCallback { +public: + virtual ~ClientCallback() = default; + + virtual void activeHosts(absl::flat_hash_set& hosts) = 0; + + virtual void heartbeat() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h new file mode 100644 index 000000000..110f469bf --- /dev/null +++ b/src/main/cpp/client/include/ClientConfig.h @@ -0,0 +1,92 @@ +#pragma once + +#include "Identifiable.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientConfig : public Identifiable { +public: + ClientConfig(); + + explicit ClientConfig(std::string group_name); + + ~ClientConfig() override = default; + + const std::string& arn() const { return arn_; } + + void arn(absl::string_view arn) { arn_ = std::string(arn.data(), arn.length()); } + + std::string clientId() const; + + const std::string& getInstanceName() const; + + void setInstanceName(std::string instance_name); + + const std::string& getGroupName() const; + void setGroupName(std::string group_name); + + const std::string& getUnitName() const { return unit_name_; } + void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } + + absl::Duration getIoTimeout() const; + void setIoTimeout(absl::Duration timeout); + + bool isTracingEnabled() { return enable_tracing_.load(); } + void enableTracing(bool enabled) { enable_tracing_.store(enabled); } + + CredentialsProviderPtr credentialsProvider() override; + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void serviceName(std::string service_name) { service_name_ = std::move(service_name); } + const std::string& serviceName() const { return service_name_; } + + void region(std::string region) { region_ = std::move(region); } + const std::string& region() const { return region_; } + + void tenantId(std::string tenant_id) { tenant_id_ = std::move(tenant_id); } + const std::string& tenantId() const override { return tenant_id_; } + + static const char* CLIENT_VERSION; + +protected: + /** + * Name of the service. + */ + std::string service_name_{"ONS"}; + + /** + * Region of the service to connect to. + */ + std::string region_; + + /** + * Abstract Resource Namespace, in which topic/group_name remain unique. + */ + std::string arn_; + + /** + * Tenant identifier. + */ + std::string tenant_id_; + + std::string instance_name_; + + std::string group_name_; + + std::string unit_name_; + + CredentialsProviderPtr credentials_provider_; + + absl::Duration io_timeout_; + std::atomic enable_tracing_{false}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientInstance.h b/src/main/cpp/client/include/ClientInstance.h new file mode 100644 index 000000000..647bf14db --- /dev/null +++ b/src/main/cpp/client/include/ClientInstance.h @@ -0,0 +1,291 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "ClientCallback.h" +#include "ClientConfig.h" +#include "Functional.h" +#include "HeartbeatDataCallback.h" +#include "Histogram.h" +#include "Identifiable.h" +#include "OrphanTransactionCallback.h" +#include "ReceiveMessageCallback.h" +#include "RpcClientImpl.h" +#include "Scheduler.h" +#include "SendMessageContext.h" +#include "ThreadPool.h" +#include "TopAddressing.h" +#include "TopicRouteChangeCallback.h" +#include "TopicRouteData.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#ifdef ENABLE_TRACING +#include "opentelemetry/exporters/otlp/otlp_exporter.h" +#include "opentelemetry/sdk/trace/batch_span_processor.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/provider.h" +#endif + +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/CommunicationMode.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientInstance : public std::enable_shared_from_this { +public: + explicit ClientInstance(std::string arn); + + ~ClientInstance(); + + void start(); + + void shutdown() LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + + static void assignLabels(Histogram& histogram); + + /** + * Resolve route data from name server for the given topic. + * + * @param target_host Name server host address; + * @param metadata Request headers; + * @param request Query route entries request. + * @param timeout RPC timeout. + * @param cb Callback to execute once the request/response completes. + */ + void resolveRoute(const std::string& target_host, const absl::flat_hash_map& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + + bool addInactiveRpcClient(const std::string& target_host) LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + + bool removeInactiveRpcClient(const std::string& target_host) + LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + + void doHealthCheck(); + + /** + * If inactive RPC clients refer to remote hosts that are absent from topic_route_table_, we need to purge them + * immediately. + */ + void cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_, inactive_rpc_clients_mtx_); + + /** + * Check if the RPC client is active or not. + * @param target_host Target host address in form of IP:port. + * @return true if the client for the target host is not temporarily blocked, aka, in inactive_rpc_clients_ map. + */ + bool isRpcClientActive(absl::string_view target_host) LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); + + void getAllInactiveRpcClientHost(absl::flat_hash_set& client_hosts) + LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); + + bool getInactiveRpcClient(absl::string_view target_host, RpcClientSharedPtr& client) + LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); + + void clientHealthCheck() LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + + bool send(const std::string& target, const absl::flat_hash_map& metadata, + SendMessageRequest& request, SendCallback* callback) LOCKS_EXCLUDED(rpc_clients_mtx_); + + /** + * Get a RpcClient according to the given target hosts, which follows scheme specified + * https://github.com/grpc/grpc/blob/master/doc/naming.md + * + * Note that a channel in gRPC is composted of one or more sub-channels. Every sub-channel represents a solid TCP + * connection. gRPC supports a number of configurable load-balancing policy with "pick-first" as the default option. + * Requests are distributed + * @param target_host + * @param need_heartbeat + * @return + */ + RpcClientSharedPtr getRpcClient(const std::string& target_host, bool need_heartbeat = true) + LOCKS_EXCLUDED(rpc_clients_mtx_); + + static SendResult processSendResponse(const MQMessageQueue& message_queue, const SendMessageResponse& response); + + // only for test + void addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) LOCKS_EXCLUDED(rpc_clients_mtx_); + + void addClientObserver(std::weak_ptr client); + + void queryAssignment(const std::string& target, const absl::flat_hash_map& metadata, + const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, + const std::function& cb); + + void receiveMessage(absl::string_view target, const absl::flat_hash_map& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + std::shared_ptr& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + + void pullMessage(absl::string_view target, absl::flat_hash_map& metadata, + const PullMessageRequest& request, std::shared_ptr& callback) + LOCKS_EXCLUDED(rpc_clients_mtx_); + + bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext); + + Scheduler& getScheduler(); + + /** + * Ack message asynchronously. + * @param target_host Target broker host address. + * @param request Ack message request. + */ + void ack(const std::string& target_host, const absl::flat_hash_map& metadata, + const AckMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& callback); + + void nack(const std::string& target_host, const absl::flat_hash_map& metadata, + const NackMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& callback); + + /** + * End a transaction asynchronously. + * + * Callback conforms the following method signature: + * void callable(bool rpc_ok, const EndTransactionResponse& response) + * + * if rpc_ok is false, the end transaction request may never reach the server and consequently no need to inspect the + * response. If rpc_ok is true, response should be further inspected to determine business tier code and logic. + * @param target_host + * @param metadata + * @param request + * @param timeout + * @param cb + */ + void endTransaction(const std::string& target_host, const absl::flat_hash_map& metadata, + const EndTransactionRequest& request, std::chrono::milliseconds timeout, + const std::function& cb); + + void multiplexingCall(const std::string& target, const absl::flat_hash_map& metadata, + const MultiplexingRequest& request, std::chrono::milliseconds timeout, + const std::function& cb); + + template + void queryOffset(const std::string& target_host, const absl::flat_hash_map& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, const Callable& cb) { + + auto client = getRpcClient(target_host); + if (!client) { + SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + auto callback = [cb, target_host](const grpc::Status& status, const grpc::ClientContext& client_context, + const QueryOffsetResponse& response) { + if (!status.ok()) { + SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", target_host, status.error_message()); + cb(false, response); + return; + } + + if (google::rpc::Code::OK != response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", target_host, + response.common().DebugString()); + cb(false, response); + } + + SPDLOG_DEBUG("Query offset from server[host={}] OK", target_host); + cb(true, response); + }; + invocation_context->callback_ = callback; + client->asyncQueryOffset(request, invocation_context); + } + + void pullMessage(const std::string& target_host, const absl::flat_hash_map& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& cb); + +#ifdef ENABLE_TRACING + nostd::shared_ptr getTracer(); + void updateTraceProvider(); +#endif + + void trace(bool trace) { trace_ = trace; } + + void heartbeat(const std::string& target_host, const absl::flat_hash_map& metadata, + const HeartbeatRequest& request, std::chrono::milliseconds timeout, + const std::function& cb); + +private: + void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, + ReceiveMessageResult& result, absl::string_view target_host); + + void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, + ReceiveMessageResult& result, absl::string_view target_host); + + void doHeartbeat(); + + void pollCompletionQueue(); + + void logStats(); + + Scheduler scheduler_; + + /** + * Abstract resource namespace. Each user may have one or more instances and each each instance has an independent + * abstract resource namespace. + */ + std::string arn_; + + std::atomic state_; + + std::vector> clients_ GUARDED_BY(clients_mtx_); + absl::Mutex clients_mtx_; + + absl::flat_hash_map> rpc_clients_ GUARDED_BY(rpc_clients_mtx_); + absl::Mutex rpc_clients_mtx_; // protects rpc_clients_ + + absl::flat_hash_map> + inactive_rpc_clients_ GUARDED_BY(inactive_rpc_clients_mtx_); + absl::Mutex inactive_rpc_clients_mtx_; + std::function inactive_rpc_client_detector_; + FunctionalSharePtr inactive_rpc_client_detector_function_; + + std::function heartbeat_loop_; + FunctionalSharePtr heartbeat_loop_function_; + + std::shared_ptr completion_queue_; + std::shared_ptr callback_thread_pool_; + + std::thread completion_queue_thread_; + + Histogram latency_histogram_; + std::function stats_functor_; + FunctionalSharePtr stats_function_; + + absl::flat_hash_set exporter_endpoint_set_ GUARDED_BY(exporter_endpoint_set_mtx_); + absl::Mutex exporter_endpoint_set_mtx_; + + /** + * Tenant-id. Each user shall have one unique identifier. + */ + std::string tenant_id_; + + std::string service_name_{"MQ"}; + + /** + * TLS configuration + */ + std::shared_ptr server_authorization_check_config_; + std::shared_ptr certificate_provider_; + grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; + + bool trace_{false}; +}; + +using ClientInstancePtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h new file mode 100644 index 000000000..b3021debb --- /dev/null +++ b/src/main/cpp/client/include/ClientManager.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "ClientConfig.h" +#include "ClientInstance.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/AdminServer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManager { +public: + static ClientManager& getInstance(); + + ClientInstancePtr getClientInstance(const ClientConfig& client_config) LOCKS_EXCLUDED(client_instance_table_mtx_); + + // For test purpose only + void addClientInstance(const std::string& arn, const ClientInstancePtr& client_instance) + LOCKS_EXCLUDED(client_instance_table_mtx_); + +private: + ClientManager(); + + virtual ~ClientManager(); + + /** + * Client Id --> Client Instance + */ + absl::flat_hash_map> + client_instance_table_ GUARDED_BY(client_instance_table_mtx_); + absl::Mutex client_instance_table_mtx_; // protects client_instance_table_ + + rocketmq::admin::AdminServer& admin_server_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h new file mode 100644 index 000000000..30ffe7955 --- /dev/null +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -0,0 +1,14 @@ +#pragma once + +#include "RpcClient.h" + +namespace rocketmq { + +/** + * This callback will be invoked to collect and prepare heartbeat data, which will be sent to brokers. + */ +class HeartbeatDataCallback { +public: + virtual void onHeartbeatDataCallback(HeartbeatRequest& request) = 0; +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/Identifiable.h b/src/main/cpp/client/include/Identifiable.h new file mode 100644 index 000000000..965a40f8b --- /dev/null +++ b/src/main/cpp/client/include/Identifiable.h @@ -0,0 +1,20 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include "rocketmq/CredentialsProvider.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Identifiable { +public: + virtual ~Identifiable() = default; + + virtual CredentialsProviderPtr credentialsProvider() = 0; + + virtual const std::string& tenantId() const = 0; +}; + +using IdentifiablePtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h new file mode 100644 index 000000000..399038685 --- /dev/null +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -0,0 +1,25 @@ +#pragma once + +#include "grpcpp/impl/codegen/interceptor.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class InterceptorContinuation { +public: + explicit InterceptorContinuation(grpc::experimental::InterceptorBatchMethods* methods) : methods_(methods) {} + + ~InterceptorContinuation() { + if (!hijacked_) { + methods_->Proceed(); + } + } + + void hijack() { hijacked_ = true; } + +private: + grpc::experimental::InterceptorBatchMethods* methods_; + bool hijacked_{false}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h new file mode 100644 index 000000000..f270a64a0 --- /dev/null +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -0,0 +1,16 @@ +#include "grpcpp/impl/codegen/client_interceptor.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LogInterceptor : public grpc::experimental::Interceptor { +public: + explicit LogInterceptor(grpc::experimental::ClientRpcInfo* client_rpc_info) : client_rpc_info_(client_rpc_info) {} + + void Intercept(grpc::experimental::InterceptorBatchMethods* methods) override; + +private: + grpc::experimental::ClientRpcInfo* client_rpc_info_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/LogInterceptorFactory.h b/src/main/cpp/client/include/LogInterceptorFactory.h new file mode 100644 index 000000000..c3560909e --- /dev/null +++ b/src/main/cpp/client/include/LogInterceptorFactory.h @@ -0,0 +1,13 @@ +#pragma once + +#include "grpcpp/impl/codegen/client_interceptor.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LogInterceptorFactory : public grpc::experimental::ClientInterceptorFactoryInterface { +public: + grpc::experimental::Interceptor* CreateClientInterceptor(grpc::experimental::ClientRpcInfo* info) override; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Metadata.h b/src/main/cpp/client/include/Metadata.h new file mode 100644 index 000000000..202ff1b68 --- /dev/null +++ b/src/main/cpp/client/include/Metadata.h @@ -0,0 +1,23 @@ +#pragma once +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Metadata { +public: + static const char* REQUEST_ID_KEY; + static const char* TENANT_ID_KEY; + static const char* ARN_KEY; + static const char* AUTHORIZATION; + static const char* DATE_TIME_KEY; + static const char* ALGORITHM_KEY; + static const char* CREDENTIAL_KEY; + static const char* SIGNED_HEADERS_KEY; + static const char* SIGNATURE_KEY; + static const char* DATE_TIME_FORMAT; + static const char* LANGUAGE_KEY; + static const char* CLIENT_VERSION_KEY; + static const char* PROTOCOL_VERSION_KEY; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/OpenSSLCompatible.h b/src/main/cpp/client/include/OpenSSLCompatible.h new file mode 100644 index 000000000..f51a47055 --- /dev/null +++ b/src/main/cpp/client/include/OpenSSLCompatible.h @@ -0,0 +1,366 @@ +#pragma once + +/** + * This is to make our code work with openssl both 1.0.x and 1.1.y + * https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes#Compatibility_Layer + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +#include + +static void* OPENSSL_zalloc(size_t num) { + void* ret = OPENSSL_malloc(num); + + if (ret != NULL) + memset(ret, 0, num); + return ret; +} + +int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) { + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +int RSA_set0_factors(RSA* r, BIGNUM* p, BIGNUM* q) { + /* If the fields p and q in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL)) + return 0; + + if (p != NULL) { + BN_free(r->p); + r->p = p; + } + if (q != NULL) { + BN_free(r->q); + r->q = q; + } + + return 1; +} + +int RSA_set0_crt_params(RSA* r, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp) { + /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->dmp1 == NULL && dmp1 == NULL) || (r->dmq1 == NULL && dmq1 == NULL) || (r->iqmp == NULL && iqmp == NULL)) + return 0; + + if (dmp1 != NULL) { + BN_free(r->dmp1); + r->dmp1 = dmp1; + } + if (dmq1 != NULL) { + BN_free(r->dmq1); + r->dmq1 = dmq1; + } + if (iqmp != NULL) { + BN_free(r->iqmp); + r->iqmp = iqmp; + } + + return 1; +} + +void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d) { + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +void RSA_get0_factors(const RSA* r, const BIGNUM** p, const BIGNUM** q) { + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} + +void RSA_get0_crt_params(const RSA* r, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp) { + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} + +void DSA_get0_pqg(const DSA* d, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g) { + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} + +int DSA_set0_pqg(DSA* d, BIGNUM* p, BIGNUM* q, BIGNUM* g) { + /* If the fields p, q and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((d->p == NULL && p == NULL) || (d->q == NULL && q == NULL) || (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(d->p); + d->p = p; + } + if (q != NULL) { + BN_free(d->q); + d->q = q; + } + if (g != NULL) { + BN_free(d->g); + d->g = g; + } + + return 1; +} + +void DSA_get0_key(const DSA* d, const BIGNUM** pub_key, const BIGNUM** priv_key) { + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} + +int DSA_set0_key(DSA* d, BIGNUM* pub_key, BIGNUM* priv_key) { + /* If the field pub_key in d is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} + +void DSA_SIG_get0(const DSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps) { + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int DSA_SIG_set0(DSA_SIG* sig, BIGNUM* r, BIGNUM* s) { + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps) { + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s) { + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} + +void DH_get0_pqg(const DH* dh, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g) { + if (p != NULL) + *p = dh->p; + if (q != NULL) + *q = dh->q; + if (g != NULL) + *g = dh->g; +} + +int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g) { + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free(dh->g); + dh->g = g; + } + + if (q != NULL) { + dh->length = BN_num_bits(q); + } + + return 1; +} + +void DH_get0_key(const DH* dh, const BIGNUM** pub_key, const BIGNUM** priv_key) { + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} + +int DH_set0_key(DH* dh, BIGNUM* pub_key, BIGNUM* priv_key) { + /* If the field pub_key in dh is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (dh->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(dh->pub_key); + dh->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(dh->priv_key); + dh->priv_key = priv_key; + } + + return 1; +} + +int DH_set_length(DH* dh, long length) { + dh->length = length; + return 1; +} + +const unsigned char* EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX* ctx) { return ctx->iv; } + +unsigned char* EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX* ctx) { return ctx->iv; } + +EVP_MD_CTX* EVP_MD_CTX_new(void) { return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); } + +void EVP_MD_CTX_free(EVP_MD_CTX* ctx) { + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +RSA_METHOD* RSA_meth_dup(const RSA_METHOD* meth) { + RSA_METHOD* ret; + + ret = OPENSSL_malloc(sizeof(RSA_METHOD)); + + if (ret != NULL) { + memcpy(ret, meth, sizeof(*meth)); + ret->name = OPENSSL_strdup(meth->name); + if (ret->name == NULL) { + OPENSSL_free(ret); + return NULL; + } + } + + return ret; +} + +int RSA_meth_set1_name(RSA_METHOD* meth, const char* name) { + char* tmpname; + + tmpname = OPENSSL_strdup(name); + if (tmpname == NULL) { + return 0; + } + + OPENSSL_free((char*)meth->name); + meth->name = tmpname; + + return 1; +} + +int RSA_meth_set_priv_enc(RSA_METHOD* meth, int (*priv_enc)(int flen, const unsigned char* from, unsigned char* to, + RSA* rsa, int padding)) { + meth->rsa_priv_enc = priv_enc; + return 1; +} + +int RSA_meth_set_priv_dec(RSA_METHOD* meth, int (*priv_dec)(int flen, const unsigned char* from, unsigned char* to, + RSA* rsa, int padding)) { + meth->rsa_priv_dec = priv_dec; + return 1; +} + +int RSA_meth_set_finish(RSA_METHOD* meth, int (*finish)(RSA* rsa)) { + meth->finish = finish; + return 1; +} + +void RSA_meth_free(RSA_METHOD* meth) { + if (meth != NULL) { + OPENSSL_free((char*)meth->name); + OPENSSL_free(meth); + } +} + +int RSA_bits(const RSA* r) { return (BN_num_bits(r->n)); } + +RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey) { + if (pkey->type != EVP_PKEY_RSA) { + return NULL; + } + return pkey->pkey.rsa; +} + +HMAC_CTX* HMAC_CTX_new(void) { + HMAC_CTX* ctx = OPENSSL_malloc(sizeof(*ctx)); + if (ctx != NULL) { + if (!HMAC_CTX_reset(ctx)) { + HMAC_CTX_free(ctx); + return NULL; + } + } + return ctx; +} + +void HMAC_CTX_free(HMAC_CTX* ctx) { + if (ctx != NULL) { + hmac_ctx_cleanup(ctx); + EVP_MD_CTX_free(ctx->i_ctx); + EVP_MD_CTX_free(ctx->o_ctx); + EVP_MD_CTX_free(ctx->md_ctx); + OPENSSL_free(ctx); + } +} +#endif \ No newline at end of file diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/src/main/cpp/client/include/OrphanTransactionCallback.h new file mode 100644 index 000000000..356fd6426 --- /dev/null +++ b/src/main/cpp/client/include/OrphanTransactionCallback.h @@ -0,0 +1,15 @@ +#pragma once + +#include "rocketmq/MQMessage.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OrphanTransactionCallback { +public: + virtual ~OrphanTransactionCallback() = default; + + virtual void onOrphanTransaction(const MQMessage& message) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h new file mode 100644 index 000000000..9a21d62d3 --- /dev/null +++ b/src/main/cpp/client/include/Partition.h @@ -0,0 +1,57 @@ +#pragma once + +#include "Broker.h" +#include "Topic.h" +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum Permission : int8_t { NONE = 0, READ = 1, WRITE = 2, READ_WRITE = 3 }; + +class Partition { +public: + Partition(Topic topic, int32_t id, Permission permission, Broker broker) + : topic_(std::move(topic)), broker_(std::move(broker)), id_(id), permission_(permission) {} + + const Topic& topic() const { return topic_; } + + int32_t id() const { return id_; } + + const Broker& broker() const { return broker_; } + + Permission permission() const { return permission_; } + + MQMessageQueue asMessageQueue() const { + MQMessageQueue message_queue(topic_.name(), broker_.name(), id_); + message_queue.serviceAddress(broker_.serviceAddress()); + return message_queue; + } + + bool operator==(const Partition& other) const { + return topic_ == other.topic_ && id_ == other.id_ && broker_ == other.broker_ && permission_ == other.permission_; + } + + bool operator<(const Partition& other) const { + if (topic_ < other.topic_) { + return true; + } else if (other.topic_ < topic_) { + return false; + } + + if (broker_ < other.broker_) { + return true; + } else if (other.broker_ < broker_) { + return false; + } + + return id_ < other.id_; + } + +private: + Topic topic_; + Broker broker_; + int32_t id_; + Permission permission_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h new file mode 100644 index 000000000..738b8a717 --- /dev/null +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ReceiveMessageResult.h" +#include "rocketmq/AsyncCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN +class ReceiveMessageCallback : public AsyncCallback { +public: + virtual ~ReceiveMessageCallback() = default; + virtual void onSuccess(ReceiveMessageResult& result) = 0; + virtual void onException(MQException& e) = 0; +}; +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h new file mode 100644 index 000000000..8de0a64b9 --- /dev/null +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -0,0 +1,156 @@ +#pragma once + +#include "MixAll.h" +#include "absl/time/time.h" +#include "rocketmq/MQMessageExt.h" + +#include +#include +#include + +namespace rocketmq { + +enum class ReceiveMessageStatus : int32_t { + /** + * Messages are received as expected. + */ + OK, + + /** + * Deadline expired before matched messages are found in the server side. + */ + DEADLINE_EXCEEDED, + + /** + * Resource has been exhausted, perhaps a per-user quota. For example, too many receive-message requests are submitted + * to the same partition at the same time. + */ + RESOURCE_EXHAUSTED, + + /** + * The target partition does not exist, which might have been deleted. + */ + NOT_FOUND, + + /** + * Receive-message operation was attempted past the valid range. For pull operation, clients may try to pull expired + * messages. + */ + OUT_OF_RANGE, + + /** + * Data is corrupted during transfer. + */ + DATA_CORRUPTED, + + /** + * Serious errors occurred in the server side. + */ + INTERNAL +}; + +static const char* EnumStrings[] = { + "FOUND", "DEADLINE_EXCEEDED", "RESOURCE_EXHAUSTED", "NOT_FOUND", "OUT_OF_RANGE", "DATA_CORRUPTED", "INTERNAL"}; + +struct ReceiveMessageResult { + ReceiveMessageResult() = default; + + ReceiveMessageResult(ReceiveMessageStatus receive_status, absl::Time pop_time, absl::Duration invisible_period, + long long rest_num, std::vector msg_found_list) + : status_(receive_status), pop_time_(pop_time), invisible_time_(invisible_period), + messages_(std::move(msg_found_list)) {} + + ReceiveMessageResult(const ReceiveMessageResult& other) { + status_ = other.status_; + pop_time_ = other.pop_time_; + invisible_time_ = other.invisible_time_; + messages_ = other.messages_; + request_id_ = other.request_id_; + source_host_ = other.source_host_; + next_offset_ = other.next_offset_; + } + + ReceiveMessageResult& operator=(const ReceiveMessageResult& other) { + if (this == &other) { + return *this; + } + status_ = other.status_; + pop_time_ = other.pop_time_; + invisible_time_ = other.invisible_time_; + messages_ = other.messages_; + request_id_ = other.request_id_; + source_host_ = other.source_host_; + next_offset_ = other.next_offset_; + return *this; + } + + ~ReceiveMessageResult() = default; + + std::string toString() const { + std::stringstream ss; + ss << "ReceiveMessageResult[ popStatus=" << EnumStrings[static_cast(status_)] + << ", popTime=" << absl::FormatTime(pop_time_, absl::UTCTimeZone()) + << "[UTC], invisibleTime=" << ToInt64Milliseconds(invisible_time_) + << "ms, len(msgFoundList)=" << messages_.size() << " ]"; + return ss.str(); + } + + void status(ReceiveMessageStatus receive_status) { status_ = receive_status; } + + ReceiveMessageStatus status() const { return status_; } + + void setPopTime(absl::Time pop_time) { pop_time_ = pop_time; } + + absl::Time getPopTime() const { return pop_time_; } + + void invisibleTime(absl::Duration invisible_period) { invisible_time_ = invisible_period; } + + absl::Duration invisibleTime() const { return invisible_time_; } + + void setMsgFoundList(std::vector& msg_found_list) { messages_ = msg_found_list; } + + const std::vector& getMsgFoundList() const { return messages_; } + + const std::string& requestId() const { return request_id_; } + + void requestId(std::string request_id) { request_id_ = std::move(request_id); } + + const std::string& sourceHost() const { return source_host_; } + + void sourceHost(std::string source_host) { source_host_ = std::move(source_host); } + + bool termId(int64_t& term_id) const { + if (request_id_.empty()) { + return false; + } + std::vector bin; + bin.reserve(16); + if (!MixAll::hexToBinary(request_id_, bin)) { + SPDLOG_WARN("Invalid request_id: {}", request_id_); + return false; + } + // IP(int32_t) + PID(int32_t) + term_id(int64_t) + assert(bin.size() == 4 + 4 + 8); + + auto ptr = reinterpret_cast(bin.data()); + term_id = *(ptr + 1); + return true; + } + + ReceiveMessageStatus status_{ReceiveMessageStatus::DEADLINE_EXCEEDED}; + + absl::Time pop_time_; + absl::Duration invisible_time_; + + std::vector messages_; + + /** + * Follow format of hex{int32(IPv4) + int32(pid) + int64(sequence)} + */ + std::string request_id_; + + std::string source_host_; + + int64_t next_offset_{0}; +}; +} // namespace rocketmq diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h new file mode 100644 index 000000000..740a0aab4 --- /dev/null +++ b/src/main/cpp/client/include/RpcClient.h @@ -0,0 +1,114 @@ +#pragma once + +#include + +#include "InvocationContext.h" +#include "OrphanTransactionCallback.h" + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "apache/rocketmq/v1/definition.grpc.pb.h" +#include "apache/rocketmq/v1/service.grpc.pb.h" +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace rmq = apache::rocketmq::v1; + +using Channel = grpc::Channel; +using Status = grpc::Status; +using CompletionQueue = grpc::CompletionQueue; +using QueryRouteRequest = rmq::QueryRouteRequest; +using QueryRouteResponse = rmq::QueryRouteResponse; +using SendMessageRequest = rmq::SendMessageRequest; +using SendMessageResponse = rmq::SendMessageResponse; +using QueryAssignmentRequest = rmq::QueryAssignmentRequest; +using QueryAssignmentResponse = rmq::QueryAssignmentResponse; +using ReceiveMessageRequest = rmq::ReceiveMessageRequest; +using ReceiveMessageResponse = rmq::ReceiveMessageResponse; +using AckMessageRequest = rmq::AckMessageRequest; +using AckMessageResponse = rmq::AckMessageResponse; +using NackMessageRequest = rmq::NackMessageRequest; +using NackMessageResponse = rmq::NackMessageResponse; +using HeartbeatRequest = rmq::HeartbeatRequest; +using HeartbeatResponse = rmq::HeartbeatResponse; +using HealthCheckRequest = rmq::HealthCheckRequest; +using HealthCheckResponse = rmq::HealthCheckResponse; +using EndTransactionRequest = rmq::EndTransactionRequest; +using EndTransactionResponse = rmq::EndTransactionResponse; +using MultiplexingRequest = rmq::MultiplexingRequest; +using MultiplexingResponse = rmq::MultiplexingResponse; +using GenericPollingRequest = rmq::GenericPollingRequest; +using GenericPollingResponse = rmq::GenericPollingRequest; +using PrintThreadStackRequest = rmq::PrintThreadStackRequest; +using PrintThreadStackResponse = rmq::PrintThreadStackResponse; +using VerifyMessageConsumptionRequest = rmq::VerifyMessageConsumptionRequest; +using VerifyMessageConsumptionResponse = rmq::VerifyMessageConsumptionResponse; +using ResolveOrphanedTransactionRequest = rmq::ResolveOrphanedTransactionRequest; +using QueryOffsetRequest = rmq::QueryOffsetRequest; +using QueryOffsetResponse = rmq::QueryOffsetResponse; +using PullMessageRequest = rmq::PullMessageRequest; +using PullMessageResponse = rmq::PullMessageResponse; + +class RpcClient { +public: + RpcClient() = default; + + virtual ~RpcClient() = default; + + virtual void asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) = 0; + + virtual std::shared_ptr& completionQueue() = 0; + + virtual void asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncAck(const AckMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncMultiplexingCall(const MultiplexingRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) = 0; + + virtual void asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) = 0; + + /** + * Indicate if heartbeat is required. + * @return true if periodic heartbeat is required; false otherwise. + */ + virtual bool needHeartbeat() = 0; + + /** + * Indicate if current client connection state is OK or recoverable. + * + * @return true if underlying connection is OK or recoverable; false otherwise. + */ + virtual bool ok() const = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h new file mode 100644 index 000000000..9cf634666 --- /dev/null +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -0,0 +1,76 @@ +#pragma once + +#include "RpcClient.h" + +#include "absl/container/flat_hash_map.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientImpl : public RpcClient, public std::enable_shared_from_this { +public: + RpcClientImpl(std::shared_ptr completion_queue, std::shared_ptr channel, + bool need_heartbeat = true) + : completion_queue_(std::move(completion_queue)), channel_(std::move(channel)), + stub_(rmq::MessagingService::NewStub(channel_)), need_heartbeat_(need_heartbeat) {} + + RpcClientImpl(const RpcClientImpl&) = delete; + + RpcClientImpl& operator=(const RpcClientImpl&) = delete; + + ~RpcClientImpl() override = default; + + void asyncQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) override; + + void asyncSend(const SendMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) override; + + std::shared_ptr& completionQueue() override; + + void asyncReceive(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) override; + + void asyncNack(const NackMessageRequest& request, + InvocationContext* invocation_context) override; + + void asyncHeartbeat(const HeartbeatRequest& request, + InvocationContext* invocation_context) override; + + void asyncHealthCheck(const HealthCheckRequest& request, + InvocationContext* invocation_context) override; + + void asyncEndTransaction(const EndTransactionRequest& request, + InvocationContext* invocation_context) override; + + void asyncMultiplexingCall(const MultiplexingRequest& request, + InvocationContext* invocation_context) override; + + void asyncQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) override; + + void asyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) override; + + bool needHeartbeat() override; + + bool ok() const override; + +private: + static void addMetadata(grpc::ClientContext& context, const absl::flat_hash_map& metadata); + + std::shared_ptr completion_queue_; + std::shared_ptr channel_; + std::unique_ptr stub_; + std::chrono::milliseconds connect_timeout_{3000}; + bool need_heartbeat_{true}; +}; + +using RpcClientSharedPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h new file mode 100644 index 000000000..301aa912e --- /dev/null +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -0,0 +1,30 @@ +#pragma once +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" +#include + +namespace rocketmq { +class SendMessageContext { +public: + const std::string& getProducerGroup() const { return producer_group_; }; + void setProducerGroup(const std::string& producer_group) { this->producer_group_ = producer_group; }; + const MQMessage& getMessage() const { return message_; } + void setMessage(const MQMessage& message) { this->message_ = message; } + const MQMessageQueue& getMessageQueue() const { return message_queue_; } + void setMessageQueue(const MQMessageQueue& message_queue) { this->message_queue_ = message_queue; } + const std::string& getBornHost() const { return born_host_; } + void setBornHost(const std::string& born_host) { this->born_host_ = born_host; } + const std::string& getMessageId() const { return message_id_; } + void setMessageId(const std::string& message_id) { this->message_id_ = message_id; } + long long int getQueueOffset() const { return queue_offset_; } + void setQueueOffset(long long queue_offset) { this->queue_offset_ = queue_offset; } + +private: + std::string producer_group_; + MQMessage message_; + MQMessageQueue message_queue_; + std::string born_host_; + std::string message_id_; + long long queue_offset_{-1}; +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h new file mode 100644 index 000000000..7c28d674b --- /dev/null +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -0,0 +1,94 @@ +#pragma once + +#include "absl/strings/string_view.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum AddressScheme : int8_t { + IPv4 = 0, + IPv6 = 1, + DOMAIN_NAME = 2, +}; + +class Address { +public: + Address(absl::string_view host, int32_t port) : host_(host.data(), host.length()), port_(port) {} + + bool operator==(const Address& other) const { return host_ == other.host_ && port_ == other.port_; } + + bool operator<(const Address& other) const { + if (host_ < other.host_) { + return true; + } else if (host_ > other.host_) { + return false; + } + + if (port_ < other.port_) { + return true; + } else if (port_ > other.port_) { + return false; + } + + return false; + } + + const std::string& host() const { return host_; } + + int32_t port() const { return port_; } + +private: + std::string host_; + int32_t port_; +}; + +/** + * Thread Safety Analysis: + * Once initialized, this class remains immutable. Thus it is threads-safe. + */ +class ServiceAddress { +public: + ServiceAddress(AddressScheme scheme, std::vector
addresses) + : scheme_(scheme), addresses_(std::move(addresses)) { + std::sort(addresses_.begin(), addresses_.end()); + } + + AddressScheme scheme() const { return scheme_; } + + explicit operator bool() const { return !addresses_.empty(); } + + std::string address() const { + std::string result; + switch (scheme_) { + case AddressScheme::IPv4: + result.append("ipv4:"); + break; + case AddressScheme::IPv6: + result.append("ipv6:"); + break; + case AddressScheme::DOMAIN_NAME: + result.append("dns:"); + break; + } + + bool first = true; + for (const auto& item : addresses_) { + if (!first) { + result.append(","); + } else { + first = false; + } + result.append(item.host()).append(":").append(std::to_string(item.port())); + } + return result; + } + +private: + AddressScheme scheme_; + std::vector
addresses_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h new file mode 100644 index 000000000..f617eccd8 --- /dev/null +++ b/src/main/cpp/client/include/Signature.h @@ -0,0 +1,11 @@ +#include "ClientConfig.h" +#include "absl/container/flat_hash_map.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Signature { +public: + static void sign(ClientConfig* client, absl::flat_hash_map& metadata); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h new file mode 100644 index 000000000..801ecf109 --- /dev/null +++ b/src/main/cpp/client/include/TlsHelper.h @@ -0,0 +1,43 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include + +#include "grpcpp/security/tls_credentials_options.h" +#include +#include +#include + +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TlsHelper { + +public: + static std::string sign(const std::string& access_secret, const std::string& content); + + static const char* CA; + + static const char* client_certificate_chain; + + static const char* client_private_key; +}; + +class TlsServerAuthorizationChecker : public grpc::experimental::TlsServerAuthorizationCheckInterface { +public: + int Schedule(grpc::experimental::TlsServerAuthorizationCheckArg* arg) override { + if (nullptr == arg) { + return 0; + } + + arg->set_success(1); + arg->set_status(GRPC_STATUS_OK); + return 0; + } + + ~TlsServerAuthorizationChecker() override { SPDLOG_DEBUG("~TlsServerAuthorizationChecker() invoked"); } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h new file mode 100644 index 000000000..118e504ea --- /dev/null +++ b/src/main/cpp/client/include/Topic.h @@ -0,0 +1,39 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class Topic { +public: + Topic(std::string arn, std::string name) : arn_(std::move(arn)), name_(std::move(name)) {} + + const std::string& arn() const { return arn_; } + + const std::string& name() const { return name_; } + + bool operator==(const Topic& other) const { return arn_ == other.arn_ && name_ == other.name_; } + + bool operator<(const Topic& other) const { + if (arn_ < other.arn_) { + return true; + } else if (arn_ > other.arn_) { + return false; + } + + if (name_ < other.name_) { + return true; + } else if (name_ > other.name_) { + return false; + } + return false; + } + +private: + std::string arn_; + std::string name_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h new file mode 100644 index 000000000..9581afb8c --- /dev/null +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Assignment.h" +#include "RpcClient.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicAssignment { +public: + explicit TopicAssignment(std::vector&& assignments) : assignment_list_(std::move(assignments)) { + std::sort(assignment_list_.begin(), assignment_list_.end()); + } + + explicit TopicAssignment(const QueryAssignmentResponse& response); + + ~TopicAssignment() = default; + + const std::vector& assignmentList() const { return assignment_list_; } + + bool operator==(const TopicAssignment& rhs) const { return assignment_list_ == rhs.assignment_list_; } + + bool operator!=(const TopicAssignment& rhs) const { return assignment_list_ != rhs.assignment_list_; } + + const std::string& debugString() const { return debug_string_; } + + static unsigned int getAndIncreaseQueryWhichBroker() { return ++query_which_broker_; } + +private: + /** + * Once it is set, it will be immutable. + */ + std::vector assignment_list_; + + std::string debug_string_; + + thread_local static uint32_t query_which_broker_; +}; + +using TopicAssignmentPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h new file mode 100644 index 000000000..5ed8ce7a7 --- /dev/null +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include "ClientInstance.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/MQMessageQueue.h" + +namespace rocketmq { + +class TopicPublishInfo { +public: + TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data); + + /** + * @param message_queue Reference to target message queue. + * @return true if manage to select one; false otherwise. + */ + bool selectOneMessageQueue(MQMessageQueue& message_queue) LOCKS_EXCLUDED(partition_list_mtx_); + + bool selectOneActiveMessageQueue(absl::flat_hash_set& isolated, MQMessageQueue& message_queue) + LOCKS_EXCLUDED(partition_list_mtx_); + + bool takeMessageQueues(absl::flat_hash_set& isolated, std::vector& candidates, int count) + LOCKS_EXCLUDED(partition_list_mtx_); + + void topicRouteData(TopicRouteDataPtr topic_route_data); + + /** + * Expose partition list in perspective of message queue list. + * + * @return + */ + std::vector getMessageQueueList() LOCKS_EXCLUDED(partition_list_mtx_); + +private: + std::vector partition_list_ GUARDED_BY(partition_list_mtx_); + absl::Mutex partition_list_mtx_; // protects message_queue_list_ + + std::string topic_; + TopicRouteDataPtr topic_route_data_; + + void updatePublishInfo(); + + thread_local static uint32_t send_which_queue_; +}; + +using TopicPublishInfoPtr = std::shared_ptr; +} // namespace rocketmq diff --git a/src/main/cpp/client/include/TopicRouteChangeCallback.h b/src/main/cpp/client/include/TopicRouteChangeCallback.h new file mode 100644 index 000000000..1415586df --- /dev/null +++ b/src/main/cpp/client/include/TopicRouteChangeCallback.h @@ -0,0 +1,15 @@ +#pragma once + +#include "TopicRouteData.h" +#include "absl/strings/string_view.h" + +ROCKETMQ_NAMESPACE_BEGIN + + + +class TopicRouteChangeCallback { +public: + virtual void onTopicRouteChange(absl::string_view topic, const TopicRouteDataPtr& topic_route_data) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h new file mode 100644 index 000000000..4719ef4ef --- /dev/null +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Partition.h" +#include "RpcClient.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace rmq = apache::rocketmq::v1; + +/** + * Thread Safety: This class is immutable and thus effectively thread safe. + */ +class TopicRouteData { +public: + TopicRouteData(std::vector partitions, std::string debug_string) + : partitions_(std::move(partitions)), debug_string_(std::move(debug_string)) { + std::sort(partitions_.begin(), partitions_.end()); + } + + const std::vector& partitions() const { return partitions_; } + + const std::string& debugString() const { return debug_string_; } + + bool operator==(const TopicRouteData& other) const { return partitions_ == other.partitions_; } + + bool operator!=(const TopicRouteData& other) const { return !this->operator==(other); } + +private: + std::vector partitions_; + std::string debug_string_; +}; + +using TopicRouteDataPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TracingUtility.h b/src/main/cpp/client/include/TracingUtility.h new file mode 100644 index 000000000..c69a98f77 --- /dev/null +++ b/src/main/cpp/client/include/TracingUtility.h @@ -0,0 +1,59 @@ +#pragma once +#ifdef ENABLE_TRACING +#include "opentelemetry/trace/span_context.h" +#include "rocketmq/RocketMQ.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace trace = opentelemetry::trace; + +class TracingUtility { +public: + static TracingUtility& get(); + + static const int kTraceDelimiterBytes = 3; + // 0: version, 1: trace id, 2: span id, 3: trace flags + constexpr static const int kHeaderElementLengths[4] = {2, 32, 16, 2}; + static const int kHeaderSize = kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + + kHeaderElementLengths[3] + kTraceDelimiterBytes; + + static const int kTraceStateMaxMembers = 32; + static const int kVersionBytes = 2; + static const int kTraceIdBytes = 32; + static const int kSpanIdBytes = 16; + static const int kTraceFlagBytes = 2; + + static const std::string INVALID_TRACE_ID; + static const std::string INVALID_SPAN_ID; + + const std::string topic_ = "topic"; + const std::string consumer_group_ = "consumer_group"; + const std::string msg_id_ = "msg_id"; + const std::string tags_ = "tags"; + const std::string store_host_ = "store_host"; + const std::string success_ = "success"; + const std::string max_attempt_times_ = "retry_time"; + const std::string expired_ = "expired"; + + static std::string injectSpanContextToTraceParent(const trace::SpanContext& span_context); + + static trace::SpanContext extractContextFromTraceParent(const std::string& trace_parent); + +private: + static void generateHexFromString(const std::string& string, int bytes, uint8_t* buf); + + static uint8_t hexToInt(char c); + + static trace::TraceId generateTraceIdFromString(const std::string& trace_id); + + static bool isValidHex(const std::string& str); + + static trace::SpanId generateSpanIdFromString(const std::string& span_id); + + static trace::TraceFlags generateTraceFlagsFromString(std::string trace_flags); +}; + +ROCKETMQ_NAMESPACE_END +#endif \ No newline at end of file diff --git a/src/main/cpp/client/mocks/CMakeLists.txt b/src/main/cpp/client/mocks/CMakeLists.txt new file mode 100644 index 000000000..c6fbe407f --- /dev/null +++ b/src/main/cpp/client/mocks/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library(rpc_client_mock RpcClientMock.cpp) +target_include_directories(rpc_client_mock + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(rpc_client_mock + gmock + client_library + mq_proto_library) + +add_library(credentials_observable_mock INTERFACE) +target_include_directories(credentials_observable_mock + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(credentials_observable_mock + INTERFACE + gmock + client_library) \ No newline at end of file diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp new file mode 100644 index 000000000..e2d8a6c2c --- /dev/null +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -0,0 +1,14 @@ +#include "include/RpcClientMock.h" + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * TODO: Add commonly used mock operation for RpcClient + */ +RpcClientMock::RpcClientMock() : completion_queue_(std::make_shared()) { + ON_CALL(*this, completionQueue()).WillByDefault(ReturnRef(completion_queue_)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/IdentifiableMock.h b/src/main/cpp/client/mocks/include/IdentifiableMock.h new file mode 100644 index 000000000..473277fae --- /dev/null +++ b/src/main/cpp/client/mocks/include/IdentifiableMock.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Identifiable.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class IdentifiableMock : public Identifiable { +public: + ~IdentifiableMock() override = default; + MOCK_METHOD(CredentialsProviderPtr, credentialsProvider, (), (override)); + MOCK_METHOD(const std::string&, tenantId, (), (const override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h new file mode 100644 index 000000000..feb136a73 --- /dev/null +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -0,0 +1,52 @@ +#pragma once + +#include "RpcClient.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientMock : public RpcClient { +public: + RpcClientMock(); + + ~RpcClientMock() override = default; + + MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncSend, (const SendMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncQueryAssignment, (const QueryAssignmentRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(std::shared_ptr&, completionQueue, (), (override)); + + MOCK_METHOD(void, asyncReceive, (const ReceiveMessageRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncAck, (const AckMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncNack, (const NackMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncHeartbeat, (const HeartbeatRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncHealthCheck, (const HealthCheckRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncEndTransaction, (const EndTransactionRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncMultiplexingCall, (const MultiplexingRequest&, InvocationContext*), + (override)); + + MOCK_METHOD(void, asyncQueryOffset, (const QueryOffsetRequest&, InvocationContext*), (override)); + + MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); + + MOCK_METHOD(bool, needHeartbeat, (), (override)); + + MOCK_METHOD(bool, ok, (), (const override)); + +protected: + std::shared_ptr completion_queue_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/concurrent/BUILD.bazel b/src/main/cpp/concurrent/BUILD.bazel new file mode 100644 index 000000000..ecb133091 --- /dev/null +++ b/src/main/cpp/concurrent/BUILD.bazel @@ -0,0 +1,20 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "countdown_latch_library", + hdrs = [ + "include/CountdownLatch.h", + ], + srcs = [ + "CountdownLatch.cpp" + ], + strip_include_prefix = "//src/main/cpp/concurrent/include", + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_absl//absl/base", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + ], + visibility = [ + "//visibility:public", + ], +) \ No newline at end of file diff --git a/src/main/cpp/concurrent/CMakeLists.txt b/src/main/cpp/concurrent/CMakeLists.txt new file mode 100644 index 000000000..f3394e0a6 --- /dev/null +++ b/src/main/cpp/concurrent/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(countdown_latch_obj OBJECT) +target_sources(countdown_latch_obj PRIVATE CountdownLatch.cpp) +target_include_directories(countdown_latch_obj + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(countdown_latch_obj + PUBLIC absl::strings + PUBLIC absl::base + PUBLIC absl::synchronization + PUBLIC base_library + PRIVATE spdlog::spdlog_header_only) + +add_library(countdown_latch_library STATIC) +target_link_libraries(countdown_latch_library PUBLIC countdown_latch_obj) \ No newline at end of file diff --git a/src/main/cpp/concurrent/CountdownLatch.cpp b/src/main/cpp/concurrent/CountdownLatch.cpp new file mode 100644 index 000000000..f660d1e9f --- /dev/null +++ b/src/main/cpp/concurrent/CountdownLatch.cpp @@ -0,0 +1,34 @@ +#include "include/CountdownLatch.h" +#include "LoggerImpl.h" + +using namespace rocketmq; + +void CountdownLatch::await() { + absl::MutexLock lock(&mtx_); + if (count_ <= 0) { + return; + } + while (count_ > 0) { + cv_.Wait(&mtx_); + } +} + +void CountdownLatch::countdown() { + absl::MutexLock lock(&mtx_); + if (--count_ <= 0) { + cv_.SignalAll(); + } + if (!name_.empty()) { + if (count_ >= 0) { + SPDLOG_TRACE("After countdown(), latch[{}]={}", name_, count_); + } + } +} + +void CountdownLatch::increaseCount() { + absl::MutexLock lock(&mtx_); + ++count_; + if (!name_.empty()) { + SPDLOG_TRACE("After increaseCount(), latch[{}]={}", name_, count_); + } +} \ No newline at end of file diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h new file mode 100644 index 000000000..2a1e869f7 --- /dev/null +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -0,0 +1,28 @@ +#pragma once + +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include + +namespace rocketmq { +class CountdownLatch { +public: + explicit CountdownLatch(int32_t count) : CountdownLatch(count, "anonymous") {} + CountdownLatch(int32_t count, absl::string_view name) : count_(count), name_(name.data(), name.length()) {} + + void await() LOCKS_EXCLUDED(mtx_); + + void countdown() LOCKS_EXCLUDED(mtx_); + + void increaseCount() LOCKS_EXCLUDED(mtx_); + +private: + int32_t count_ GUARDED_BY(mtx_); + + absl::Mutex mtx_; // protects count_ + absl::CondVar cv_; + + std::string name_; +}; +} // namespace rocketmq diff --git a/src/main/cpp/log/BUILD.bazel b/src/main/cpp/log/BUILD.bazel new file mode 100644 index 000000000..317f1fec9 --- /dev/null +++ b/src/main/cpp/log/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "log_library", + srcs = [ + "Logger.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_github_gabime_spdlog//:spdlog", + "@com_github_gulrak_filesystem//:filesystem", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt new file mode 100644 index 000000000..cf9b1438d --- /dev/null +++ b/src/main/cpp/log/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(logger_obj OBJECT) +target_sources(logger_obj PRIVATE Logger.cpp) +target_link_libraries(logger_obj + PUBLIC + admin_library + ghc_filesystem) + +add_library(logger_library + STATIC + $) +target_link_libraries(logger_library + PUBLIC + admin_library + ghc_filesystem) \ No newline at end of file diff --git a/src/main/cpp/log/Logger.cpp b/src/main/cpp/log/Logger.cpp new file mode 100644 index 000000000..186958fe4 --- /dev/null +++ b/src/main/cpp/log/Logger.cpp @@ -0,0 +1,99 @@ +#include "rocketmq/Logger.h" +#include +#include + +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/spdlog.h" + +// Check C++17 is used +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif + +#ifndef GHC_USE_STD_FS +#include "ghc/filesystem.hpp" +namespace fs = ghc::filesystem; +#endif + +namespace rocketmq { + +Logger::Logger() : level_(Level::Info), file_size_(1048576 * 256), file_count_(16) {} + +Logger::~Logger() {} + +void Logger::setLevel(Level level) { level_ = level; } + +void Logger::init() { + std::call_once(init_once_, [this] { init0(); }); +} + +void Logger::init0() { + if (log_home_.empty()) { + char* home = getenv(USER_HOME_ENV); + log_home_.append(home).append("/logs/rocketmq/client.log"); + } + + // Create directories if necessary + fs::path log_file(log_home_); + fs::path log_dir = log_file.parent_path(); + if (!fs::exists(log_dir)) { + if (!fs::create_directories(log_dir)) { + std::cerr << "Failed to mkdir " << log_dir << std::endl; + abort(); + } + } + + if (pattern_.empty()) { + pattern_ = DEFAULT_PATTERN; + } + + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::info); + console_sink->set_pattern(pattern_); + auto file_sink = std::make_shared(log_home_, file_size_, file_count_); + file_sink->set_pattern(pattern_); + + switch (level_) { + case Trace: + file_sink->set_level(spdlog::level::trace); + break; + case Debug: + file_sink->set_level(spdlog::level::debug); + break; + case Info: + file_sink->set_level(spdlog::level::info); + break; + case Warn: + file_sink->set_level(spdlog::level::warn); + break; + case Error: + file_sink->set_level(spdlog::level::err); + break; + default: + file_sink->set_level(spdlog::level::critical); + break; + } + + // std::make_shared does not with initializer_list. + auto default_logger = + std::make_shared("rocketmq_logger", spdlog::sinks_init_list{console_sink, file_sink}); + + default_logger->set_level(spdlog::level::debug); + spdlog::flush_on(spdlog::level::info); + spdlog::flush_every(std::chrono::seconds(1)); + spdlog::set_default_logger(default_logger); +} + +Logger& getLogger() { + static Logger logger; + return logger; +} + +const char* Logger::USER_HOME_ENV = "HOME"; +const char* Logger::DEFAULT_PATTERN = "[%Y/%m/%d-%H:%M:%S.%e %z] [%n] [%^---%L---%$] [thread %t] %v %@"; +} // namespace rocketmq diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/rocketmq/AwaitPullCallback.cpp new file mode 100644 index 000000000..99ca912c4 --- /dev/null +++ b/src/main/cpp/rocketmq/AwaitPullCallback.cpp @@ -0,0 +1,31 @@ +#include "AwaitPullCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void AwaitPullCallback::onSuccess(const PullResult& pull_result) { + absl::MutexLock lk(&mtx_); + has_failure_ = false; + completed_ = true; + // TODO: optimize out messages copy here. + pull_result_ = pull_result; + cv_.SignalAll(); +} + +void AwaitPullCallback::onException(const MQException& e) { + absl::MutexLock lk(&mtx_); + has_failure_ = true; + completed_ = true; + cv_.SignalAll(); +} + +bool AwaitPullCallback::await() { + { + absl::MutexLock lk(&mtx_); + while (!completed_) { + cv_.Wait(&mtx_); + } + return !has_failure_; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel new file mode 100644 index 000000000..9a92c3942 --- /dev/null +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "rocketmq_library", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/rocketmq/include", + deps = [ + "//src/main/cpp/client:client_library", + "//src/main/cpp/log:log_library", + "//src/main/cpp/admin:admin_server_library", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BaseImpl.cpp b/src/main/cpp/rocketmq/BaseImpl.cpp new file mode 100644 index 000000000..61c7a1fbe --- /dev/null +++ b/src/main/cpp/rocketmq/BaseImpl.cpp @@ -0,0 +1,320 @@ +#include "BaseImpl.h" +#include "ClientManager.h" +#include "Signature.h" +#include "absl/strings/str_join.h" + +ROCKETMQ_NAMESPACE_BEGIN + +BaseImpl::BaseImpl(std::string group_name) + : ClientConfig(std::move(group_name)), state_(State::CREATED), top_addressing_(absl::make_unique()) { + topic_route_info_updater_ = std::bind(&BaseImpl::updateRouteInfo, this); + topic_route_info_updater_function_ = std::make_shared(&topic_route_info_updater_); + + name_server_list_updater_ = std::bind(&BaseImpl::renewNameServerList, this); + name_server_list_updater_function_ = std::make_shared(&name_server_list_updater_); +} + +void BaseImpl::start() { + State expected = CREATED; + if (!state_.compare_exchange_strong(expected, State::STARTING)) { + SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); + return; + } + + client_instance_ = ClientManager::getInstance().getClientInstance(*this); + client_instance_->start(); + top_addressing_->setUnitName(unit_name_); + bool update_name_server_list = false; + { + absl::MutexLock lock(&name_server_list_mtx_); + if (name_server_list_.empty()) { + update_name_server_list = true; + } + } + + if (update_name_server_list) { + // Acquire name server list immediately + renewNameServerList(); + + // Schedule to renew name server list periodically + SPDLOG_INFO("Name server list was empty. Schedule a task to fetch and renew periodically"); + bool scheduled = client_instance_->getScheduler().schedule(name_server_list_updater_function_, + std::chrono::milliseconds(0), std::chrono::seconds(30)); + if (scheduled) { + SPDLOG_INFO("Name server list updater scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule name server list updater"); + } + } + + bool scheduled = client_instance_->getScheduler().schedule(topic_route_info_updater_function_, + std::chrono::seconds(10), std::chrono::seconds(30)); + if (scheduled) { + SPDLOG_INFO("Topic route info updater scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule topic route info updater"); + } + state_.store(State::STARTED); +} + +void BaseImpl::activeHosts(absl::flat_hash_set& hosts) { + absl::MutexLock lk(&topic_route_table_mtx_); + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!hosts.contains(endpoint)) { + hosts.emplace(std::move(endpoint)); + } + } + } +} + +void BaseImpl::getRouteFor(const std::string& topic, const std::function& cb) { + TopicRouteDataPtr route = nullptr; + { + absl::MutexLock lock(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + route = topic_route_table_.at(topic); + } + } + + if (route) { + cb(route); + return; + } + + bool query_backend = true; + { + absl::MutexLock lk(&inflight_route_requests_mtx_); + { + absl::MutexLock route_table_lock(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + route = topic_route_table_.at(topic); + query_backend = false; + } + } + + if (query_backend) { + if (inflight_route_requests_.contains(topic)) { + inflight_route_requests_.at(topic).emplace_back(cb); + SPDLOG_DEBUG("Would reuse prior route request for topic={}", topic); + return; + } else { + std::vector> inflight{cb}; + inflight_route_requests_.insert({topic, inflight}); + SPDLOG_INFO("Create inflight route query cache for topic={}", topic); + } + } + } + + if (!query_backend && route) { + cb(route); + } else { + fetchRouteFor(topic, std::bind(&BaseImpl::onTopicRouteReady, this, topic, std::placeholders::_1)); + } +} + +void BaseImpl::debugNameServerChanges(const std::vector& list) { + std::string previous; + bool changed = false; + { + absl::MutexLock lock(&name_server_list_mtx_); + if (name_server_list_ != list) { + changed = true; + if (name_server_list_.empty()) { + previous.append("[]"); + } else { + previous = absl::StrJoin(name_server_list_.begin(), name_server_list_.end(), ";"); + } + } + } + std::string current = absl::StrJoin(list.begin(), list.end(), ";"); + if (changed) { + SPDLOG_INFO("Name server list changed. {} --> {}", previous, current); + } else { + SPDLOG_DEBUG("Name server list remains the same: {}", current); + } +} + +void BaseImpl::renewNameServerList() { + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); + return; + } + + std::vector list; + SPDLOG_DEBUG("Begin to renew name server list"); + if (!top_addressing_->fetchNameServerAddresses(list)) { + SPDLOG_WARN("Failed to fetch name server list"); + } else { + if (list.empty()) { + SPDLOG_WARN("Yuck, got an empty name server list"); + } else { + debugNameServerChanges(list); + { + absl::MutexLock lock(&name_server_list_mtx_); + if (name_server_list_ != list) { + name_server_list_.clear(); + name_server_list_.insert(name_server_list_.begin(), list.begin(), list.end()); + } + { + absl::MutexLock promise_lock(&name_server_promise_list_mtx_); + for (auto& promise : name_server_promise_list_) { + promise.set_value(name_server_list_); + } + name_server_promise_list_.clear(); + } + } + } + } +} + +bool BaseImpl::selectNameServer(std::string& selected, bool change) { + static uint32_t index = 0; + if (change) { + index++; + } + { + absl::MutexLock lock(&name_server_list_mtx_); + if (name_server_list_.empty()) { + return false; + } + uint32_t idx = index % name_server_list_.size(); + selected = name_server_list_[idx]; + } + return true; +} + +void BaseImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { + std::string name_server; + if (!selectNameServer(name_server)) { + SPDLOG_WARN("No name server available"); + return; + } + + auto callback = [this, topic, name_server, cb](bool ok, const TopicRouteDataPtr& route) { + if (!ok || !route) { + SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); + std::string name_server_changed; + if (selectNameServer(name_server_changed, true)) { + SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); + } + cb(nullptr); + return; + } + + SPDLOG_DEBUG("Apply callback of fetchRouteFor({}) since a valid route is fetched", topic); + cb(route); + }; + + QueryRouteRequest request; + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(topic); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_instance_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void BaseImpl::updateRouteInfo() { + if (State::STARTED != state_.load(std::memory_order_relaxed) && + State::STARTING != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + return; + } + + std::vector topics; + { + absl::MutexLock lock(&topic_route_table_mtx_); + for (const auto& entry : topic_route_table_) { + topics.push_back(entry.first); + } + } + + if (!topics.empty()) { + for (const auto& topic : topics) { + fetchRouteFor(topic, std::bind(&BaseImpl::updateRouteCache, this, topic, std::placeholders::_1)); + } + } + +#ifdef ENABLE_TRACING + updateTraceProvider(); +#endif + + SPDLOG_DEBUG("Topic route info updated"); +} + +void BaseImpl::heartbeat() { + absl::flat_hash_set hosts; + activeHosts(hosts); + if (hosts.empty()) { + SPDLOG_WARN("No hosts to send heartbeat to at present"); + return; + } + + HeartbeatRequest request; + prepareHeartbeatData(request); + if (request.heartbeats().empty()) { + SPDLOG_INFO("No heartbeat entries to send. Skip."); + return; + } + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + for (const auto& target : hosts) { + auto callback = [target](bool ok, const HeartbeatResponse& response) { + if (!ok) { + SPDLOG_WARN("Failed to send heartbeat request to {}", target); + return; + } + SPDLOG_DEBUG("Heartbeat to {} OK", target); + }; + client_instance_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } +} + +void BaseImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) { + updateRouteCache(topic, route); + + // Take all pending callbacks + std::vector> pending_requests; + { + absl::MutexLock lk(&inflight_route_requests_mtx_); + assert(inflight_route_requests_.contains(topic)); + auto& inflight_requests = inflight_route_requests_.at(topic); + pending_requests.insert(pending_requests.end(), inflight_requests.begin(), inflight_requests.end()); + inflight_route_requests_.erase(topic); + } + + SPDLOG_DEBUG("Apply cached callbacks with acquired route data for topic={}", topic); + for (const auto& cb : pending_requests) { + cb(route); + } +} + +void BaseImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) { + if (!route || route->partitions().empty()) { + SPDLOG_WARN("Yuck! route for {} is invalid", topic); + return; + } + + { + absl::MutexLock lk(&topic_route_table_mtx_); + if (!topic_route_table_.contains(topic)) { + topic_route_table_.insert({topic, route}); + SPDLOG_INFO("TopicRouteData for topic={} has changed. NONE --> {}", topic, route->debugString()); + } else { + TopicRouteDataPtr cached = topic_route_table_.at(topic); + if (*cached != *route) { + topic_route_table_.insert_or_assign(topic, route); + std::string previous = cached->debugString(); + SPDLOG_INFO("TopicRouteData for topic={} has changed. {} --> {}", topic, cached->debugString(), + route->debugString()); + } + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt new file mode 100644 index 000000000..683cd6d46 --- /dev/null +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -0,0 +1,39 @@ +add_library(rocketmq_obj OBJECT) +target_sources(rocketmq_obj + PRIVATE + SendCallbacks.cpp + AwaitPullCallback.cpp + BaseImpl.cpp + ConsumeMessageService.cpp + ConsumeMessageConcurrentlyService.cpp + ConsumeMessageOrderlyService.cpp + CredentialsProvider.cpp + DefaultMQProducer.cpp + DefaultMQProducerImpl.cpp + DefaultMQPushConsumer.cpp + DefaultMQPushConsumerImpl.cpp + DefaultMQPullConsumer.cpp + DefaultMQPullConsumerImpl.cpp + MessageClientIDSetter.cpp + ProcessQueue.cpp + TransactionImpl.cpp) +target_include_directories(rocketmq_obj + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(rocketmq_obj + PUBLIC + client_library + logger_library + thread_pool) + +add_library(rocketmq_library + STATIC + $) +target_include_directories(rocketmq_library + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(rocketmq_library + PUBLIC + client_library + CURL::libcurl + logger_library) diff --git a/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp new file mode 100644 index 000000000..49e44e56a --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp @@ -0,0 +1,286 @@ +#include "ConsumeMessageService.h" +#include "DefaultMQPushConsumerImpl.h" +#include "LoggerImpl.h" +#include "Protocol.h" +#include "ThreadPool.h" +#include "TracingUtility.h" +#include "absl/strings/str_join.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeMessageConcurrentlyService::ConsumeMessageConcurrentlyService(std::weak_ptr consumer, + int thread_count, + MQMessageListener* message_listener_ptr) + : thread_count_(thread_count), message_listener_ptr_(message_listener_ptr), consumer_weak_ptr_(std::move(consumer)), + state_(State::CREATED) { + pool_ = std::make_shared(thread_count_); +} + +void ConsumeMessageConcurrentlyService::start() { + // Loop each process queue + // If there is message to dispatch + // Then + // If Not rate limiter configured or has permits + // Submit consume task + // Else + // Continue + // Else + // Continue + // Wait on condition variable + + // Once consume task completes + // If the topic were configured with rate limiter + // Then Wake up dispatch_thread + + state_.store(State::STARTING, std::memory_order_relaxed); + dispatch_thread_ = std::thread([this] { + while (State::STOPPED != state_.load(std::memory_order_relaxed)) { + dispatch0(); + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.WaitWithTimeout(&dispatch_mtx_, absl::Milliseconds(100)); + } + } + }); +} + +void ConsumeMessageConcurrentlyService::shutdown() { + state_.store(State::STOPPED, std::memory_order_relaxed); + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.SignalAll(); + } + if (dispatch_thread_.joinable()) { + dispatch_thread_.join(); + } + ConsumeMessageService::shutdown(); + pool_.reset(); + SPDLOG_DEBUG("ConsumeMessageConcurrentlyService thread pool stop"); +} + +void ConsumeMessageConcurrentlyService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); + return; + } + std::shared_ptr consumer_impl_ptr = process_queue_ptr->getCallbackOwner().lock(); + + if (!consumer_impl_ptr) { + return; + } + + std::string topic = process_queue_ptr->topic(); + std::shared_ptr> rate_limiter = rateLimiter(topic); + bool has_more = true; + while (has_more) { + std::vector messages; + uint32_t batch_size = consumer_impl_ptr->consumeBatchSize(); + uint32_t acquired; + if (rate_limiter) { + uint32_t permits_to_acquire = + std::min({batch_size, rate_limiter->available(), process_queue_ptr->cachedMessagesSize()}); + acquired = rate_limiter->acquire(permits_to_acquire); + if (!acquired) { + SPDLOG_DEBUG("Throttled: failed to acquire permits from rate limiter"); + break; + } + } else if (permits < 0) { + acquired = batch_size; + } else { + SPDLOG_WARN("submitConsumeTask with permits = 0"); + break; + } + + has_more = process_queue_ptr->consume(acquired, messages); + if (messages.empty()) { + assert(!has_more); + break; + } + + // In case custom executor is used. + const Executor& custom_executor = consumer_impl_ptr->customExecutor(); + if (custom_executor) { + std::function consume_task = + std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, process_queue, messages); + custom_executor(consume_task); + SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); + continue; + } + + // submit batch message + std::function&)> consumer_task = + std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, std::placeholders::_1, std::placeholders::_2); + SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); + pool_->enqueue(consumer_task, process_queue, messages); + } +} + +MessageListenerType ConsumeMessageConcurrentlyService::getConsumeMsgServiceListenerType() { + return messageListenerConcurrently; +} + +void ConsumeMessageConcurrentlyService::stopThreadPool() { + // do nothing, ThreadPool destructor will be invoked automatically +} + +void ConsumeMessageConcurrentlyService::dispatch() { + absl::MutexLock lk(&dispatch_mtx_); + // Wake up dispatch_thread_ + dispatch_cv_.Signal(); +} + +void ConsumeMessageConcurrentlyService::dispatch0() { + std::shared_ptr consumer = consumer_weak_ptr_.lock(); + if (!consumer) { + SPDLOG_WARN("The consumer was destructed"); + return; + } + + auto lambda = [this](const ProcessQueueSharedPtr& process_queue_ptr) { + // Note: we have already got process_queue_table_mtx_ locked + std::string topic = process_queue_ptr->topic(); + if (hasConsumeRateLimiter(topic)) { + std::shared_ptr> rate_limiter = rateLimiter(topic); + if (rate_limiter) { + uint32_t permits = rate_limiter->available(); + if (permits) { + submitConsumeTask(process_queue_ptr, permits); + } + } else { + submitConsumeTask(process_queue_ptr, -1); + } + } else { + submitConsumeTask(process_queue_ptr, -1); + } + }; + consumer->iterateProcessQueue(lambda); +} + +void ConsumeMessageConcurrentlyService::consumeTask(const ProcessQueueWeakPtr& process_queue, + const std::vector& msgs) { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue.lock(); + if (!process_queue_shared_ptr || msgs.empty()) { + return; + } + std::string topic = msgs.begin()->getTopic(); + ConsumeStatus status; + std::shared_ptr consumer_shared_ptr = consumer_weak_ptr_.lock(); + // consumer may be destructed here. + if (!consumer_shared_ptr) { + return; + } + // consumer does not start yet. +#ifdef ENABLE_TRACING + nostd::shared_ptr tracer = consumer_shared_ptr->getTracer(); + if (!tracer) { + return; + } + auto system_start = std::chrono::system_clock::now(); +#endif + + auto steady_start = std::chrono::steady_clock::now(); + + try { + assert(nullptr != message_listener_ptr_); + status = message_listener_ptr_->consumeMessage(msgs); + } catch (...) { + status = RECONSUME_LATER; + SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); + } + + auto duration = std::chrono::steady_clock::now() - steady_start; +#ifdef ENABLE_TRACING + std::chrono::microseconds average_duration = + std::chrono::microseconds(MixAll::microsecondsOf(duration) / msgs.size()); +#endif + + std::vector msg_id_list; + msg_id_list.reserve(msgs.size()); + + for (const auto& msg : msgs) { + msg_id_list.emplace_back(msg.getMsgId()); +#ifdef ENABLE_TRACING + nostd::shared_ptr span = nostd::shared_ptr(nullptr); + trace::EndSpanOptions end_options; + if (consumer_shared_ptr->isTracingEnabled()) { + const std::string& serialized_span_context = msg.traceContext(); + trace::SpanContext span_context = TracingUtility::extractContextFromTraceParent(serialized_span_context); + trace::StartSpanOptions start_options; + start_options.start_system_time = + opentelemetry::core::SystemTimestamp(system_start + i * std::chrono::microseconds(average_duration)); + start_options.start_steady_time = + opentelemetry::core::SteadyTimestamp(steady_start + i * std::chrono::microseconds(average_duration)); + start_options.parent = span_context; + end_options.end_steady_time = + opentelemetry::core::SteadyTimestamp(steady_start + (i + 1) * std::chrono::microseconds(average_duration)); + span = tracer->StartSpan("ConsumeMessage", start_options); + } +#endif + + process_queue_shared_ptr->messageCachedNumber().fetch_sub(1, std::memory_order_relaxed); + +#ifdef ENABLE_TRACING + if (span) { + span->SetAttribute(TracingUtility::get().expired_, false); + span->SetAttribute(TracingUtility::get().success_, CONSUME_SUCCESS == status); + span->End(end_options); + } +#endif + + if (MessageModel::CLUSTERING == consumer_shared_ptr->messageModel()) { + if (status == CONSUME_SUCCESS) { + auto ack_callback = [process_queue_shared_ptr, msg](bool ok) { + if (ok) { + SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_shared_ptr->simpleName(), + msg.getMsgId()); + } else { + SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]", + process_queue_shared_ptr->simpleName(), msg.getMsgId()); + } + }; + consumer_shared_ptr->ack(msg, ack_callback); + } else { + auto nack_callback = [process_queue_shared_ptr, msg](bool ok) { + if (ok) { + SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_shared_ptr->simpleName(), + msg.getMsgId()); + } else { + SPDLOG_INFO( + "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Message will be re-consumed " + "after default invisible time", + process_queue_shared_ptr->simpleName(), msg.getMsgId()); + } + }; + consumer_shared_ptr->nack(msg, nack_callback); + } + } + } + + if (MessageModel::BROADCASTING == consumer_shared_ptr->messageModel()) { + if (consumer_shared_ptr->offset_store_) { + consumer_shared_ptr->offset_store_->updateOffset(process_queue_shared_ptr->getMQMessageQueue(), + process_queue_shared_ptr->nextOffset()); + } + } + + // Log client consume-time costs + SPDLOG_DEBUG("Business callback spent {}ms processing {} messages. Message-identifier-list: {}", + MixAll::millisecondsOf(duration), msgs.size(), + absl::StrJoin(msg_id_list.begin(), msg_id_list.end(), ",")); + + bool need_dispatch = false; + { + absl::MutexLock lk(&rate_limiter_table_mtx_); + if (rate_limiter_table_.contains(topic)) { + need_dispatch = true; + } + } + + if (need_dispatch) { + dispatch(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp new file mode 100644 index 000000000..2b4b3e13f --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp @@ -0,0 +1,39 @@ +#include "ConsumeMessageService.h" +#include "DefaultMQPushConsumerImpl.h" +#include "ThreadPool.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeMessageOrderlyService ::ConsumeMessageOrderlyService( + const std::weak_ptr&& consumer_impl_ptr, int thread_count, + MQMessageListener* message_listener_ptr) + : consumer_weak_ptr_(consumer_impl_ptr), message_listener_ptr_(message_listener_ptr), thread_count_(thread_count) { + pool_ = std::make_shared(thread_count_); + // Suppress field not used warning for now + (void)message_listener_ptr_; +} + +void ConsumeMessageOrderlyService::start() { + // pool_ = std::make_shared(thread_count_); +} + +void ConsumeMessageOrderlyService::shutdown() { ConsumeMessageService::shutdown(); } + +void ConsumeMessageOrderlyService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) {} + +MessageListenerType ConsumeMessageOrderlyService::getConsumeMsgServiceListenerType() { + return MessageListenerType::messageListenerOrderly; +} + +void ConsumeMessageOrderlyService::stopThreadPool() { + // do nothing, ThreadPool destructor will be invoked automatically +} + +void ConsumeMessageOrderlyService::dispatch() { + // Not implemented. +} + +void ConsumeMessageOrderlyService::consumeTask(const ProcessQueueWeakPtr& process_queue, + const std::vector& msgs) {} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageService.cpp b/src/main/cpp/rocketmq/ConsumeMessageService.cpp new file mode 100644 index 000000000..2a5729ffc --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeMessageService.cpp @@ -0,0 +1,27 @@ +#include "ConsumeMessageService.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void ConsumeMessageService::throttle(const std::string& topic, uint32_t threshold) { + absl::MutexLock lk(&rate_limiter_table_mtx_); + std::shared_ptr> rate_limiter = std::make_shared>(threshold); + rate_limiter_table_.insert_or_assign(topic, rate_limiter); + rate_limiter_observer_.subscribe(rate_limiter); +} + +void ConsumeMessageService::shutdown() { rate_limiter_observer_.stop(); } + +bool ConsumeMessageService::hasConsumeRateLimiter(const std::string& topic) const { + absl::MutexLock lk(&rate_limiter_table_mtx_); + return rate_limiter_table_.contains(topic); +} + +std::shared_ptr> ConsumeMessageService::rateLimiter(const std::string& topic) const { + if (!hasConsumeRateLimiter(topic)) { + return nullptr; + } + absl::MutexLock lk(&rate_limiter_table_mtx_); + return rate_limiter_table_[topic]; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp new file mode 100644 index 000000000..29e70e726 --- /dev/null +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -0,0 +1,97 @@ +#include "rocketmq/CredentialsProvider.h" +#include "MixAll.h" +#include "absl/strings/match.h" +#include "ghc/filesystem.hpp" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +StaticCredentialsProvider::StaticCredentialsProvider(std::string access_key, std::string access_secret) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)) {} + +Credentials StaticCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } + +std::chrono::system_clock::duration StaticCredentialsProvider::refreshInterval() { + return std::chrono::system_clock::duration::zero(); +} + +const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY = "ROCKETMQ_ACCESS_KEY"; +const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET = "ROCKETMQ_ACCESS_SECRET"; + +EnvironmentVariablesCredentialsProvider::EnvironmentVariablesCredentialsProvider() { + char* key = getenv(ENVIRONMENT_ACCESS_KEY); + if (key) { + access_key_ = std::string(key); + } + + char* secret = getenv(ENVIRONMENT_ACCESS_SECRET); + if (secret) { + access_secret_ = std::string(secret); + } +} + +Credentials EnvironmentVariablesCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} + +const char* ConfigFileCredentialsProvider::CREDENTIAL_FILE_ = "rocketmq/credentials"; + +const char* ConfigFileCredentialsProvider::ACCESS_KEY_FIELD_NAME = "AccessKey"; +const char* ConfigFileCredentialsProvider::ACCESS_SECRET_FIELD_NAME = "AccessSecret"; + +ConfigFileCredentialsProvider::ConfigFileCredentialsProvider() { + std::string config_file; + if (MixAll::homeDirectory(config_file)) { + std::string path_separator(1, ghc::filesystem::path::preferred_separator); + if (!absl::EndsWith(config_file, path_separator)) { + config_file.append(1, ghc::filesystem::path::preferred_separator); + } + config_file.append(CREDENTIAL_FILE_); + ghc::filesystem::path config_file_path(config_file); + std::error_code ec; + if (!ghc::filesystem::exists(config_file_path, ec) || ec) { + SPDLOG_WARN("Config file[{}] does not exist.", config_file); + return; + } + std::ifstream config_file_stream; + config_file_stream.open(config_file.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + if (config_file_stream.good()) { + auto size = config_file_stream.tellg(); + std::string content(size, '\0'); + config_file_stream.seekg(0); + config_file_stream.read(&content[0], size); + config_file_stream.close(); + google::protobuf::Struct root; + google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(content, &root); + if (status.ok()) { + auto&& fields = root.fields(); + if (fields.contains(ACCESS_KEY_FIELD_NAME)) { + access_key_ = fields.at(ACCESS_KEY_FIELD_NAME).string_value(); + } + + if (fields.contains(ACCESS_SECRET_FIELD_NAME)) { + access_secret_ = fields.at(ACCESS_SECRET_FIELD_NAME).string_value(); + } + } else { + SPDLOG_WARN("Failed to parse credential JSON config file. Message: {}", status.message().data()); + } + } else { + SPDLOG_WARN("Failed to open file: {}", config_file); + return; + } + } +} + +Credentials ConfigFileCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } + +std::chrono::system_clock::duration ConfigFileCredentialsProvider::refreshInterval() { + return std::chrono::seconds(10); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp new file mode 100644 index 000000000..760734fd5 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -0,0 +1,102 @@ +#include "rocketmq/DefaultMQProducer.h" +#include "DefaultMQProducerImpl.h" +#include "MixAll.h" + +#include "absl/strings/str_split.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQProducer::DefaultMQProducer(const std::string& group_name) + : impl_(std::make_shared(group_name)) {} + +void DefaultMQProducer::start() { impl_->start(); } + +void DefaultMQProducer::shutdown() { impl_->shutdown(); } + +int DefaultMQProducer::getSendMsgTimeout() const { + return static_cast(absl::ToInt64Milliseconds(impl_->getIoTimeout())); +} + +void DefaultMQProducer::setSendMsgTimeout(int timeout_millis) { + impl_->setIoTimeout(absl::Milliseconds(timeout_millis)); +} + +void DefaultMQProducer::setNamesrvAddr(const std::string& name_server_address_list) { + std::vector name_server_list = absl::StrSplit(name_server_address_list, ';'); + impl_->setNameServerList(name_server_list); +} + +void DefaultMQProducer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } + +void DefaultMQProducer::setInstanceName(const std::string& instance_name) { impl_->setInstanceName(instance_name); } + +void DefaultMQProducer::enableTracing(bool enabled) { impl_->enableTracing(enabled); } + +bool DefaultMQProducer::isTracingEnabled() { return impl_->isTracingEnabled(); } + +SendResult DefaultMQProducer::send(const rocketmq::MQMessage& message, bool filter_active_broker) { + return impl_->send(message); +} + +SendResult DefaultMQProducer::send(const MQMessage& msg, const MQMessageQueue& mq) { return impl_->send(msg, mq); } + +SendResult DefaultMQProducer::send(const MQMessage& msg, MessageQueueSelector* selector, void* arg) { + return impl_->send(msg, selector, arg); +} + +SendResult DefaultMQProducer::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, + bool select_active_broker) { + return impl_->send(message, selector, arg, retry_times); +} + +void DefaultMQProducer::send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker) { + impl_->send(message, send_callback); +} + +void DefaultMQProducer::send(const MQMessage& message, const MQMessageQueue& message_queue, + SendCallback* send_callback) { + impl_->send(message, message_queue, send_callback); +} + +void DefaultMQProducer::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, + SendCallback* send_callback) { + impl_->send(message, selector, arg, send_callback); +} + +void DefaultMQProducer::sendOneway(const MQMessage& message, bool select_active_broker) { impl_->sendOneway(message); } + +void DefaultMQProducer::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { + impl_->sendOneway(message, message_queue); +} + +void DefaultMQProducer::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { + impl_->sendOneway(message, selector, arg); +} + +void DefaultMQProducer::setRetryTimes(int retry_times) { impl_->maxAttemptTimes(retry_times); } + +int DefaultMQProducer::getRetryTimes() const { return impl_->maxAttemptTimes(); } + +std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { + return impl_->getTopicMessageQueueInfo(topic); +} + +void DefaultMQProducer::setUnitName(std::string unit_name) { impl_->setUnitName(std::move(unit_name)); } + +const std::string& DefaultMQProducer::getUnitName() { return impl_->getUnitName(); } + +uint32_t DefaultMQProducer::compressBodyThreshold() const { return impl_->compressBodyThreshold(); } + +void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { impl_->compressBodyThreshold(threshold); } + +void DefaultMQProducer::arn(const std::string& arn) { impl_->arn(arn); } + +void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQProducer::setRegion(const std::string& region) { + impl_->region(region); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp new file mode 100644 index 000000000..677ead3a8 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp @@ -0,0 +1,481 @@ +#include "DefaultMQProducerImpl.h" +#include "MessageClientIDSetter.h" +#include "MessageSystemFlag.h" +#include "Metadata.h" +#include "MixAll.h" +#include "Protocol.h" +#include "RequestIdGenerator.h" +#include "SendCallbacks.h" +#include "SendMessageContext.h" +#include "Signature.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQClientException.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQProducerImpl::DefaultMQProducerImpl(std::string group_name) + : BaseImpl(std::move(group_name)), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { + // TODO: initialize client_config_ and fault_strategy_ +} + +DefaultMQProducerImpl::~DefaultMQProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); } + +void DefaultMQProducerImpl::start() { + BaseImpl::start(); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected producer state: {}", state_.load(std::memory_order_relaxed)); + return; + } + + client_instance_->addClientObserver(shared_from_this()); +} + +void DefaultMQProducerImpl::shutdown() { client_instance_.reset(); } + +bool DefaultMQProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } + +void DefaultMQProducerImpl::ensureRunning() const { + if (!isRunning()) { + THROW_MQ_EXCEPTION(MQClientException, "Invoke #start() first", ILLEGAL_STATE); + } +} + +void DefaultMQProducerImpl::validate(const MQMessage& message) {} + +std::string DefaultMQProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue) { + request.mutable_message()->mutable_topic()->set_arn(arn_); + request.mutable_message()->mutable_topic()->set_name(message.getTopic()); + + auto system_attribute = request.mutable_message()->mutable_system_attribute(); + + // Born-time + auto duration = absl::Now() - absl::UnixEpoch(); + int64_t seconds = absl::ToInt64Seconds(duration); + system_attribute->mutable_born_timestamp()->set_seconds(seconds); + system_attribute->mutable_born_timestamp()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + + system_attribute->set_born_host(UtilAll::getHostIPv4()); + + system_attribute->mutable_producer_group()->set_arn(arn_); + system_attribute->mutable_producer_group()->set_name(group_name_); + + // Set system flag if the message is transactional + const auto& properties = message.getProperties(); + auto search = properties.find(MixAll::PROPERTY_TRANSACTION_PREPARED_); + // TODO: set message type for normal/order/delay + if (search != properties.end()) { + const std::string& transactional_flag = search->second; + if ("true" == transactional_flag) { + system_attribute->set_message_type(rmq::MessageType::TRANSACTION); + } + } + + if (message.bodyLength() >= compress_body_threshold_) { + std::string compressed_body; + UtilAll::compress(message.getBody(), compressed_body); + request.mutable_message()->set_body(compressed_body); + system_attribute->set_body_encoding(rmq::Encoding::GZIP); + } else { + request.mutable_message()->set_body(message.getBody()); + system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); + } + + for (auto& item : message.getProperties()) { + request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); + } + + // Create unique message-id + std::string message_id = MessageClientIDSetter::createUniqId(); + system_attribute->set_message_id(message_id); + system_attribute->set_partition_id(message_queue.getQueueId()); + + SPDLOG_DEBUG("SendMessageRequest: {}", request.DebugString()); + return message_id; +} + +SendResult DefaultMQProducerImpl::send(const MQMessage& message) { + ensureRunning(); + auto topic_publish_info = getPublishInfo(message.getTopic()); + if (!topic_publish_info) { + THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + } + + std::vector message_queue_list; + takeMessageQueuesRoundRobin(topic_publish_info, message_queue_list, max_attempt_times_); + if (message_queue_list.empty()) { + THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + } + + AwaitSendCallback callback; + send0(message, &callback, message_queue_list, max_attempt_times_); + callback.await(); + + if (callback) { + return callback.sendResult(); + } + THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); +} + +SendResult DefaultMQProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue) { + ensureRunning(); + std::vector message_queue_list{message_queue}; + AwaitSendCallback callback; + send0(message, &callback, message_queue_list, max_attempt_times_); + callback.await(); + if (callback) { + return callback.sendResult(); + } + THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); +} + +SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg) { + ensureRunning(); + std::vector message_queue_list; + executeMessageQueueSelector(message, selector, arg, message_queue_list); + if (message_queue_list.empty()) { + THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + } + AwaitSendCallback callback; + send0(message, &callback, message_queue_list, max_attempt_times_); + callback.await(); + if (callback) { + return callback.sendResult(); + } + THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); +} + +SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, + int max_attempt_times) { + ensureRunning(); + std::vector message_queue_list; + executeMessageQueueSelector(message, selector, arg, message_queue_list); + if (message_queue_list.empty()) { + THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + } + AwaitSendCallback callback; + send0(message, &callback, message_queue_list, max_attempt_times); + callback.await(); + if (callback) { + return callback.sendResult(); + } + THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); +} + +void DefaultMQProducerImpl::send(const MQMessage& message, SendCallback* cb) { + ensureRunning(); + auto callback = [this, message, cb](const TopicPublishInfoPtr& publish_info) { + if (!publish_info) { + MQClientException e("Failed to acquire topic route data", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); + cb->onException(e); + return; + } + + std::vector message_queue_list; + takeMessageQueuesRoundRobin(publish_info, message_queue_list, max_attempt_times_); + send0(message, cb, message_queue_list, max_attempt_times_); + }; + + asyncPublishInfo(message.getTopic(), callback); +} + +void DefaultMQProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue, + SendCallback* callback) { + ensureRunning(); + std::vector message_queue_list{message_queue}; + send0(message, callback, message_queue_list, max_attempt_times_); +} + +void DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, + SendCallback* callback) { + ensureRunning(); + + auto cb = [this, message, selector, callback, arg](const TopicPublishInfoPtr& ptr) { + if (!ptr) { + MQClientException e("Failed to acquire topic route", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); + callback->onException(e); + return; + } + + MQMessageQueue queue = selector->select(ptr->getMessageQueueList(), message, arg); + std::vector message_queue_list{queue}; + + send0(message, callback, message_queue_list, max_attempt_times_); + }; + + asyncPublishInfo(message.getTopic(), cb); +} + +void DefaultMQProducerImpl::sendOneway(const MQMessage& message) { + ensureRunning(); + auto callback = [this, message](const TopicPublishInfoPtr& ptr) { + if (!ptr) { + SPDLOG_WARN("Failed acquire topic publish info for {}", message.getTopic()); + return; + } + MQMessageQueue message_queue; + absl::flat_hash_set clientsHosts; + client_instance_->getAllInactiveRpcClientHost(clientsHosts); + ptr->selectOneActiveMessageQueue(clientsHosts, message_queue); + std::vector list{message_queue}; + send0(message, onewaySendCallback(), list, 1); + }; + asyncPublishInfo(message.getTopic(), callback); +} + +void DefaultMQProducerImpl::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { + ensureRunning(); + std::vector list{message_queue}; + send0(message, onewaySendCallback(), list, 1); +} + +void DefaultMQProducerImpl::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { + ensureRunning(); + + auto callback = [this, message, selector, arg](const TopicPublishInfoPtr& ptr) { + if (!ptr) { + SPDLOG_WARN("No topic route for {}", message.getTopic()); + return; + } + MQMessageQueue queue = selector->select(ptr->getMessageQueueList(), message, arg); + std::vector list{queue}; + send0(message, onewaySendCallback(), list, 1); + }; + + asyncPublishInfo(message.getTopic(), callback); +} + +void DefaultMQProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, + int max_attempt_times) { + assert(callback); + if (list.empty()) { + MQClientException e("Topic route not found", -1, __FILE__, __LINE__); + callback->onException(e); + return; + } + + if (max_attempt_times <= 0) { + MQClientException e("Retry times illegal", BAD_CONFIGURATION, __FILE__, __LINE__); + callback->onException(e); + return; + } + + const std::string& target = list[0].serviceAddress(); + if (target.empty()) { + SPDLOG_DEBUG("Unable to resolve broker address"); + THROW_MQ_EXCEPTION(MQClientException, "Failed to resolve broker address from topic route", + FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE); + } + + SendMessageRequest request; + // Unique message-id is generated using IP, timestamp, hash-code and sequence during the transformation + wrapSendMessageRequest(message, request, list[0]); + + absl::flat_hash_map metadata; + + // Sign the request. Metadata entries will be part of HTT2 headers frame + Signature::sign(this, metadata); + + if (max_attempt_times == 1) { + client_instance_->send(target, metadata, request, callback); + return; + } + + auto retry_callback = + new RetrySendCallback(client_instance_, metadata, request, max_attempt_times, callback, std::move(list)); + client_instance_->send(target, metadata, request, retry_callback); +} + +bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, + const std::string& transaction_id, TransactionResolution resolution) { + + EndTransactionRequest request; + request.set_message_id(message_id); + request.set_transaction_id(transaction_id); + std::string action; + switch (resolution) { + case TransactionResolution::COMMIT: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); + action = "commit"; + break; + case TransactionResolution::ROLLBACK: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); + action = "rollback"; + break; + } + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + bool completed = false; + bool success = false; + absl::Mutex mtx; + absl::CondVar cv; + auto cb = [&](bool rpc_ok, const EndTransactionResponse& response) { + completed = true; + if (!rpc_ok) { + SPDLOG_WARN("Failed to send {} transaction request to {}", action, target); + success = false; + } else if (response.common().status().code() != google::rpc::Code::OK) { + success = false; + SPDLOG_WARN("Server[host={}] failed to {} transaction. Reason: {}", target, action, + response.common().DebugString()); + } else { + success = true; + } + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + client_instance_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); + { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + return success; +} + +bool DefaultMQProducerImpl::isClientActive(const std::string& target) { + return client_instance_->isRpcClientActive(target); +} + +std::unique_ptr DefaultMQProducerImpl::prepare(const MQMessage& message) { + try { + SendResult send_result = send(message); + return std::unique_ptr(new TransactionImpl(send_result.getMsgId(), send_result.getTransactionId(), + send_result.getMessageQueue().serviceAddress(), + DefaultMQProducerImpl::shared_from_this())); + } catch (const MQClientException& e) { + SPDLOG_ERROR("Failed to send transaction message. Cause: {}", e.what()); + return nullptr; + } +} + +bool DefaultMQProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, + const std::string& target) { + return endTransaction0(target, message_id, transaction_id, TransactionResolution::COMMIT); +} + +bool DefaultMQProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, + const std::string& target) { + return endTransaction0(target, message_id, transaction_id, TransactionResolution::ROLLBACK); +} + +void DefaultMQProducerImpl::asyncPublishInfo(const std::string& topic, + const std::function& cb) { + TopicPublishInfoPtr ptr; + { + absl::MutexLock lock(&topic_publish_info_mtx_); + if (topic_publish_info_table_.contains(topic)) { + ptr = topic_publish_info_table_.at(topic); + } + } + if (ptr) { + cb(ptr); + } else { + auto callback = [this, topic, cb](const TopicRouteDataPtr& route) { + if (!route) { + cb(nullptr); + return; + } + + auto publish_info = std::make_shared(topic, route); + { + absl::MutexLock lk(&topic_publish_info_mtx_); + topic_publish_info_table_.insert_or_assign(topic, publish_info); + } + cb(publish_info); + }; + getRouteFor(topic, callback); + } +} + +TopicPublishInfoPtr DefaultMQProducerImpl::getPublishInfo(const std::string& topic) { + bool complete = false; + absl::Mutex mtx; + absl::CondVar cv; + TopicPublishInfoPtr topic_publish_info; + auto cb = [&](const TopicPublishInfoPtr& ptr) { + absl::MutexLock lk(&mtx); + topic_publish_info = ptr; + complete = true; + cv.SignalAll(); + }; + asyncPublishInfo(topic, cb); + + // Wait till acquiring topic publish info completes + while (!complete) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + return topic_publish_info; +} + +bool DefaultMQProducerImpl::executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, + void* arg, std::vector& result) { + TopicPublishInfoPtr publish_info; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto callback = [&](const TopicPublishInfoPtr& ptr) { + absl::MutexLock lk(&mtx); + publish_info = ptr; + completed = true; + cv.SignalAll(); + }; + asyncPublishInfo(message.getTopic(), callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + if (!publish_info) { + THROW_MQ_EXCEPTION(MQClientException, "Failed to acquire topic route data", NO_TOPIC_ROUTE_INFO); + } + + MQMessageQueue queue = selector->select(publish_info->getMessageQueueList(), message, arg); + result.emplace_back(std::move(queue)); + return true; +} + +void DefaultMQProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, + std::vector& message_queues, int number) { + assert(publish_info); + absl::flat_hash_set isolated; + client_instance_->getAllInactiveRpcClientHost(isolated); + publish_info->takeMessageQueues(isolated, message_queues, number); +} + +std::vector DefaultMQProducerImpl::getTopicMessageQueueInfo(const std::string& topic) { + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + TopicPublishInfoPtr ptr; + auto await_callback = [&](const TopicPublishInfoPtr& publish_info) { + absl::MutexLock lk(&mtx); + ptr = publish_info; + completed = true; + cv.SignalAll(); + }; + asyncPublishInfo(topic, await_callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + if (!ptr) { + THROW_MQ_EXCEPTION(MQClientException, "Failed to acquire topic publish_info", NO_TOPIC_ROUTE_INFO); + } + + return ptr->getMessageQueueList(); +} + +void DefaultMQProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + rmq::HeartbeatEntry entry; + entry.mutable_producer_group()->mutable_group()->set_arn(arn_); + entry.mutable_producer_group()->mutable_group()->set_name(group_name_); + request.mutable_heartbeats()->Add(std::move(entry)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp new file mode 100644 index 000000000..338a977c0 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -0,0 +1,42 @@ +#include "rocketmq/DefaultMQPullConsumer.h" +#include "AwaitPullCallback.h" +#include "DefaultMQPullConsumerImpl.h" +#include "absl/strings/str_split.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DefaultMQPullConsumer::DefaultMQPullConsumer(const std::string& group_name) + : impl_(std::make_shared(group_name)) {} + +void DefaultMQPullConsumer::start() { impl_->start(); } + +void DefaultMQPullConsumer::shutdown() { impl_->shutdown(); } + +std::future> DefaultMQPullConsumer::queuesFor(const std::string& topic) { + return impl_->queuesFor(topic); +} + +std::future DefaultMQPullConsumer::queryOffset(const OffsetQuery& query) { return impl_->queryOffset(query); } + +bool DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullResult& pull_result) { + auto callback = absl::make_unique(pull_result); + pull(query, callback.get()); + return callback->await(); +} + +void DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullCallback* callback) { + impl_->pull(query, callback); +} + +void DefaultMQPullConsumer::setArn(const std::string& arn) { impl_->arn(arn); } + +void DefaultMQPullConsumer::setCredentialsProvider(std::shared_ptr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQPullConsumer::setNamesrvAddr(const std::string& name_srv) { + std::vector name_server_list = absl::StrSplit(name_srv, absl::ByChar(';')); + impl_->setNameServerList(name_server_list); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp new file mode 100644 index 000000000..e5339753e --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp @@ -0,0 +1,147 @@ +#include "DefaultMQPullConsumerImpl.h" +#include "ClientManager.h" +#include "Signature.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void DefaultMQPullConsumerImpl::start() { + BaseImpl::start(); + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); + return; + } + client_instance_->addClientObserver(shared_from_this()); +} + +void DefaultMQPullConsumerImpl::shutdown() {} + +std::future> DefaultMQPullConsumerImpl::queuesFor(const std::string& topic) { + auto promise = std::make_shared>>(); + { + absl::MutexLock lk(&topic_route_table_mtx_); + if (topic_route_table_.contains(topic)) { + TopicRouteDataPtr topic_route = topic_route_table_.at(topic); + auto partitions = topic_route->partitions(); + std::vector message_queues; + message_queues.reserve(partitions.size()); + for (const auto& partition : partitions) { + message_queues.emplace_back(partition.asMessageQueue()); + } + promise->set_value(std::move(message_queues)); + return promise->get_future(); + } + } + auto callback = [promise](const TopicRouteDataPtr& route) { + if (route) { + std::vector message_queues; + for (const auto& partition : route->partitions()) { + message_queues.emplace_back(partition.asMessageQueue()); + } + promise->set_value(message_queues); + } + }; + getRouteFor(topic, callback); + return promise->get_future(); +} + +std::future DefaultMQPullConsumerImpl::queryOffset(const OffsetQuery& query) { + QueryOffsetRequest request; + switch (query.policy) { + case QueryOffsetPolicy::BEGINNING: + request.set_policy(rmq::QueryOffsetPolicy::BEGINNING); + break; + case QueryOffsetPolicy::END: + request.set_policy(rmq::QueryOffsetPolicy::END); + break; + case QueryOffsetPolicy::TIME_POINT: + request.set_policy(rmq::QueryOffsetPolicy::TIME_POINT); + auto duration = absl::FromChrono(query.time_point.time_since_epoch()); + int64_t seconds = absl::ToInt64Seconds(duration); + request.mutable_time_point()->set_seconds(seconds); + request.mutable_time_point()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + break; + } + + request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(arn_); + + request.mutable_partition()->set_id(query.message_queue.getQueueId()); + QueryOffsetResponse response; + + absl::flat_hash_map metadata; + + Signature::sign(this, metadata); + + // TODO: Use std::unique_ptr if C++14 is adopted. + auto promise_ptr = std::make_shared>(); + auto callback = [promise_ptr](bool ok, const QueryOffsetResponse& response) { + if (ok) { + promise_ptr->set_value(response.offset()); + return; + } + MQClientException e("Failed to query offset", -1, __FILE__, __LINE__); + promise_ptr->set_exception(std::make_exception_ptr(e)); + }; + + client_instance_->queryOffset(query.message_queue.serviceAddress(), metadata, request, + absl::ToChronoMilliseconds(io_timeout_), callback); + return promise_ptr->get_future(); +} + +void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { + PullMessageRequest request; + request.set_offset(query.offset); + auto duration = absl::FromChrono(query.await_time); + int64_t seconds = absl::ToInt64Seconds(duration); + request.mutable_await_time()->set_seconds(seconds); + request.mutable_await_time()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + request.mutable_group()->set_name(group_name_); + request.mutable_group()->set_arn(arn_); + + request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(arn_); + request.mutable_partition()->set_id(query.message_queue.getQueueId()); + request.set_client_id(clientId()); + + std::string target_host = query.message_queue.serviceAddress(); + assert(!target_host.empty()); + + auto callback = [this, target_host, cb](bool ok, const PullMessageResponse& response) { + if (!ok) { + MQClientException exception(fmt::format("Server[{}] is not reachable", target_host), -1, __FILE__, __LINE__); + cb->onException(exception); + return; + } + + auto biz_status = response.common().status(); + if (google::rpc::Code::OK != biz_status.code()) { + MQClientException exception(response.common().status().message(), biz_status.code(), __FILE__, __LINE__); + cb->onException(exception); + return; + } + + std::vector messages; + for (const auto& item : response.messages()) { + MQMessageExt message_ext; + if (client_instance_->wrapMessage(item, message_ext)) { + messages.emplace_back(message_ext); + } + } + PullResult pull_result(response.min_offset(), response.max_offset(), response.next_offset(), std::move(messages)); + cb->onSuccess(pull_result); + }; + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + client_instance_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void DefaultMQPullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + rmq::HeartbeatEntry entry; + entry.mutable_producer_group()->mutable_group()->set_arn(arn_); + entry.mutable_producer_group()->mutable_group()->set_name(group_name_); + request.mutable_heartbeats()->Add(std::move(entry)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp new file mode 100644 index 000000000..64751b648 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -0,0 +1,82 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#include "DefaultMQPushConsumerImpl.h" +#include "absl/strings/str_split.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +static std::set consumerTable{}; +DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) : group_name_(group_name) { + if (consumerTable.count(group_name)) { + SPDLOG_ERROR("create consumer with same group name in a process, group name : {}", group_name); + std::string err_msg = "create consumer with same group name in a process, group name :" + group_name; + THROW_MQ_EXCEPTION(MQClientException, err_msg, -1); + } else { + impl_ = std::make_shared(group_name); + consumerTable.insert(group_name); + } +} + +void DefaultMQPushConsumer::start() { impl_->start(); } + +void DefaultMQPushConsumer::shutdown() { + impl_->shutdown(); + consumerTable.erase(group_name_); + SPDLOG_DEBUG("DefaultMQPushConsumerImpl shared_ptr use_count : {}", impl_.use_count()); +} + +void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type) { + impl_->subscribe(topic, expression, expression_type); +} + +void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { impl_->setConsumeFromWhere(policy); } + +void DefaultMQPushConsumer::registerMessageListener(MQMessageListener* listener) { + impl_->registerMessageListener(listener); +} + +void DefaultMQPushConsumer::setNamesrvAddr(const std::string& name_srv) { + std::vector name_server_list = absl::StrSplit(name_srv, ';'); + impl_->setNameServerList(name_server_list); +} + +void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } + +void DefaultMQPushConsumer::setConsumeThreadCount(int thread_count) { impl_->consumeThreadPoolSize(thread_count); } + +void DefaultMQPushConsumer::setInstanceName(const std::string& instance_name) { impl_->setInstanceName(instance_name); } + +int DefaultMQPushConsumer::getProcessQueueTableSize() { return impl_->getProcessQueueTableSize(); } + +void DefaultMQPushConsumer::setUnitName(std::string unit_name) { impl_->setUnitName(std::move(unit_name)); } + +const std::string& DefaultMQPushConsumer::getUnitName() const { return impl_->getUnitName(); } + +void DefaultMQPushConsumer::enableTracing(bool enabled) { impl_->enableTracing(enabled); } + +bool DefaultMQPushConsumer::isTracingEnabled() { return impl_->isTracingEnabled(); } + +void DefaultMQPushConsumer::setAsyncPull(bool) {} + +void DefaultMQPushConsumer::setMaxCacheMsgSizePerQueue(int max_cache_size) { + impl_->maxCachedMessageNumberPerQueue(max_cache_size); +} + +void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { impl_->consumeBatchSize(batch_size); } + +void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { impl_->setCustomExecutor(executor); } + +void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t threshold) { + impl_->setThrottle(topic, threshold); +} + +void DefaultMQPushConsumer::setArn(const char* arn) { impl_->arn(arn); } + +void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + impl_->setCredentialsProvider(std::move(credentials_provider)); +} + +void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { impl_->setMessageModel(message_model); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp new file mode 100644 index 000000000..e182091b5 --- /dev/null +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp @@ -0,0 +1,702 @@ +#include "DefaultMQPushConsumerImpl.h" +#include "ClientManager.h" + +#include "MessageAccessor.h" +#include "MixAll.h" +#include "Signature.h" +#include "rocketmq/MQClientException.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) + : process_queue_(std::move(process_queue)) { + receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); + receive_message_later_functional_ = std::make_shared(&receive_message_later_); +} + +void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + if (!process_queue_shared_ptr) { + SPDLOG_WARN("Process queue has been released. Drop PopResult: {}", result.toString()); + return; + } + + int64_t term_id = 0; + if (!result.termId(term_id)) { + SPDLOG_WARN("Failed to decode Term-Id from Request-Id: {}", result.requestId()); + } + + std::shared_ptr impl = process_queue_shared_ptr->getCallbackOwner().lock(); + if (impl->isStopped()) { + return; + } + + switch (result.status()) { + case ReceiveMessageStatus::OK: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", + result.sourceHost(), result.getMsgFoundList().size(), process_queue_shared_ptr->simpleName()); + process_queue_shared_ptr->cacheMessages(result.getMsgFoundList()); + impl->getConsumeMessageService()->dispatch(); + + if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::PULL) { + process_queue_shared_ptr->nextOffset(result.next_offset_); + } + + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + checkThrottleThenReceive(); + } + break; + case ReceiveMessageStatus::DATA_CORRUPTED: + if (process_queue_shared_ptr->consumeType() == POP) { + process_queue_shared_ptr->cacheMessages(result.messages_); + impl->getConsumeMessageService()->dispatch(); + } + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + checkThrottleThenReceive(); + } + break; + + case ReceiveMessageStatus::OUT_OF_RANGE: + assert(ConsumeMessageType::PULL == process_queue_shared_ptr->consumeType()); + process_queue_shared_ptr->nextOffset(result.next_offset_); + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + checkThrottleThenReceive(); + } + break; + case ReceiveMessageStatus::DEADLINE_EXCEEDED: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=DEADLINE_EXCEEDED, queue={}", + result.sourceHost(), process_queue_shared_ptr->simpleName()); + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + checkThrottleThenReceive(); + } + break; + case ReceiveMessageStatus::INTERNAL: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=UNKNOWN, queue={}", result.sourceHost(), + process_queue_shared_ptr->simpleName()); + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + receiveMessageLater(); + } + break; + case ReceiveMessageStatus::RESOURCE_EXHAUSTED: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=RESOURCE_EXHAUSTED, queue={}", + result.sourceHost(), process_queue_shared_ptr->simpleName()); + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + receiveMessageLater(); + } + break; + case ReceiveMessageStatus::NOT_FOUND: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=NOT_FOUND, queue={}", result.sourceHost(), + process_queue_shared_ptr->simpleName()); + if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { + receiveMessageLater(); + } + break; + default: + SPDLOG_WARN("Unknown receive message status: {} from broker[host={}], queue={}", result.status(), + result.sourceHost(), process_queue_shared_ptr->simpleName()); + receiveMessageLater(); + break; + } +} + +void AsyncReceiveMessageCallback::checkThrottleThenReceive() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + SPDLOG_WARN("Process queue should have been destructed"); + return; + } + + if (process_queue->shouldThrottle()) { + SPDLOG_INFO("Number of messages in {} exceeds throttle threshold. Receive messages later.", + process_queue->simpleName()); + process_queue->updateThrottleTimestamp(); + receiveMessageLater(); + } else { + // Receive message immediately + receiveMessageImmediately(); + } +} + +void AsyncReceiveMessageCallback::onException(MQException& e) { + auto process_queue_ptr = process_queue_.lock(); + if (process_queue_ptr) { + SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", e.what(), process_queue_ptr->simpleName()); + // pop message later + receiveMessageLater(); + } +} + +void AsyncReceiveMessageCallback::receiveMessageLater() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + return; + } + + auto client_instance = process_queue->getClientInstance(); + bool scheduled = client_instance->getScheduler().schedule(receive_message_later_functional_, std::chrono::seconds(1), + std::chrono::seconds(0)); + if (scheduled) { + SPDLOG_INFO("Receive message later scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule receive message later"); + } +} + +void AsyncReceiveMessageCallback::receiveMessageImmediately() { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + if (!process_queue_shared_ptr) { + SPDLOG_INFO("ProcessQueue has been released. Ignore further receive message request-response cycles"); + return; + } + + std::shared_ptr impl = process_queue_shared_ptr->getCallbackOwner().lock(); + if (!impl) { + SPDLOG_INFO("Owner of ProcessQueue[{}] has been released. Ignore further receive message request-response cycles", + process_queue_shared_ptr->simpleName()); + return; + } + impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression(), + process_queue_shared_ptr->consumeType()); +} + +const int32_t DefaultMQPushConsumerImpl::MAX_CACHED_MESSAGE_COUNT = 65535; +const int32_t DefaultMQPushConsumerImpl::DEFAULT_CACHED_MESSAGE_COUNT = 1024; +const int32_t DefaultMQPushConsumerImpl::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE = 1; +const int32_t DefaultMQPushConsumerImpl::DEFAULT_CONSUME_THREAD_POOL_SIZE = 20; + +DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(std::string group_name) + : BaseImpl(std::move(group_name)), consume_thread_pool_size_(DEFAULT_CONSUME_THREAD_POOL_SIZE), + message_listener_ptr_(nullptr), consume_batch_size_(DEFAULT_CONSUME_MESSAGE_BATCH_SIZE), + max_cached_message_number_per_queue_(DEFAULT_CACHED_MESSAGE_COUNT) { + scan_assignment_executor_ = std::bind(&DefaultMQPushConsumerImpl::scanAssignments, this); + scan_assignment_functional_ = std::make_shared(&scan_assignment_executor_); +} + +DefaultMQPushConsumerImpl::~DefaultMQPushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } + +void DefaultMQPushConsumerImpl::start() { + BaseImpl::start(); + + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_WARN("Unexpected consumer state: {}", state_.load(std::memory_order_relaxed)); + return; + } + + client_instance_->addClientObserver(shared_from_this()); + + fetchRoutes(); + heartbeat(); + + if (message_listener_ptr_) { + if (message_listener_ptr_->getMessageListenerType() == messageListenerOrderly) { + SPDLOG_INFO("start orderly consume service: {}", group_name_); + consume_message_service_ = std::make_shared( + shared_from_this(), consume_thread_pool_size_, message_listener_ptr_); + } else { + // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. + SPDLOG_INFO("start concurrently consume service: {}", group_name_); + consume_message_service_ = std::make_shared( + shared_from_this(), consume_thread_pool_size_, message_listener_ptr_); + } + consume_message_service_->start(); + + { + // Set consumer throttling + absl::MutexLock lock(&throttle_table_mtx_); + for (const auto& item : throttle_table_) { + consume_message_service_->throttle(item.first, item.second); + } + } + } + + bool scheduled; + scheduled = client_instance_->getScheduler().schedule(scan_assignment_functional_, std::chrono::seconds(0), + std::chrono::seconds(5)); + if (scheduled) { + SPDLOG_INFO("pop message executor scheduled"); + } else { + SPDLOG_ERROR("Failed to schedule pop message executor"); + } + state_.store(State::STARTED, std::memory_order_relaxed); + SPDLOG_INFO("DefaultMQPushConsumer started, groupName={}", group_name_); +} + +void DefaultMQPushConsumerImpl::shutdown() { + state_.store(State::STOPPING, std::memory_order_relaxed); + bool cancelled; + cancelled = client_instance_->getScheduler().cancel(&scan_assignment_executor_); + if (cancelled) { + SPDLOG_INFO("pop message executor cancelled OK"); + } else { + SPDLOG_ERROR("Failed to cancel pop message executor"); + } + + { + absl::MutexLock lock(&process_queue_table_mtx_); + process_queue_table_.clear(); + } + + if (consume_message_service_) { + consume_message_service_->shutdown(); + } + + // Destruct client instance as soon as possible. + client_instance_.reset(); +} + +bool DefaultMQPushConsumerImpl::isStopped() const { + State current_state = state_.load(std::memory_order_relaxed); + return State::STOPPED == current_state || State::STOPPING == current_state; +} + +void DefaultMQPushConsumerImpl::subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type) { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + FilterExpression filter_expression{expression, expression_type}; + topic_filter_expression_table_.emplace(topic, filter_expression); +} + +void DefaultMQPushConsumerImpl::unsubscribe(const std::string& topic) { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + topic_filter_expression_table_.erase(topic); +} + +absl::flat_hash_map DefaultMQPushConsumerImpl::getTopicFilterExpressionTable() { + absl::flat_hash_map topic_filter_expression_table; + { + absl::MutexLock lock(&topic_filter_expression_table_mtx_); + for (auto item : topic_filter_expression_table_) { + topic_filter_expression_table.emplace(item.first, item.second); + } + } + + return topic_filter_expression_table; +} + +void DefaultMQPushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { + consume_from_where_ = consume_from_where; +} + +void DefaultMQPushConsumerImpl::scanAssignments() { + SPDLOG_DEBUG("Start of assignment scanning"); + if (isStopped()) { + SPDLOG_INFO("Client has stopped. Abort scanning immediately."); + return; + } + absl::flat_hash_map topic_filter_expression_table = getTopicFilterExpressionTable(); + { + for (auto& filter_entry : topic_filter_expression_table) { + std::string topic = filter_entry.first; + FilterExpression filter_expression = filter_entry.second; + SPDLOG_DEBUG("Scan assignments for {}", topic); + auto callback = [this, topic, filter_expression](const TopicAssignmentPtr& assignments) { + if (assignments && !assignments->assignmentList().empty()) { + syncProcessQueue(topic, assignments, filter_expression); + } else { + SPDLOG_WARN("Failed to acquire assignments for topic={} from load balancer for the first time", topic); + } + }; + queryAssignment(topic, callback); + } // end of for-loop + } + SPDLOG_DEBUG("End of assignment scanning."); +} + +bool DefaultMQPushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_route_data, std::string& broker_host) { + if (topic_route_data && !topic_route_data->partitions().empty()) { + uint32_t index = TopicAssignment::getAndIncreaseQueryWhichBroker(); + for (uint32_t i = index; i < index + topic_route_data->partitions().size(); i++) { + auto partition = topic_route_data->partitions().at(i % topic_route_data->partitions().size()); + if (MixAll::MASTER_BROKER_ID != partition.broker().id() || Permission::NONE == partition.permission()) { + continue; + } + + if (partition.broker()) { + broker_host = partition.broker().serviceAddress(); + return true; + } + } + } + return false; +} + +void DefaultMQPushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, + const std::string& client_id, + const std::string& strategy_name, + QueryAssignmentRequest& request) { + request.mutable_topic()->set_name(topic); + request.mutable_topic()->set_arn(arn()); + request.mutable_group()->set_name(consumer_group); + request.mutable_group()->set_arn(arn()); + request.set_client_id(client_id); +} + +void DefaultMQPushConsumerImpl::queryAssignment(const std::string& topic, + const std::function& cb) { + + auto callback = [this, topic, cb](const TopicRouteDataPtr& topic_route) { + TopicAssignmentPtr topic_assignment; + if (MessageModel::BROADCASTING == message_model_) { + if (!topic_route) { + SPDLOG_WARN("Failed to get valid route entries for topic={}", topic); + cb(topic_assignment); + } + + std::vector assignments; + assignments.reserve(topic_route->partitions().size()); + for (const auto& partition : topic_route->partitions()) { + assignments.emplace_back(Assignment(partition.asMessageQueue(), ConsumeMessageType::PULL)); + } + topic_assignment = std::make_shared(std::move(assignments)); + cb(topic_assignment); + return; + } + + std::string broker_host; + if (!selectBroker(topic_route, broker_host)) { + SPDLOG_WARN("Failed to select a broker to query assignment for group={}, topic={}", group_name_, topic); + } + + QueryAssignmentRequest request; + wrapQueryAssignmentRequest(topic, group_name_, clientId(), MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_, request); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + auto assignment_callback = [this, cb, topic, broker_host](bool ok, const QueryAssignmentResponse& response) { + if (ok) { + SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, group_name_, + response.assignments().size()); + SPDLOG_TRACE("Query assignment response for {} is: {}", topic, ptr->debugString()); + cb(std::make_shared(response)); + } else { + SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); + cb(nullptr); + } + }; + + client_instance_->queryAssignment(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + assignment_callback); + }; + getRouteFor(topic, callback); +} + +/** + * + * @param topic Topic to process + * @param assignment Latest assignment from load balancer + * @param filter_expression Filter expression + */ +void DefaultMQPushConsumerImpl::syncProcessQueue(const std::string& topic, + const std::shared_ptr& topic_assignment, + const FilterExpression& filter_expression) { + const std::vector& assignment_list = topic_assignment->assignmentList(); + std::vector message_queue_list; + message_queue_list.reserve(assignment_list.size()); + for (const auto& assignment : assignment_list) { + message_queue_list.push_back(assignment.messageQueue()); + } + + std::vector current; + { + absl::MutexLock lock(&process_queue_table_mtx_); + for (auto it = process_queue_table_.begin(); it != process_queue_table_.end();) { + if (topic != it->first.getTopic()) { + it++; + continue; + } + + if (std::none_of(message_queue_list.cbegin(), message_queue_list.cend(), + [&](const MQMessageQueue& message_queue) { return it->first == message_queue; })) { + SPDLOG_INFO("Stop receiving messages from {} as it is not assigned to current client according to latest " + "assignment result from load balancer", + it->first.simpleName()); + process_queue_table_.erase(it++); + } else { + if (!it->second || it->second->expired()) { + SPDLOG_WARN("ProcessQueue={} is expired. Remove it for now.", it->first.simpleName()); + process_queue_table_.erase(it++); + continue; + } + current.push_back(it->first); + it++; + } + } + } + + for (const auto& message_queue : message_queue_list) { + if (std::none_of(current.cbegin(), current.cend(), + [&](const MQMessageQueue& item) { return item == message_queue; })) { + SPDLOG_INFO("Start to receive message from {} according to latest assignment info from load balancer", + message_queue.simpleName()); + // Clustering message model is implemented through POP while broadcasting is implemented through classic PULL. + ConsumeMessageType consume_type = + MessageModel::CLUSTERING == message_model_ ? ConsumeMessageType::POP : ConsumeMessageType::PULL; + if (!receiveMessage(message_queue, filter_expression, consume_type)) { + if (!isStopped()) { + SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", message_queue.simpleName()); + // TODO: remove it from current assignment such that a second attempt will be made again in the next round. + } + } + } + } +} + +ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression, + ConsumeMessageType consume_type) { + ProcessQueueSharedPtr process_queue; + { + absl::MutexLock lock(&process_queue_table_mtx_); + if (isStopped()) { + SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop creation of ProcessQueue"); + return process_queue; + } + + if (process_queue_table_.contains(message_queue)) { + process_queue = process_queue_table_.at(message_queue); + } else { + SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); + // create ProcessQueue + process_queue = + std::make_shared(message_queue, filter_expression, consume_type, + max_cached_message_number_per_queue_, shared_from_this(), client_instance_); + std::shared_ptr receive_callback = + std::make_shared(process_queue); + process_queue->callback(receive_callback); + process_queue_table_.emplace(std::make_pair(message_queue, process_queue)); + } + } + return process_queue; +} + +bool DefaultMQPushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression, + ConsumeMessageType consume_type) { + if (isStopped()) { + SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop further receive message request"); + return false; + } + + ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression, consume_type); + if (!process_queue_ptr) { + SPDLOG_INFO("consumer has stopped. Stop creating processQueue"); + return false; + } + + const std::string& broker_host = message_queue.serviceAddress(); + if (broker_host.empty()) { + SPDLOG_ERROR("Failed to resolve address for brokerName={}", message_queue.getBrokerName()); + return false; + } + + switch (consume_type) { + case ConsumeMessageType::PULL: { + int64_t offset = -1; + if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { + // Query latest offset from server. + QueryOffsetRequest request; + request.mutable_partition()->mutable_topic()->set_arn(arn_); + request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); + request.mutable_partition()->set_id(message_queue.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); + request.set_policy(rmq::QueryOffsetPolicy::END); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + auto callback = [broker_host, message_queue, process_queue_ptr](bool ok, const QueryOffsetResponse& response) { + if (ok) { + assert(response.offset() >= 0); + process_queue_ptr->nextOffset(response.offset()); + process_queue_ptr->receiveMessage(); + } else { + SPDLOG_WARN("Failed to acquire latest offset for partition[{}] from server[host={}]", + message_queue.simpleName(), broker_host); + } + }; + client_instance_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } + break; + } + case ConsumeMessageType::POP: + process_queue_ptr->receiveMessage(); + break; + } + return true; +} + +std::shared_ptr DefaultMQPushConsumerImpl::getConsumeMessageService() { + return consume_message_service_; +} + +void DefaultMQPushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { + const std::string& target_host = MessageAccessor::targetEndpoint(msg); + SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, + msg.getTopic(), msg.getQueueId(), msg.getMsgId()); + AckMessageRequest request; + wrapAckMessageRequest(msg, request); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_instance_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void DefaultMQPushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { + std::string target_host = MessageAccessor::targetEndpoint(msg); + SPDLOG_DEBUG("Send message nack to broker. brokerAddress={}", target_host); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + rmq::NackMessageRequest request; + + // Group + request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_name(group_name_); + // Topic + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(msg.getTopic()); + request.set_client_id(clientId()); + request.set_receipt_handle(msg.receiptHandle()); + request.set_message_id(msg.getMsgId()); + request.set_reconsume_times(msg.getReconsumeTimes() + 1); + request.set_max_reconsume_times(max_delivery_attempts_); + + client_instance_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); +} + +void DefaultMQPushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { + request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_name(group_name_); + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(msg.getTopic()); + request.set_client_id(clientId()); + request.set_message_id(msg.getMsgId()); + request.set_receipt_handle(msg.receiptHandle()); +} + +int DefaultMQPushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } + +void DefaultMQPushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { + if (thread_pool_size >= 1) { + consume_thread_pool_size_ = thread_pool_size; + } +} + +uint32_t DefaultMQPushConsumerImpl::consumeBatchSize() const { return consume_batch_size_; } + +void DefaultMQPushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { + if (consume_batch_size >= 1) { + consume_batch_size_ = consume_batch_size; + } +} + +void DefaultMQPushConsumerImpl::maxCachedMessageNumberPerQueue(int max_cached_message_number_per_queue) { + if (max_cached_message_number_per_queue > 0 && max_cached_message_number_per_queue < MAX_CACHED_MESSAGE_COUNT) { + max_cached_message_number_per_queue_ = max_cached_message_number_per_queue; + } else { + max_cached_message_number_per_queue_ = DEFAULT_CACHED_MESSAGE_COUNT; + } +} + +void DefaultMQPushConsumerImpl::registerMessageListener(MQMessageListener* message_listener) { + message_listener_ptr_ = message_listener; +} + +int DefaultMQPushConsumerImpl::getProcessQueueTableSize() { + absl::MutexLock lock(&process_queue_table_mtx_); + return process_queue_table_.size(); +} + +void DefaultMQPushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) { + absl::MutexLock lock(&throttle_table_mtx_); + throttle_table_.emplace(topic, threshold); + // If consumer has started, update it dynamically. + if (getConsumeMessageService()) { + getConsumeMessageService()->throttle(topic, threshold); + } +} + +#ifdef ENABLE_TRACING +nostd::shared_ptr DefaultMQPushConsumerImpl::getTracer() { + if (nullptr == client_instance_) { + return nostd::shared_ptr(nullptr); + } + return client_instance_->getTracer(); +} +#endif + +void DefaultMQPushConsumerImpl::iterateProcessQueue(const std::function& functor) { + absl::MutexLock lock(&process_queue_table_mtx_); + for (const auto& item : process_queue_table_) { + functor(item.second); + } +} + +void DefaultMQPushConsumerImpl::fetchRoutes() { + std::vector topics; + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (const auto& item : topic_filter_expression_table_) { + topics.emplace_back(item.first); + } + } + + if (topics.empty()) { + return; + } + + int countdown = topics.size(); + absl::Mutex mtx; + absl::CondVar cv; + int acquired = 0; + auto callback = [&](const TopicRouteDataPtr& route) { + absl::MutexLock lk(&mtx); + countdown--; + cv.SignalAll(); + if (route) { + acquired++; + } + }; + + for (const auto& topic : topics) { + getRouteFor(topic, callback); + } + + while (countdown) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + SPDLOG_INFO("Fetched route for {} out of {} topics", acquired, topics.size()); +} + +void DefaultMQPushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + auto heartbeat = new rmq::HeartbeatEntry(); + heartbeat->set_client_id(clientId()); + auto consumer_group = heartbeat->mutable_consumer_group(); + consumer_group->mutable_group()->set_arn(arn()); + consumer_group->mutable_group()->set_name(group_name_); + consumer_group->set_consume_model(rmq::ConsumeModel::CLUSTERING); + { + absl::flat_hash_map topic_filter_expression_table = getTopicFilterExpressionTable(); + for (auto& filter_entry : topic_filter_expression_table) { + auto subscription = new rmq::SubscriptionEntry; + subscription->mutable_topic()->set_arn(arn_); + subscription->mutable_topic()->set_name(filter_entry.first); + subscription->mutable_expression()->set_expression(filter_entry.second.content_); + switch (filter_entry.second.type_) { + case ExpressionType::TAG: + subscription->mutable_expression()->set_type(rmq::FilterType::TAG); + break; + case ExpressionType::SQL92: + subscription->mutable_expression()->set_type(rmq::FilterType::SQL); + break; + } + consumer_group->mutable_subscriptions()->AddAllocated(subscription); + } + + // Add heartbeat + request.mutable_heartbeats()->AddAllocated(heartbeat); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/MessageClientIDSetter.cpp b/src/main/cpp/rocketmq/MessageClientIDSetter.cpp new file mode 100644 index 000000000..1455e8ff9 --- /dev/null +++ b/src/main/cpp/rocketmq/MessageClientIDSetter.cpp @@ -0,0 +1,59 @@ +#include "MessageClientIDSetter.h" +#include "spdlog/spdlog.h" + +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +absl::Mutex MessageClientIDSetter::mtx_; +char MessageClientIDSetter::data[16] = {0}; +int16_t MessageClientIDSetter::counter = 0; +int16_t MessageClientIDSetter::basePos = 0; +int32_t MessageClientIDSetter::hash_code = 0; +std::chrono::system_clock::time_point MessageClientIDSetter::m_startTime = std::chrono::system_clock::now(); +std::chrono::system_clock::time_point MessageClientIDSetter::m_nextStartTime = std::chrono::system_clock::now(); + +std::string MessageClientIDSetter::createUniqId() { + absl::MutexLock lock(&mtx_); + char tmp_buf[4]; + static bool initialized = false; + if (!initialized) { // only first execute + std::string&& ip = UtilAll::getHostIPv4(); + struct in_addr addr = {}; + if (inet_aton(ip.c_str(), &addr)) { + memcpy(data, &(addr.s_addr), sizeof(addr.s_addr)); + } else { + SPDLOG_ERROR("inet_aton failed. IP: {}", ip); + } + + // TO-DO: WIN32 + UtilAll::int16tobyte(tmp_buf, static_cast(getpid())); + memcpy(data + 4, tmp_buf, 2); + + UtilAll::int32tobyte(tmp_buf, static_cast(hash_code++)); + memcpy(data + 6, tmp_buf, 4); + + setStartTime(std::chrono::system_clock::now()); + initialized = true; + } + + UtilAll::int32tobyte(tmp_buf, static_cast(hash_code++)); + memcpy(data + 6, tmp_buf, 4); + std::chrono::system_clock::time_point current = std::chrono::system_clock::now(); + if (current.time_since_epoch() >= m_nextStartTime.time_since_epoch()) { + setStartTime(current); + } + int64_t time_duration = + std::chrono::duration_cast(std::chrono::system_clock::now() - m_startTime).count(); + UtilAll::int32tobyte(tmp_buf, static_cast(time_duration)); + memcpy(data + 10, tmp_buf, 4); + + UtilAll::int16tobyte(tmp_buf, counter++); + memcpy(data + 14, tmp_buf, 2); + + return bytes2string(data, 16); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueue.cpp b/src/main/cpp/rocketmq/ProcessQueue.cpp new file mode 100644 index 000000000..7fe43b490 --- /dev/null +++ b/src/main/cpp/rocketmq/ProcessQueue.cpp @@ -0,0 +1,236 @@ +#include "ProcessQueue.h" +#include "ClientInstance.h" +#include "DefaultMQPushConsumerImpl.h" +#include "Metadata.h" +#include "Protocol.h" +#include "Signature.h" +#include +#include +#include + +using namespace std::chrono; + +ROCKETMQ_NAMESPACE_BEGIN + +ProcessQueue::ProcessQueue(MQMessageQueue message_queue, FilterExpression filter_expression, + ConsumeMessageType consume_type, int max_cache_size, + std::weak_ptr call_back_owner, + std::shared_ptr client_instance) + : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), + consume_type_(consume_type), max_message_number_(MixAll::MAX_MESSAGE_NUMBER_PER_BATCH), + invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), message_cached_number_(0), + max_cache_size_(max_cache_size), simple_name_(message_queue_.simpleName()), term_id_(1L), + call_back_owner_(std::move(call_back_owner)), client_instance_(std::move(client_instance)) { + SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); +} + +ProcessQueue::~ProcessQueue() { + SPDLOG_INFO("ProcessQueue={} should have been re-balanced away, thus, is destructed", simpleName()); +} + +void ProcessQueue::callback(std::shared_ptr callback) { callback_ = std::move(callback); } + +bool ProcessQueue::expired() const { + auto duration = std::chrono::steady_clock::now() - last_poll_timestamp_; + auto throttle_duration = std::chrono::steady_clock::now() - last_throttle_timestamp_; + if (duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ && + throttle_duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_) { + SPDLOG_WARN("ProcessQueue={} is expired. Duration from last poll is: {}ms; from last throttle is: {}ms", + simpleName(), MixAll::millisecondsOf(duration), MixAll::millisecondsOf(throttle_duration)); + return true; + } + return false; +} + +bool ProcessQueue::shouldThrottle() const { + int current = message_cached_number_.load(std::memory_order_relaxed); + bool need_throttle = current >= max_cache_size_; + if (need_throttle) { + SPDLOG_INFO("{}: Number of locally cached messages is {}, which exceeds threshold={}", simple_name_, current, + max_cache_size_); + } + return need_throttle; +} + +void ProcessQueue::receiveMessage() { + switch (consume_type_) { + case ConsumeMessageType::POP: + popMessage(); + break; + case ConsumeMessageType::PULL: + pullMessage(); + break; + } +} + +void ProcessQueue::popMessage() { + rmq::ReceiveMessageRequest request; + + absl::flat_hash_map metadata; + auto consumer_client = call_back_owner_.lock(); + if (!consumer_client) { + return; + } + Signature::sign(consumer_client.get(), metadata); + + wrapPopMessageRequest(metadata, request); + last_poll_timestamp_ = std::chrono::steady_clock::now(); + SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); + client_instance_->receiveMessage(message_queue_.serviceAddress(), metadata, request, + absl::ToChronoMilliseconds(consumer_client->getIoTimeout()), callback_); +} + +void ProcessQueue::pullMessage() { + rmq::PullMessageRequest request; + absl::flat_hash_map metadata; + wrapPullMessageRequest(metadata, request); + client_instance_->pullMessage(message_queue_.serviceAddress(), metadata, request, callback_); +} + +void ProcessQueue::cacheMessages(const std::vector& messages) { + absl::MutexLock lock(&cached_messages_mtx_); + cached_messages_.reserve(cached_messages_.size() + messages.size()); + cached_messages_.insert(cached_messages_.end(), messages.begin(), messages.end()); + message_cached_number_.fetch_add(messages.size(), std::memory_order_relaxed); + SPDLOG_DEBUG("{}: Number of locally cached messages is: {}", message_queue_.simpleName(), + message_cached_number_.load(std::memory_order_relaxed)); +} + +bool ProcessQueue::consume(int batch_size, std::vector& messages) { + absl::MutexLock lock(&cached_messages_mtx_); + if (cached_messages_.empty()) { + return false; + } + + for (auto it = cached_messages_.begin(); it != cached_messages_.end();) { + if (--batch_size < 0) { + break; + } + + messages.push_back(*it); + it = cached_messages_.erase(it); + } + return !cached_messages_.empty(); +} + +int64_t ProcessQueue::termId() const { return term_id_.load(std::memory_order_relaxed); } + +bool ProcessQueue::leaseNextTerm(int64_t current_term_id, absl::string_view source_host) { + if (current_term_id) { + if (current_term_id < termId()) { + return false; + } + int64_t term_id = current_term_id; + bool acquired = term_id_.compare_exchange_strong(term_id, current_term_id + 1, std::memory_order_relaxed); + if (acquired) { + SPDLOG_DEBUG("{}: Lease acquired. New termId={}", message_queue_.simpleName(), termId()); + } + return acquired; + } + SPDLOG_WARN("Version of server[host={}] is too old.", source_host.data()); + return true; +} + +std::string ProcessQueue::requestId() const { + int32_t numeric[4]; + std::string request_id; + const std::string& ip = UtilAll::getHostIPv4(); + struct in_addr addr = {}; + int ret = inet_aton(ip.data(), &addr); + + // ip is supposed to be legal and parsable. + assert(ret == 1); + numeric[0] = htonl(addr.s_addr); + numeric[1] = getpid(); + int64_t term_id = term_id_.load(std::memory_order_relaxed); + *(reinterpret_cast(numeric) + 1) = term_id; + return MixAll::hex(numeric, sizeof(numeric)); +} + +void ProcessQueue::wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request) { + metadata.insert({Metadata::REQUEST_ID_KEY, requestId()}); + + std::shared_ptr consumer = call_back_owner_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_arn(consumer->arn()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + + auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); + if (consumer->getTopicFilterExpressionTable().end() != search) { + FilterExpression expression = search->second; + switch (expression.type_) { + case TAG: + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + case SQL92: + request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + } + } else { + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression("*"); + } + + // Batch size + request.set_batch_size(max_message_number_); + + // Consume policy + request.set_consume_policy(rmq::ConsumePolicy::RESUME); + + // Set invisible time + request.mutable_invisible_duration()->set_seconds( + std::chrono::duration_cast(invisible_time_).count()); + auto fraction = invisible_time_ - std::chrono::duration_cast(invisible_time_); + int32_t nano_seconds = static_cast(std::chrono::duration_cast(fraction).count()); + request.mutable_invisible_duration()->set_nanos(nano_seconds); +} + +void ProcessQueue::wrapPullMessageRequest(absl::flat_hash_map& metadata, + rmq::PullMessageRequest& request) { + metadata.insert({Metadata::REQUEST_ID_KEY, requestId()}); + std::shared_ptr consumer = call_back_owner_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_arn(consumer->arn()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + request.set_offset(next_offset_); + request.set_batch_size(consumer->receiveBatchSize()); + + auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); + if (consumer->getTopicFilterExpressionTable().end() != search) { + FilterExpression expression = search->second; + switch (expression.type_) { + case TAG: + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + case SQL92: + request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + } + } else { + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression("*"); + } +} + +std::weak_ptr ProcessQueue::getCallbackOwner() { return call_back_owner_; } + +std::shared_ptr ProcessQueue::getClientInstance() { return client_instance_; } + +MQMessageQueue ProcessQueue::getMQMessageQueue() { return message_queue_; } + +const FilterExpression& ProcessQueue::getFilterExpression() const { return filter_expression_; } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp new file mode 100644 index 000000000..837e6b2a8 --- /dev/null +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -0,0 +1,78 @@ +#include "SendCallbacks.h" + +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void OnewaySendCallback::onException(const MQException& e) { + SPDLOG_WARN("Failed to one-way send message. Message: {}", e.what()); +} + +void OnewaySendCallback::onSuccess(const SendResult& send_result) { + SPDLOG_DEBUG("Send message in one-way OK. MessageId: {}", send_result.getMsgId()); +} + +OnewaySendCallback* onewaySendCallback() { + static OnewaySendCallback callback; + return &callback; +} + +void AwaitSendCallback::await() { + absl::MutexLock lk(&mtx_); + while (!completed_) { + cv_.Wait(&mtx_); + } +} + +void AwaitSendCallback::onSuccess(const SendResult& send_result) { + send_result_ = send_result; + success_ = true; + completed_ = true; + absl::MutexLock lk(&mtx_); + cv_.SignalAll(); +} + +void AwaitSendCallback::onException(const MQException& e) { + success_ = false; + error_message_ = e.what(); + completed_ = true; + absl::MutexLock lk(&mtx_); + cv_.SignalAll(); +} + +void RetrySendCallback::onSuccess(const SendResult& send_result) { + callback_->onSuccess(send_result); + SPDLOG_DEBUG(""); + delete this; +} + +void RetrySendCallback::onException(const MQException& e) { + if (++retry_ >= max_retry_) { + SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", retry_, max_retry_); + callback_->onException(e); + delete this; + return; + } + + std::shared_ptr client = client_instance_.lock(); + if (!client) { + SPDLOG_WARN("Client instance has destructed"); + callback_->onException(e); + delete this; + return; + } + + if (candidates_.empty()) { + SPDLOG_WARN("No alternative hosts to perform additional retries"); + callback_->onException(e); + delete this; + return; + } + + const std::string& host = candidates_[retry_ % candidates_.size()].serviceAddress(); + SPDLOG_DEBUG("Retry-send message to {} for {} times", host, retry_); + client->send(host, metadata_, request_, this); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp new file mode 100644 index 000000000..6e7080dbf --- /dev/null +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -0,0 +1,23 @@ +#include "TransactionImpl.h" +#include "DefaultMQProducerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +bool TransactionImpl::commit() { + std::shared_ptr producer = producer_.lock(); + if (!producer) { + return false; + } + + return producer->commit(message_id_, transaction_id_, endpoint_); +} + +bool TransactionImpl::rollback() { + std::shared_ptr producer = producer_.lock(); + if (!producer) { + return false; + } + return producer->rollback(message_id_, transaction_id_, endpoint_); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h new file mode 100644 index 000000000..d4a68bc5f --- /dev/null +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -0,0 +1,25 @@ +#include "absl/synchronization/mutex.h" +#include "rocketmq/AsyncCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AwaitPullCallback : public PullCallback { +public: + explicit AwaitPullCallback(PullResult& pull_result) + : pull_result_(pull_result), completed_(false), has_failure_(false) {} + + void onSuccess(const PullResult& pull_result) override; + + void onException(const MQException& e) override; + + bool await(); + +private: + PullResult& pull_result_; + absl::Mutex mtx_; + absl::CondVar cv_; + bool completed_; + bool has_failure_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/BaseImpl.h b/src/main/cpp/rocketmq/include/BaseImpl.h new file mode 100644 index 000000000..0a2cdafd3 --- /dev/null +++ b/src/main/cpp/rocketmq/include/BaseImpl.h @@ -0,0 +1,91 @@ +#include "ClientInstance.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class BaseImpl : public ClientConfig, public ClientCallback { +public: + explicit BaseImpl(std::string group_name); + + ~BaseImpl() override = default; + + virtual void start(); + + void getRouteFor(const std::string& topic, const std::function& cb) + LOCKS_EXCLUDED(inflight_route_requests_mtx_, topic_route_table_mtx_); + + void activeHosts(absl::flat_hash_set& hosts) override LOCKS_EXCLUDED(topic_route_table_mtx_); + + void setNameServerList(std::vector name_server_list) { + absl::MutexLock lk(&name_server_list_mtx_); + name_server_list_ = std::move(name_server_list); + } + + virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; + + void heartbeat() override; + +protected: + ClientInstancePtr client_instance_; + std::atomic state_; + + absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); + absl::Mutex topic_route_table_mtx_ ACQUIRED_AFTER(inflight_route_requests_mtx_); // protects topic_route_table_ + + absl::flat_hash_map>> + inflight_route_requests_ GUARDED_BY(inflight_route_requests_mtx_); + absl::Mutex inflight_route_requests_mtx_ ACQUIRED_BEFORE(topic_route_table_mtx_); // Protects inflight_route_requests_ + + std::function topic_route_info_updater_; + FunctionalSharePtr topic_route_info_updater_function_; + + // Name server list management + std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); + absl::Mutex name_server_list_mtx_; // protects name_server_list_ + + std::function name_server_list_updater_; + FunctionalSharePtr name_server_list_updater_function_; + + std::string unit_name_; + TopAddressingPtr top_addressing_; + + std::vector>> name_server_promise_list_; + absl::Mutex name_server_promise_list_mtx_ ACQUIRED_AFTER(name_server_list_mtx_); + + void debugNameServerChanges(const std::vector& list) LOCKS_EXCLUDED(name_server_list_mtx_); + + void renewNameServerList() LOCKS_EXCLUDED(name_server_list_mtx_); + + bool selectNameServer(std::string& selected, bool change = false) LOCKS_EXCLUDED(name_server_list_mtx_); + + void updateRouteInfo() LOCKS_EXCLUDED(topic_route_table_mtx_); + +private: + /** + * This is a low-level API that fetches route data from name server through gRPC unary request/response. Once + * request/response is completed, either timeout or response arrival in time, callback would get invoked. + * @param topic + * @param cb + */ + void fetchRouteFor(const std::string& topic, const std::function& cb); + + /** + * Callback to execute once route data is fetched from name server. + * @param topic + * @param route + */ + void onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) + LOCKS_EXCLUDED(inflight_route_requests_mtx_); + + /** + * Update local cache for the topic. Note, route differences are logged in INFO level since route bears fundamental + * importance. + * + * @param topic + * @param route + */ + void updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) + LOCKS_EXCLUDED(topic_route_table_mtx_); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h new file mode 100644 index 000000000..846d5ffd1 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include + +#include "ProcessQueue.h" +#include "RateLimiter.h" +#include "ThreadPool.h" +#include "rocketmq/MQMessageListener.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPushConsumerImpl; + +class ConsumeMessageService { +public: + ConsumeMessageService() = default; + virtual ~ConsumeMessageService() = default; + + /** + * Make it noncopyable. + */ + ConsumeMessageService(const ConsumeMessageService& other) = delete; + ConsumeMessageService& operator=(const ConsumeMessageService& other) = delete; + + virtual void start() = 0; + + virtual void shutdown(); + + /** + * + * @param process_queue_ptr + * @param permits submit at most permits messages to consume threads; if permits is negative, all messages in process + * queue should be submitted. + */ + virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr, int32_t permits) = 0; + + virtual MessageListenerType getConsumeMsgServiceListenerType() = 0; + virtual void stopThreadPool() = 0; + + virtual void dispatch() = 0; + + void throttle(const std::string& topic, uint32_t threshold); + + bool hasConsumeRateLimiter(const std::string& topic) const; + + std::shared_ptr> rateLimiter(const std::string& topic) const; + +protected: + RateLimiterObserver rate_limiter_observer_; + + mutable absl::flat_hash_map>> + rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); + mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ +}; + +class ConsumeMessageConcurrentlyService : public ConsumeMessageService { +public: + ConsumeMessageConcurrentlyService(std::weak_ptr consumer, int thread_count, + MQMessageListener* message_listener_ptr); + + ~ConsumeMessageConcurrentlyService() override = default; + + void start() override; + void shutdown() override; + + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) override; + + MessageListenerType getConsumeMsgServiceListenerType() override; + void stopThreadPool() override; + + void dispatch() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); + + void dispatch0(); + + int thread_count_; + MQMessageListener* message_listener_ptr_; + std::shared_ptr pool_; + std::weak_ptr consumer_weak_ptr_; + + absl::Mutex dispatch_mtx_; + std::thread dispatch_thread_; + absl::CondVar dispatch_cv_; + + std::atomic state_; +}; + +class ConsumeMessageOrderlyService : public ConsumeMessageService { +public: + ConsumeMessageOrderlyService(const std::weak_ptr&& consumer_impl_ptr, int thread_count, + MQMessageListener* message_listener_ptr); + void start() override; + void shutdown() override; + + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) override; + + MessageListenerType getConsumeMsgServiceListenerType() override; + void stopThreadPool() override; + void dispatch() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); + std::weak_ptr consumer_weak_ptr_; + MQMessageListener* message_listener_ptr_; + int thread_count_; + std::shared_ptr pool_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h new file mode 100644 index 000000000..ecb879fac --- /dev/null +++ b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include +#include + +#include "BaseImpl.h" +#include "ClientConfig.h" +#include "ClientInstance.h" +#include "MixAll.h" +#include "TopicPublishInfo.h" +#include "TransactionImpl.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/SendResult.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class TransactionResolution : int8_t { + COMMIT = 0, + ROLLBACK = 1, +}; + +class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_this { +public: + explicit DefaultMQProducerImpl(std::string group_name); + + ~DefaultMQProducerImpl() override; + + void prepareHeartbeatData(HeartbeatRequest& request) override; + + void start() override; + + void shutdown(); + + SendResult send(const MQMessage& message); + SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); + SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); + + SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int auto_retry_times); + + void send(const MQMessage& message, SendCallback* callback); + void send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* callback); + void send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* callback); + + void sendOneway(const MQMessage& message); + void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); + void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + + std::unique_ptr prepare(const MQMessage& message); + + bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& target); + + bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& target); + + /** + * Check if the RPC client for the target host is active or note. + * @param target Address of target host. + * @return true if client is active; false otherwise. + */ + bool isClientActive(const std::string& target); + + int maxAttemptTimes() const { return max_attempt_times_; } + + void maxAttemptTimes(int times) { max_attempt_times_ = times; } + + int getFailedTimes() const { return failed_times_; } + + void setFailedTimes(int times) { failed_times_ = times; } + + std::vector getTopicMessageQueueInfo(const std::string& topic); + + uint32_t compressBodyThreshold() const { return compress_body_threshold_; } + + void compressBodyThreshold(uint32_t threshold) { compress_body_threshold_ = threshold; } + +private: + absl::flat_hash_map topic_publish_info_table_ GUARDED_BY(topic_publish_info_mtx_); + absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ + + int32_t max_attempt_times_{MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_}; + int32_t failed_times_{0}; // only for test + uint32_t compress_body_threshold_; + + void asyncPublishInfo(const std::string& topic, const std::function& cb) + LOCKS_EXCLUDED(topic_publish_info_mtx_); + + TopicPublishInfoPtr getPublishInfo(const std::string& topic); + + bool executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, void* arg, + std::vector& result); + + void takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, std::vector& message_queues, + int number); + + std::string wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue); + + bool isRunning() const; + + void ensureRunning() const; + + void validate(const MQMessage& message); + + void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); + + bool endTransaction0(const std::string& target, const std::string& message_id, const std::string& transaction_id, + TransactionResolution resolution); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h new file mode 100644 index 000000000..2b6bd536e --- /dev/null +++ b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "BaseImpl.h" +#include "ClientInstance.h" +#include "Identifiable.h" +#include "rocketmq/ConsumerType.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPullConsumerImpl : public BaseImpl, public std::enable_shared_from_this { +public: + explicit DefaultMQPullConsumerImpl(std::string group_name) : BaseImpl(std::move(group_name)) {} + + void start() override; + + void shutdown(); + + std::future> queuesFor(const std::string& topic); + + std::future queryOffset(const OffsetQuery& query); + + void pull(const PullMessageQuery& query, PullCallback* callback); + + void prepareHeartbeatData(HeartbeatRequest& request) override; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h new file mode 100644 index 000000000..bf33cddfd --- /dev/null +++ b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h @@ -0,0 +1,213 @@ +#pragma once + +#include +#include +#include + +#include "BaseImpl.h" +#include "ClientConfig.h" +#include "ClientInstance.h" +#include "ConsumeMessageService.h" +#include "FilterExpression.h" +#include "Functional.h" +#include "ProcessQueue.h" +#include "Scheduler.h" +#include "TopicAssignmentInfo.h" +#include "TopicPublishInfo.h" +#include "UtilAll.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/OffsetStore.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeMessageService; +class ConsumeMessageOrderlyService; +class ConsumeMessageConcurrentlyService; + +class DefaultMQPushConsumerImpl : public BaseImpl, + public std::enable_shared_from_this { +public: + explicit DefaultMQPushConsumerImpl(std::string group_name); + + ~DefaultMQPushConsumerImpl() override; + + void prepareHeartbeatData(HeartbeatRequest& request) override; + + void start() override; + + void shutdown(); + + void subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type = ExpressionType::TAG) + LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + absl::flat_hash_map getTopicFilterExpressionTable() + LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void setConsumeFromWhere(ConsumeFromWhere consume_from_where); + + void registerMessageListener(MQMessageListener* message_listener); + + void scanAssignments() LOCKS_EXCLUDED(process_queue_table_mtx_); + + static bool selectBroker(const TopicRouteDataPtr &route, std::string& broker_host); + + void wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, + const std::string& client_id, const std::string& strategy_name, + QueryAssignmentRequest& request); + + /** + * Query assignment of the specified topic from load balancer directly if message consuming mode is clustering. In + * case current client is operating in the broadcasting mode, assignments are constructed locally from topic route + * entries. + * + * @param topic Topic to query + * @return shared pointer to topic assignment info + */ + void queryAssignment(const std::string& topic, const std::function& cb); + + void syncProcessQueue(const std::string& topic, const TopicAssignmentPtr& topic_assignment, + const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); + + ProcessQueueSharedPtr getOrCreateProcessQueue(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression, + ConsumeMessageType consume_type) + LOCKS_EXCLUDED(process_queue_table_mtx_); + + bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, + ConsumeMessageType consume_type) LOCKS_EXCLUDED(process_queue_table_mtx_); + + int consumeThreadPoolSize() const; + + void consumeThreadPoolSize(int thread_pool_size); + + uint32_t consumeBatchSize() const; + + int32_t receiveBatchSize() const { return receive_message_batch_size_; } + + void consumeBatchSize(uint32_t consume_batch_size); + + void maxCachedMessageNumberPerQueue(int max_cached_message_number_per_queue); + + std::shared_ptr getConsumeMessageService(); + + void ack(const MQMessageExt& msg, const std::function& callback); + + /** + * Negative acknowledge the given message; Refer to https://en.wikipedia.org/wiki/Acknowledgement_(data_networks) for + * background info. + * + * Current implementation is to change invisible time of the given message. + * + * @param message Message to negate on the broker side. + */ + void nack(const MQMessageExt& message, const std::function& callback); + + void wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request); + + bool isStopped() const; + + // only for test + int getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); + + void setCustomExecutor(const Executor& executor) { custom_executor_ = executor; } + + const Executor& customExecutor() const { return custom_executor_; } + + void setThrottle(const std::string& topic, uint32_t threshold); + +#ifdef ENABLE_TRACING + nostd::shared_ptr getTracer(); +#endif + + MessageModel messageModel() const { return message_model_; } + + void setMessageModel(MessageModel message_model) { message_model_ = message_model; } + + void offsetStore(std::unique_ptr offset_store) { offset_store_ = std::move(offset_store); } + +private: + absl::flat_hash_map + topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); + absl::Mutex topic_filter_expression_table_mtx_; + + /** + * Consume message thread pool size. + */ + int consume_thread_pool_size_; + + MQMessageListener* message_listener_ptr_; + + std::shared_ptr consume_message_service_; + + uint32_t consume_batch_size_; + + // TODO: make it configurable + int32_t receive_message_batch_size_{32}; + + int max_cached_message_number_per_queue_; + + std::function scan_assignment_executor_; + FunctionalSharePtr scan_assignment_functional_; + + absl::flat_hash_map process_queue_table_ GUARDED_BY(process_queue_table_mtx_); + absl::Mutex process_queue_table_mtx_; + + ConsumeFromWhere consume_from_where_{ConsumeFromWhere::CONSUME_FROM_LAST_OFFSET}; + Executor custom_executor_; + + absl::flat_hash_map + throttle_table_ GUARDED_BY(throttle_table_mtx_); + absl::Mutex throttle_table_mtx_; + + // TODO: Make default value configurable + int32_t max_delivery_attempts_{16}; + + MessageModel message_model_{MessageModel::CLUSTERING}; + + std::unique_ptr offset_store_; + + void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void iterateProcessQueue(const std::function& functor); + + friend class ConsumeMessageConcurrentlyService; + friend class ConsumeMessageOrderlyService; + + static const int32_t MAX_CACHED_MESSAGE_COUNT; + static const int32_t DEFAULT_CACHED_MESSAGE_COUNT; + static const int32_t DEFAULT_CONSUME_MESSAGE_BATCH_SIZE; + static const int32_t DEFAULT_CONSUME_THREAD_POOL_SIZE; +}; + +class AsyncReceiveMessageCallback : public ReceiveMessageCallback { +public: + explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); + + ~AsyncReceiveMessageCallback() override = default; + + void onSuccess(ReceiveMessageResult& result) override; + + void onException(MQException& e) override; + + void receiveMessageLater(); + + void receiveMessageImmediately(); + +private: + /** + * Hold a weak_ptr to ProcessQueue. Once ProcessQueue was released, stop the pop-cycle immediately. + */ + ProcessQueueWeakPtr process_queue_; + + std::function receive_message_later_; + FunctionalSharePtr receive_message_later_functional_; + + void checkThrottleThenReceive(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h new file mode 100644 index 000000000..b0f4fa4bc --- /dev/null +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -0,0 +1,24 @@ +#pragma once + +#include "rocketmq/ExpressionType.h" +#include "rocketmq/RocketMQ.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * Server supported message filtering expression. At present, two types are supported: tag and SQL92. + */ +struct FilterExpression { + explicit FilterExpression(std::string expression, ExpressionType expression_type = ExpressionType::TAG) + : content_(std::move(expression)), type_(expression_type), version_(std::chrono::steady_clock::now()) { + if (ExpressionType::TAG == type_ && content_.empty()) { + content_ = "*"; + } + } + std::string content_; + ExpressionType type_; + std::chrono::steady_clock::time_point version_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/MessageClientIDSetter.h b/src/main/cpp/rocketmq/include/MessageClientIDSetter.h new file mode 100644 index 000000000..d12380b9d --- /dev/null +++ b/src/main/cpp/rocketmq/include/MessageClientIDSetter.h @@ -0,0 +1,81 @@ +#pragma once +#include "UtilAll.h" +#include "sstream" +#ifndef WIN32 +#include +#include +#endif +#include "LoggerImpl.h" +#include "UtilAll.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageClientIDSetter { +public: + MessageClientIDSetter() = default; + + static std::string createUniqId() LOCKS_EXCLUDED(mtx_); + + static void setStartTime(std::chrono::system_clock::time_point time_p) { + time_t now = std::chrono::system_clock::to_time_t(time_p); + struct tm* p = localtime(&now); + p->tm_sec = 0; + p->tm_min = 0; + p->tm_hour = 0; + p->tm_mday = 1; + m_startTime = std::chrono::system_clock::from_time_t(mktime(p)); + p->tm_mon = p->tm_mon + 1; // next month + // mktime auto adjust time + m_nextStartTime = std::chrono::system_clock::from_time_t(mktime(p)); + } + + static std::string bytes2string(const char* bytebuffer, int arr_len) { + const auto* byte_arr = reinterpret_cast(bytebuffer); + std::ostringstream ret; + for (int i = 0; i < arr_len; ++i) { + char hex1; + char hex2; + int value = byte_arr[i]; + // parse 16 + int v1 = value / 16; + int v2 = value % 16; + if (v1 >= 0 && v1 <= 9) { + hex1 = static_cast(48 + v1); + } else { + hex1 = static_cast(55 + v1); + } + + if (v2 >= 0 && v2 <= 9) { + hex2 = static_cast(48 + v2); + } else { + hex2 = static_cast(55 + v2); + } + + ret << hex1; + ret << hex2; + } + + return ret.str(); + } + +private: + static char data[16]; + static int32_t hash_code; + static int16_t counter; + static int16_t basePos; + static std::chrono::system_clock::time_point m_startTime; + static std::chrono::system_clock::time_point m_nextStartTime; + static absl::Mutex mtx_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h new file mode 100644 index 000000000..b88fe860c --- /dev/null +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include + +#include "ClientInstance.h" +#include "FilterExpression.h" +#include "MixAll.h" +#include "ReceiveMessageCallback.h" +#include "TopicAssignmentInfo.h" +#include "absl/container/flat_hash_map.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/ConsumerType.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPushConsumerImpl; +class ProcessQueue { +public: + ProcessQueue(MQMessageQueue message_queue, FilterExpression filter_expression, ConsumeMessageType consume_type, + int max_cache_size, std::weak_ptr call_back_owner, + std::shared_ptr client_instance); + + ~ProcessQueue(); + + void callback(std::shared_ptr callback); + + MQMessageQueue getMQMessageQueue(); + + bool expired() const; + + bool shouldThrottle() const; + + const FilterExpression& getFilterExpression() const; + + std::weak_ptr getCallbackOwner(); + + std::shared_ptr getClientInstance(); + + void receiveMessage(); + + const std::string& simpleName() const { return simple_name_; } + + std::string topic() const { return message_queue_.getTopic(); } + + /** + * Put message fetched from broker into cache. + * + * @param messages + */ + void cacheMessages(const std::vector& messages) LOCKS_EXCLUDED(cached_messages_mtx_); + + /** + * @return Number of messages that is not yet dispatched to thread pool, likely, due to topic-rate-limiting. + */ + uint32_t cachedMessagesSize() const LOCKS_EXCLUDED(cached_messages_mtx_) { + absl::MutexLock lk(&cached_messages_mtx_); + return cached_messages_.size(); + } + + /** + * Dispatch messages from cache to thread pool in form of consumeTask. + * @param batch_size + * @param messages + * @return true if there are more messages to consume in cache + */ + bool consume(int batch_size, std::vector& messages) LOCKS_EXCLUDED(cached_messages_mtx_); + + int64_t termId() const; + + /** + * Try to lease current process queue for another term with term_id from current pop response, which is forwarded + * by broker and is of previous request. + * + * @param current_term_id Term Id of previous request + * @return true if new lease is granted; false otherwise. + */ + bool leaseNextTerm(int64_t current_term_id, absl::string_view source_host); + + void updateThrottleTimestamp() { last_throttle_timestamp_ = std::chrono::steady_clock::now(); } + + std::atomic_int& messageCachedNumber() { return message_cached_number_; } + + std::string requestId() const; + + ConsumeMessageType consumeType() const { return consume_type_; } + + void nextOffset(int64_t next_offset) { + assert(next_offset >= 0); + next_offset_ = next_offset; + } + + int64_t nextOffset() const { return next_offset_; } + +private: + MQMessageQueue message_queue_; + + /** + * Expression used to filter message in the server side. + */ + const FilterExpression filter_expression_; + + ConsumeMessageType consume_type_{ConsumeMessageType::POP}; + + int max_message_number_; + std::chrono::milliseconds invisible_time_; + + std::chrono::steady_clock::time_point last_poll_timestamp_{std::chrono::steady_clock::now()}; + + std::chrono::steady_clock::time_point last_throttle_timestamp_{std::chrono::steady_clock::now()}; + + absl::Time born_timestamp_{absl::Now()}; + + ConsumeInitialMode initial_mode_{ConsumeInitialMode::MAX}; + + /** + * Number of messages that are not yet acknowledged. + */ + std::atomic_int message_cached_number_; + + /** + * Maximum number of locally cached messages. Once exceeding this threshold, fetch-loop should be throttled. + */ + int max_cache_size_; + + std::string simple_name_; + + /** + * This field indicate term-id of the in-flight pop-request. + */ + std::atomic term_id_; + + // callback + std::weak_ptr call_back_owner_; + std::shared_ptr client_instance_; + + std::shared_ptr callback_; + + mutable std::vector cached_messages_; + mutable absl::Mutex cached_messages_mtx_; + + int64_t next_offset_{-1}; + + void popMessage(); + void wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request); + + void pullMessage(); + void wrapPullMessageRequest(absl::flat_hash_map& metadata, + rmq::PullMessageRequest& request); +}; + +using ProcessQueueSharedPtr = std::shared_ptr; +using ProcessQueueWeakPtr = std::weak_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h new file mode 100644 index 000000000..5905bad94 --- /dev/null +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -0,0 +1,68 @@ +#pragma once + +#include "ClientInstance.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "apache/rocketmq/v1/service.grpc.pb.h" +#include "rocketmq/AsyncCallback.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +using SendMessageRequest = apache::rocketmq::v1::SendMessageRequest; + +class OnewaySendCallback : public SendCallback { +public: + void onSuccess(const SendResult& send_result) override; + + void onException(const MQException& e) override; +}; + +OnewaySendCallback* onewaySendCallback(); + +class AwaitSendCallback : public SendCallback { +public: + void onSuccess(const SendResult& send_result) override; + + void onException(const MQException& e) override; + + void await(); + + explicit operator bool() const { return success_; } + + const SendResult& sendResult() const { return send_result_; } + + const std::string& errorMessage() const { return error_message_; } + +private: + absl::Mutex mtx_; + absl::CondVar cv_; + bool completed_{false}; + bool success_{false}; + SendResult send_result_; + std::string error_message_; +}; + +class RetrySendCallback : public SendCallback { +public: + RetrySendCallback(std::weak_ptr client_instance, + absl::flat_hash_map metadata, SendMessageRequest request, int max_retry, + SendCallback* callback, std::vector candidates) + : client_instance_(std::move(client_instance)), metadata_(std::move(metadata)), request_(std::move(request)), + max_retry_(max_retry), callback_(callback), candidates_(std::move(candidates)) {} + + void onSuccess(const SendResult& send_result) override; + + void onException(const MQException& e) override; + +private: + std::weak_ptr client_instance_; + absl::flat_hash_map metadata_; + SendMessageRequest request_; + int retry_{0}; + int max_retry_; + SendCallback* callback_; + std::vector candidates_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h new file mode 100644 index 000000000..1c1b9219d --- /dev/null +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -0,0 +1,32 @@ +#pragma once + +#include "rocketmq/Transaction.h" + +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQProducerImpl; + +class TransactionImpl : public Transaction { +public: + TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, + const std::shared_ptr& producer) + : message_id_(std::move(message_id)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), + producer_(producer) {} + + ~TransactionImpl() override = default; + + bool commit() override; + + bool rollback() override; + +private: + std::string message_id_; + std::string transaction_id_; + std::string endpoint_; + std::weak_ptr producer_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/BUILD.bazel b/src/main/cpp/scheduler/BUILD.bazel new file mode 100644 index 000000000..1ee04d08e --- /dev/null +++ b/src/main/cpp/scheduler/BUILD.bazel @@ -0,0 +1,21 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "scheduler_library", + hdrs = [ + "include/Scheduler.h", + ], + srcs = [ + "Scheduler.cpp", + ], + strip_include_prefix = "//src/main/cpp/scheduler/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "//external:libuv", + "@com_google_absl//absl/base", + "@com_google_absl//absl/strings", + "@com_github_progschj_ThreadPool//:ThreadPool", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/scheduler/CMakeLists.txt b/src/main/cpp/scheduler/CMakeLists.txt new file mode 100644 index 000000000..d3bf794f6 --- /dev/null +++ b/src/main/cpp/scheduler/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library(scheduler_obj OBJECT) +target_sources(scheduler_obj + PRIVATE + Scheduler.cpp) +target_include_directories(scheduler_obj + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(scheduler_obj + PUBLIC + base_library + countdown_latch_library + thread_pool) + +add_library(scheduler_library + STATIC + $) +target_include_directories(scheduler_library + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(scheduler_library + PUBLIC + base_library + countdown_latch_library + thread_pool) \ No newline at end of file diff --git a/src/main/cpp/scheduler/Scheduler.cpp b/src/main/cpp/scheduler/Scheduler.cpp new file mode 100644 index 000000000..0b7605b2b --- /dev/null +++ b/src/main/cpp/scheduler/Scheduler.cpp @@ -0,0 +1,263 @@ +#include "Scheduler.h" + +#include "absl/memory/memory.h" +#include +#include +#include + +using namespace rocketmq; + +Scheduler::Scheduler() + : loop_(), async_handle_(), running_(false), uv_loop_latch_(1), + thread_pool_(absl::make_unique(1)) { + daemon_callback_ = std::bind(&Scheduler::do_daemon, this); + functor_ = std::make_shared(&daemon_callback_); + spdlog::set_level(spdlog::level::trace); +} + +Scheduler::~Scheduler() { + if (running_.load()) { + SPDLOG_WARN("shutdown() should have been invoked"); + } +} + +void Scheduler::start() { + bool expected = false; + if (running_.compare_exchange_strong(expected, true)) { + // Initialize uv_loop + if (uv_loop_init(&loop_)) { + SPDLOG_ERROR("uv_loop_init failed"); + } + + async_handle_.data = this; + uv_async_init(&loop_, &async_handle_, &Scheduler::uv_async_do_schedule); + + // Initialize a daemon timer task which keeps uv_loop looping. + auto timer_handler = new uv_timer_t(); + auto timer = new Timer(timer_handler, functor_, this); + timer_handler->data = timer; + uv_timer_init(&loop_, timer_handler); + uv_timer_start(timer_handler, &Scheduler::uv_timer_callback, 0, 1000); + + // Event loop lambda + auto loop_lambda = [this] { + spdlog::info("uv_run starts to loop"); + if (uv_run(&loop_, UV_RUN_DEFAULT)) { + spdlog::debug("uv_stop() was called while there are still active handles or requests."); + } else { + spdlog::info("uv_run completes"); + } + }; + + // Run event loop + loop_thread_ = std::thread(loop_lambda); + + // Wait till uv_run actually fires + uv_loop_latch_.await(); + SPDLOG_DEBUG("There are {} active handle", uv_loop_alive(&loop_)); + } +} + +void Scheduler::shutdown() { + // Mark running flag as false + bool expected = true; + if (running_.compare_exchange_strong(expected, false)) { + SPDLOG_TRACE("Scheduler starts to shut down"); + uv_async_send(&async_handle_); + + // Join to wait the thread to quit + SPDLOG_TRACE("Scheduler starts to wait loop_thread"); + if (loop_thread_.joinable()) { + loop_thread_.join(); + } + SPDLOG_TRACE("Scheduler awaits loop_thread OK"); + + // Close uv_loop + if (uv_loop_alive(&loop_)) { + SPDLOG_WARN("There are {} active events", uv_loop_alive(&loop_)); + } else { + SPDLOG_DEBUG("There is no alive uv handles any more"); + } + + int loop_close_status = uv_loop_close(&loop_); + assert(0 == loop_close_status); + if (loop_close_status == UV_EBUSY) { + SPDLOG_ERROR("uv_loop_close failed with UV_EBUSY."); + abort(); + } + SPDLOG_DEBUG("Scheduler has shut down"); + } +} + +bool Scheduler::schedule(const FunctionalWeakPtr& functor, std::chrono::milliseconds delay, + std::chrono::milliseconds interval) { + if (!running_.load()) { + return false; + } + + if (!functor.lock()) { + SPDLOG_DEBUG("Functor pointed by weak_ptr should have been released"); + return false; + } + + { + absl::MutexLock lock(&pending_task_list_mtx_); + pending_task_list_.emplace_back(functor, delay, interval); + } + return 0 == uv_async_send(&async_handle_); +} + +bool Scheduler::cancel(std::function* callable) { + { + absl::MutexLock lk(&pending_task_list_mtx_); + std::weak_ptr ptr; + Task task(ptr, std::chrono::milliseconds(0), std::chrono::milliseconds(0), + TaskType::CANCEL); + task.cancelled_callable_ = callable; + pending_task_list_.push_back(task); + } + uv_async_send(&async_handle_); + return true; +} + +void Scheduler::uv_timer_callback(uv_timer_t* handler) { + if (handler->data) { + auto timer = static_cast(handler->data); + + auto functor_shared_ptr = timer->callback_.lock(); + if (functor_shared_ptr) { + auto req = new uv_work_t; + req->data = timer; + timer->scheduler_->thread_pool_->enqueue(&Scheduler::uv_queue_work_callback, req); + } + } +} + +void Scheduler::uv_close_callback(uv_handle_t* handle) { + if (handle->type == uv_handle_type::UV_TIMER) { + auto attachment = reinterpret_cast(handle->data); + // handle wil be deleted while releasing attachment + delete attachment; + } + SPDLOG_DEBUG("Scheduler#uv_close_callback: a uv_handle was closed OK. ThreadId={}", getThreadId()); +} + +void Scheduler::uv_async_do_schedule(uv_async_t* handle) { + auto scheduler = reinterpret_cast(handle->data); + scheduler->do_schedule(); +} + +void Scheduler::do_schedule() { + if (running_.load(std::memory_order_relaxed)) { + + // Process cancelled job + bool has_job_cancelled = false; + // Handle new scheduled jobs + { + absl::MutexLock lk(&pending_task_list_mtx_); + for (auto it = pending_task_list_.begin(); it != pending_task_list_.end();) { + if (it->type_ == TaskType::TIMER) { + auto timer_handle = new uv_timer_t(); + uv_timer_init(&loop_, timer_handle); + auto delay = it->delay_; + auto duration = it->interval_; + auto timer = new Timer(timer_handle, it->functor_ptr_, this); + // Make handle#data point to the actual functor + timer_handle->data = timer; + if (uv_timer_start(timer_handle, &Scheduler::uv_timer_callback, delay.count(), duration.count())) { + // As it failed to arm a timer, we just delete the resources. + delete timer; + timer_handle->data = nullptr; + SPDLOG_ERROR("uv_timer_start failed"); + } + it = pending_task_list_.erase(it); + } else { + if (it->type_ == TaskType::CANCEL) { + has_job_cancelled = true; + } + it++; + } + } + } + + if (has_job_cancelled) { + uv_walk(&loop_, uv_cancel_walk_cb, this); + } + } else { + uv_walk(&loop_, &Scheduler::uv_stop_walk_cb, nullptr); + } + SPDLOG_DEBUG("do_schedule threadId={}", getThreadId()); +} + +void Scheduler::uv_stop_walk_cb(uv_handle_t* handle, void* arg) { + if (handle->type == uv_handle_type::UV_TIMER) { + auto timer_handle = reinterpret_cast(handle); + SPDLOG_DEBUG("uv_timer_stop a timer handle"); + uv_timer_stop(timer_handle); + } + uv_close(handle, &Scheduler::uv_close_callback); +} + +void Scheduler::uv_cancel_walk_cb(uv_handle_t* handle, void* arg) { + auto scheduler = reinterpret_cast(arg); + if (scheduler->running_.load(std::memory_order_relaxed)) { + scheduler->check_and_cancel(handle); + } +} + +void Scheduler::check_and_cancel(uv_handle_t* handle) { + if (!handle) { + return; + } + + if (handle->type != uv_handle_type::UV_TIMER) { + return; + } + + auto timer = reinterpret_cast(handle->data); + if (!timer) { + return; + } + + auto ptr = timer->callback_.lock(); + if (!ptr) { + return; + } + + auto callable = ptr->getFunction(); + + bool found = false; + { + absl::MutexLock lk(&pending_task_list_mtx_); + for (auto it = pending_task_list_.begin(); it != pending_task_list_.end(); it++) { + if (it->type_ == TaskType::CANCEL && it->cancelled_callable_ == callable) { + pending_task_list_.erase(it); + found = true; + break; + } + } + } + + if (found) { + uv_timer_stop(reinterpret_cast(handle)); + uv_close(handle, &Scheduler::uv_close_callback); + } +} + +void Scheduler::do_daemon() { uv_loop_latch_.countdown(); } + +std::string Scheduler::getThreadId() { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} + +void Scheduler::uv_queue_work_callback(uv_work_t* req) { + auto timer = static_cast(req->data); + FunctionalSharePtr callback = timer->callback_.lock(); + if (callback) { + SPDLOG_TRACE("ThreadID={} executes scheduled task", getThreadId()); + (*(callback->getFunction()))(); + } + delete req; +} \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h new file mode 100644 index 000000000..a60256d17 --- /dev/null +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "CountdownLatch.h" +#include "Functional.h" +#include "LoggerImpl.h" +#include "ThreadPool.h" +#include "uv.h" + +namespace rocketmq { +class Scheduler { + /** + * uv_handle_t attachment structure. + */ + enum TaskType { TIMER, CANCEL }; + + template struct Task { + std::weak_ptr functor_ptr_; + Duration delay_; + Duration interval_; + TaskType type_; + + // pointer to cancelled task + void* cancelled_callable_; + Task(std::weak_ptr functor_ptr, Duration delay, Duration interval, TaskType type = TIMER) + : functor_ptr_(std::move(functor_ptr)), delay_(delay), interval_(interval), type_(type), + cancelled_callable_(nullptr) {} + }; + + struct Attachment { + TaskType type_; + uv_handle_t* uv_handle_; + explicit Attachment(TaskType type, uv_handle_t* uv_handle) : type_(type), uv_handle_(uv_handle) {} + + virtual ~Attachment() { + if (TIMER == type_) { + delete reinterpret_cast(uv_handle_); + } else { + delete reinterpret_cast(uv_handle_); + } + } + }; + + struct Timer : public Attachment { + FunctionalWeakPtr callback_; + Scheduler* scheduler_; + Timer(uv_timer_t* timer_handle, FunctionalWeakPtr callback, Scheduler* scheduler) + : Attachment(TIMER, reinterpret_cast(timer_handle)), callback_(std::move(callback)), + scheduler_(scheduler) {} + + ~Timer() override { + SPDLOG_DEBUG("~Timer invoked"); + } + }; + +public: + Scheduler(); + + ~Scheduler(); + + void start(); + + void shutdown(); + + /** + * @callable Pointer to the functor. Lifecycle of this functor should be maintained by the caller. + * @delay The amount of time to wait before the first shot. + * @interval The interval between each fire-shot. If it is 0ms, callable will just fired once. + * @fixed_delay Execute callable with fixed delay if true; otherwise, scheduler will try its best to execute callable + * with fixed rate + */ + bool schedule(const FunctionalWeakPtr& functor, std::chrono::milliseconds delay, std::chrono::milliseconds interval) + LOCKS_EXCLUDED(pending_task_list_mtx_); + + bool cancel(std::function* callable) LOCKS_EXCLUDED(pending_task_list_mtx_); + +private: + uv_loop_t loop_; + + /** + * Used to notify loop thread that some external events are pending to process + */ + uv_async_t async_handle_; + + std::atomic_bool running_; + + std::thread loop_thread_; + + CountdownLatch uv_loop_latch_; + + std::function daemon_callback_; + FunctionalSharePtr functor_; + + std::vector> pending_task_list_ GUARDED_BY(pending_task_list_mtx_); + absl::Mutex pending_task_list_mtx_; // Protects pending_task_list_. + + std::unique_ptr thread_pool_; + + static void uv_timer_callback(uv_timer_t* handle); + + static void uv_close_callback(uv_handle_t* handle); + + static void uv_async_do_schedule(uv_async_t* handle); + + static void uv_queue_work_callback(uv_work_t* req); + + void do_daemon(); + + /** + * Will execute in uv_loop thread. + */ + void do_schedule(); + + static void uv_stop_walk_cb(uv_handle_t* handle, void* arg); + + static void uv_cancel_walk_cb(uv_handle_t* handle, void* arg); + + void check_and_cancel(uv_handle_t* handle) LOCKS_EXCLUDED(pending_task_list_mtx_); + + static std::string getThreadId(); +}; +} // namespace rocketmq \ No newline at end of file diff --git a/src/test/cpp/benchmark/BUILD.bazel b/src/test/cpp/benchmark/BUILD.bazel new file mode 100644 index 000000000..e8aa27ac3 --- /dev/null +++ b/src/test/cpp/benchmark/BUILD.bazel @@ -0,0 +1,11 @@ +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") + +cc_test( + name = "benchmark_test", + srcs = [ + "BenchmarkTest.cpp", + ], + deps = [ + "//external:benchmark", + ], +) \ No newline at end of file diff --git a/src/test/cpp/benchmark/BenchmarkTest.cpp b/src/test/cpp/benchmark/BenchmarkTest.cpp new file mode 100644 index 000000000..388a5ef39 --- /dev/null +++ b/src/test/cpp/benchmark/BenchmarkTest.cpp @@ -0,0 +1,18 @@ +#include "benchmark/benchmark.h" + +static void BM_StringCreation(benchmark::State& state) { + for (auto _ : state) + std::string empty_string; +} +// Register the function as a benchmark +BENCHMARK(BM_StringCreation); + +// Define another benchmark +static void BM_StringCopy(benchmark::State& state) { + std::string x = "hello"; + for (auto _ : state) + std::string copy(x); +} +BENCHMARK(BM_StringCopy); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel new file mode 100644 index 000000000..8ed6c5cf1 --- /dev/null +++ b/src/test/cpp/it/BUILD.bazel @@ -0,0 +1,40 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "it_top_addressing", + srcs = [ + "TopAddressingTest.cpp", + ], + deps = [ + "//src/main/cpp/base:top_addressing", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "it_rpc_client", + srcs = [ + "RpcClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + copts = [ + "-Isrc/main/client/mocks/include", + ], + linkstatic = True, +) + +cc_test( + name = "it_topic_publish_info", + srcs = [ + "TopicPublishInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/client:rpc_client_mock", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/it/CMakeLists.txt b/src/test/cpp/it/CMakeLists.txt new file mode 100644 index 000000000..51df5ba4b --- /dev/null +++ b/src/test/cpp/it/CMakeLists.txt @@ -0,0 +1,42 @@ +add_executable(client_instance_test ClientInstanceTest.cpp) +target_link_libraries(client_instance_test + PRIVATE client_library + PRIVATE gtest_main) +add_test(it_client_instance client_instance_test) + +add_executable(rpc_client_integration_test RpcClientIntegrationTest.cpp) +target_link_libraries(rpc_client_integration_test + client_library + rpc_client_mock + gtest_main) +add_test(it_rpc_client rpc_client_integration_test) + +add_executable(rpc_client_test RpcClientTest.cpp) +target_link_libraries(rpc_client_test + client_library + CURL::libcurl + credentials_observable_mock + rpc_client_mock + gtest_main) +add_test(it_rpc_client_2 rpc_client_test) + +add_executable(top_addressing_test TopAddressingTest.cpp) +target_link_libraries(top_addressing_test + base_library + gtest_main) +add_test(it_top_addressing top_addressing_test) + + +add_executable(topic_publish_info_test TopicPublishInfoTest.cpp) +target_link_libraries(topic_publish_info_test + PRIVATE + client_library + credentials_observable_mock + gtest_main) +add_test(it_topic_publish_info topic_publish_info_test) + +add_executable(default_push_consumer_test DefaultMQPushConsumerIntegrationTest.cpp) +target_link_libraries(default_push_consumer_test + rocketmq_library + gtest_main) +add_test(it_default_push_consumer default_push_consumer_test) \ No newline at end of file diff --git a/src/test/cpp/it/ClientInstanceTest.cpp b/src/test/cpp/it/ClientInstanceTest.cpp new file mode 100644 index 000000000..1b2e95f12 --- /dev/null +++ b/src/test/cpp/it/ClientInstanceTest.cpp @@ -0,0 +1,12 @@ +#include "ClientInstance.h" +#include "ClientManager.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientInstanceTest : public testing::Test { +protected: + std::string name_server_{"grpc.dev:9876"}; +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp new file mode 100644 index 000000000..9f24d8785 --- /dev/null +++ b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp @@ -0,0 +1,45 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace rocketmq; + +class SampleMQMessageListener : public MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + for (const MQMessageExt& msg : msgs) { + std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Body=" << msg.getBody() + << std::endl; + } + return ConsumeStatus::CONSUME_SUCCESS; + } +}; + +class DefaultMQPushConsumerTest : public testing::Test { +public: + DefaultMQPushConsumerTest() : push_consumer_("CID_sample"), listener_(new SampleMQMessageListener) {} + + void SetUp() override { + push_consumer_.setGroupName("CID_sample"); + push_consumer_.setInstanceName("CID_sample_member_0"); + push_consumer_.subscribe("TopicTest", "*"); + push_consumer_.setNamesrvAddr("localhost:9886"); + push_consumer_.registerMessageListener(listener_); + } + + void TearDown() override { push_consumer_.shutdown(); } + + ~DefaultMQPushConsumerTest() override { delete listener_; } + +protected: + DefaultMQPushConsumer push_consumer_; + MQMessageListener* listener_; +}; + +TEST_F(DefaultMQPushConsumerTest, testStartupAndShutdown) { + push_consumer_.setConsumeThreadCount(1); + push_consumer_.start(); + std::this_thread::sleep_for(std::chrono::minutes(10)); +} diff --git a/src/test/cpp/it/README.md b/src/test/cpp/it/README.md new file mode 100644 index 000000000..d9bac84d3 --- /dev/null +++ b/src/test/cpp/it/README.md @@ -0,0 +1,4 @@ +## Introduction + +Execution of integration test relies on presence of daily environment, which provides dependent services. Dependent +services are discovered through configured DNS records in hosts file. \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientIntegrationTest.cpp b/src/test/cpp/it/RpcClientIntegrationTest.cpp new file mode 100644 index 000000000..29229ab2c --- /dev/null +++ b/src/test/cpp/it/RpcClientIntegrationTest.cpp @@ -0,0 +1,263 @@ +#include +#include +#include + +#include "ClientConfig.h" +#include "MixAll.h" +#include "Protocol.h" +#include "RpcClientImpl.h" +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientIntegrationTest : public testing::Test { +protected: + RpcClientIntegrationTest() + : topic_name("TestTopic"), group_name("CID_sample"), client_id("client_id_01"), tag_name("TagA"), + completion_queue_(std::make_shared()), + rpcClient(completion_queue_, grpc::CreateChannel(target, grpc::InsecureChannelCredentials())) {} + + void SetUp() override { + { + heartbeat_request.mutable_heartbeats()->Clear(); + + auto heartbeat_entry = new rmq::HeartbeatEntry; + heartbeat_entry->set_client_id(client_id); + heartbeat_entry->mutable_consumer_group()->mutable_group()->set_name(group_name); + auto subscription_entry = new rmq::SubscriptionEntry; + subscription_entry->mutable_topic()->set_name(topic_name); + subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); + subscription_entry->mutable_expression()->set_expression(tag_name); + heartbeat_entry->mutable_consumer_group()->mutable_subscriptions()->AddAllocated(subscription_entry); + } + + { + query_assignment_request.mutable_topic()->set_name(topic_name); + query_assignment_request.set_client_id(client_id); + query_assignment_request.mutable_group()->set_name(group_name); + } + + { + receive_message_request.mutable_group()->set_name(group_name); + receive_message_request.set_client_id(client_id); + receive_message_request.mutable_partition()->set_id(0); + receive_message_request.mutable_partition()->mutable_topic()->set_name(topic_name); + receive_message_request.mutable_partition()->mutable_broker()->set_name("broker-a"); + auto endpoint = receive_message_request.mutable_partition()->mutable_broker()->mutable_endpoints(); + endpoint->set_scheme(rmq::AddressScheme::IPv4); + auto address = new rmq::Address; + address->set_host("127.0.0.1"); + address->set_port(10911); + endpoint->mutable_addresses()->AddAllocated(address); + receive_message_request.set_batch_size(32); + receive_message_request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + receive_message_request.mutable_filter_expression()->set_expression(tag_name); + receive_message_request.set_consume_policy(rmq::ConsumePolicy::RESUME); + receive_message_request.mutable_invisible_duration()->set_seconds(30); + receive_message_request.mutable_await_time()->set_seconds(10); + } + } + + std::shared_ptr completion_queue_; + std::string target{"127.0.0.1:10921"}; + std::string topic_name; + std::string group_name; + std::string client_id; + std::string tag_name; + RpcClientImpl rpcClient; + rmq::HeartbeatRequest heartbeat_request; + rmq::QueryAssignmentRequest query_assignment_request; + rmq::ReceiveMessageRequest receive_message_request; +}; + +TEST_F(RpcClientIntegrationTest, testQueryAssignment_without_heartbeat) { + QueryAssignmentResponse response; + auto status = rpcClient.queryAssignment(query_assignment_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(response.common().status().code(), google::rpc::Code::OK); + EXPECT_EQ(8, response.assignments().size()); + for (auto& assignment : response.assignments()) { + auto& partition = assignment.partition(); + std::cout << partition.topic().name() << " --> " << partition.broker().name() << " --> " << partition.id() + << std::endl; + } +} + +TEST_F(RpcClientIntegrationTest, testQueryAssignment_with_prior_heartbeat) { + + { + HeartbeatResponse response; + grpc::ClientContext client_context; + auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + } + + QueryAssignmentResponse response; + + auto status = rpcClient.queryAssignment(query_assignment_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + EXPECT_FALSE(response.assignments().empty()); +} + +TEST_F(RpcClientIntegrationTest, testQueryAssignment_with_mutiple_clients) { + std::string consumer_group("CID_sample"); + std::string client_id_0("CID_sample_member_0"); + std::string client_id_1("CID_sample_member_1"); + std::string topic("TopicTest"); + + { + // Send heartbeat to broker + HeartbeatResponse response; + grpc::ClientContext client_context; + auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + } + + { + HeartbeatResponse response; + grpc::ClientContext client_context; + auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + } + + { + QueryAssignmentResponse response; + + auto status = rpcClient.queryAssignment(query_assignment_request, &response); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + EXPECT_FALSE(response.assignments().empty()); + } +} + +TEST_F(RpcClientIntegrationTest, testPop) { + + auto invocation_context = new InvocationContext; + + auto callback = [](const grpc::Status& status, const grpc::ClientContext& client_context, + const ReceiveMessageResponse& response) { + for (int i = 0; i < response.messages_size(); ++i) { + auto msg = response.messages().at(i); + std::cout << msg.system_attribute().message_id() << std::endl; + const std::string& receipt_handle = msg.system_attribute().receipt_handle(); + EXPECT_FALSE(receipt_handle.empty()); + } + }; + + invocation_context->callback_ = callback; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); + invocation_context->context_.set_deadline(deadline); + + rpcClient.asyncReceive(receive_message_request, invocation_context); + void* tag; + bool ok; + bool stop = false; + while (!stop) { + const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); + grpc::CompletionQueue::NextStatus status = rpcClient.completionQueue()->AsyncNext(&tag, &ok, deadline); + EXPECT_FALSE(grpc::CompletionQueue::SHUTDOWN == status); + switch (status) { + case grpc::CompletionQueue::SHUTDOWN: + stop = true; + break; + case grpc::CompletionQueue::GOT_EVENT: { + stop = true; + auto invocation_ctx = reinterpret_cast*>(tag); + EXPECT_TRUE(nullptr != invocation_ctx); + invocation_ctx->onCompletion(ok); + break; + } + case grpc::CompletionQueue::TIMEOUT: + break; + } + } +} + +TEST_F(RpcClientIntegrationTest, testPopAndAck) { + auto invocation_context = new InvocationContext; + + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, + const ReceiveMessageResponse& response) { + for (int i = 0; i < response.messages_size(); ++i) { + auto msg = response.messages().at(i); + const std::string& message_id = msg.system_attribute().message_id(); + const std::string& receipt_handle = msg.system_attribute().receipt_handle(); + + EXPECT_FALSE(message_id.empty()); + EXPECT_FALSE(receipt_handle.empty()); + + AckMessageRequest ack_request; + ack_request.set_client_id(client_id); + ack_request.mutable_group()->set_name(group_name); + ack_request.mutable_topic()->set_name(topic_name); + ack_request.set_message_id(message_id); + ack_request.set_receipt_handle(receipt_handle); + InvocationContext invocation_context; + + std::mutex ack_mtx; + std::condition_variable ack_cv; + grpc::Status ack_rpc_status = grpc::Status::CANCELLED; + google::rpc::Status ack_status; + std::atomic_bool ack_done(false); + + invocation_context.callback_ = [&](const grpc::Status& status, const grpc::ClientContext& client_context, + const AckMessageResponse& response) { + ack_rpc_status = status; + ack_status = response.common().status(); + ack_done.store(true); + { + std::unique_lock lk(ack_mtx); + ack_cv.notify_all(); + } + }; + rpcClient.asyncAck(ack_request, &invocation_context); + + // Wait for Ack completion. + { + std::unique_lock lk(ack_mtx); + ack_cv.wait(lk, [&]() { return ack_done.load(); }); + } + + // Verify gRPC is OK + EXPECT_TRUE(ack_rpc_status.ok()); + EXPECT_EQ(google::rpc::Code::OK, ack_status.code()); + } + }; + + invocation_context->callback_ = callback; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); + invocation_context->context_.set_deadline(deadline); + + rpcClient.asyncReceive(receive_message_request, invocation_context); + void* tag; + bool ok; + bool stop = false; + while (!stop) { + const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); + grpc::CompletionQueue::NextStatus status = rpcClient.completionQueue()->AsyncNext(&tag, &ok, deadline); + EXPECT_FALSE(grpc::CompletionQueue::SHUTDOWN == status); + switch (status) { + case grpc::CompletionQueue::SHUTDOWN: + stop = true; + break; + case grpc::CompletionQueue::GOT_EVENT: { + stop = true; + auto invocation_ctx = reinterpret_cast*>(tag); + EXPECT_TRUE(nullptr != invocation_ctx); + invocation_ctx->onCompletion(ok); + break; + } + case grpc::CompletionQueue::TIMEOUT: + break; + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp new file mode 100644 index 000000000..0470d1f76 --- /dev/null +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -0,0 +1,353 @@ +#include "ClientConfig.h" +#include "IdentifiableMock.h" +#include "LogInterceptorFactory.h" +#include "MixAll.h" +#include "RpcClientImpl.h" +#include "Signature.h" +#include "TlsHelper.h" +#include "UtilAll.h" +#include "absl/container/flat_hash_set.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/CredentialsProvider.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include +#include +#include + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +class RpcClientTest : public ::testing::Test { +protected: + RpcClientTest() : completion_queue_(std::make_shared()) { + + server_authorization_check_config_ = std::make_shared( + std::make_shared()); + std::vector pem_list; + grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, + .certificate_chain = TlsHelper::client_certificate_chain}; + pem_list.emplace_back(pair); + certificate_provider_ = + std::make_shared(TlsHelper::CA, pem_list); + tls_channel_credential_options_.set_certificate_provider(certificate_provider_); + tls_channel_credential_options_.set_server_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION); + tls_channel_credential_options_.set_server_authorization_check_config(server_authorization_check_config_); + tls_channel_credential_options_.watch_root_certs(); + tls_channel_credential_options_.watch_identity_key_cert_pairs(); + channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); + credentials_observable_ = std::make_shared(); + credentials_provider_ = std::make_shared(); + ON_CALL(*credentials_observable_, tenantId).WillByDefault(testing::ReturnRef(tenant_id_)); + ON_CALL(*credentials_observable_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + } + + void SetUp() override { + + client_config_.setCredentialsProvider(std::make_shared()); + client_config_.arn(arn_); + client_config_.region(region_id_); + client_config_.tenantId(tenant_id_); + + Signature::sign(&client_config_, metadata_); + + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + name_server_channel_ = grpc::experimental::CreateCustomChannelWithInterceptors( + name_server_target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + } + + void TearDown() override {} + + ~RpcClientTest() override { completion_queue_->Shutdown(); } + + bool brokerEndpoint(const std::string& topic, std::string& endpoint) { + QueryRouteRequest route_request; + route_request.mutable_topic()->set_name(topic.data()); + QueryRouteResponse route_response; + RpcClientImpl name_server_client(completion_queue_, name_server_channel_); + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, + const QueryRouteResponse& response) { + ASSERT_TRUE(status.ok()); + std::cout << "Route Response:" << route_response.DebugString() << std::endl; + absl::MutexLock lk(&mtx); + route_response = response; + completed = true; + cv.SignalAll(); + }; + + auto invocation_context = new InvocationContext(); + invocation_context->callback_ = callback; + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + + for (const auto& item : metadata_) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + name_server_client.asyncQueryRoute(route_request, invocation_context); + + absl::flat_hash_set broker_addresses; + for (auto& partition : route_response.partitions()) { + auto& broker = partition.broker(); + if (MixAll::MASTER_BROKER_ID == broker.id()) { + for (auto& item : broker.endpoints().addresses()) { + std::string connect_string(fmt::format("{}:{}", item.host(), item.port())); + if (!broker_addresses.contains(connect_string)) { + broker_addresses.insert(connect_string); + } + } + } else { + for (const auto& item : broker.endpoints().addresses()) { + SPDLOG_WARN("Unexpected endpoint[{}:{}] with brokerId={}", item.host(), item.port(), broker.id()); + } + } + } + if (broker_addresses.empty()) { + return false; + } + + endpoint = *broker_addresses.begin(); + return true; + } + + void fillSendMessageRequest(SendMessageRequest& send_message_request) { + send_message_request.mutable_message()->mutable_topic()->set_arn(arn_); + send_message_request.mutable_message()->mutable_topic()->set_name(topic_); + std::unordered_map props; + props["key"] = "value"; + props["Jack"] = "Bauer"; + send_message_request.mutable_message()->mutable_user_attribute()->insert(props.begin(), props.end()); + const char* msgId = "1EE10C841BF3D007000011480C6ACF07"; + auto system_attribute = send_message_request.mutable_message()->mutable_system_attribute(); + system_attribute->set_message_id(msgId); + system_attribute->set_message_type(rmq::MessageType::NORMAL); + system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); + system_attribute->set_born_host(UtilAll::getHostIPv4()); + system_attribute->set_tag("TagA"); + send_message_request.mutable_message()->set_body("Example data"); + } + + RpcClientSharedPtr brokerRpcClient() { + std::string broker_endpoint; + bool success = brokerEndpoint(topic_, broker_endpoint); + if (!success) { + return nullptr; + } + SPDLOG_INFO("Target broker address: {}", broker_endpoint); + + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + auto broker_channel = grpc::experimental::CreateCustomChannelWithInterceptors( + broker_endpoint, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + auto client = std::make_shared(completion_queue_, broker_channel); + return client; + } + + void fillHeartbeatRequest(HeartbeatRequest& heartbeat_request) { + auto heartbeat_entry = new rmq::HeartbeatEntry; + heartbeat_entry->set_client_id("client_id_0"); + heartbeat_entry->mutable_consumer_group()->mutable_group()->set_name(group_); + heartbeat_entry->mutable_consumer_group()->mutable_group()->set_arn(arn_); + auto subscription_entry = new rmq::SubscriptionEntry; + subscription_entry->mutable_topic()->set_name(topic_); + subscription_entry->mutable_topic()->set_arn(arn_); + subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); + subscription_entry->mutable_expression()->set_expression("*"); + heartbeat_entry->mutable_consumer_group()->mutable_subscriptions()->AddAllocated(subscription_entry); + heartbeat_request.mutable_heartbeats()->AddAllocated(heartbeat_entry); + } + + std::string name_server_target_{"11.165.223.199:9876"}; + std::shared_ptr completion_queue_; + std::shared_ptr name_server_channel_; + std::string topic_{"yc001"}; + std::string group_{"GID_group003"}; + std::string arn_{"MQ_INST_1973281269661160_BXmPlOA6"}; + std::string tenant_id_{"sample-tenant"}; + std::string region_id_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + ClientConfig client_config_; + absl::flat_hash_map metadata_; + std::shared_ptr credentials_observable_; + CredentialsProviderPtr credentials_provider_; + std::shared_ptr certificate_provider_; + grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; + std::shared_ptr server_authorization_check_config_; + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; +}; + +TEST_F(RpcClientTest, testRouteInfo) { + RpcClientImpl client(completion_queue_, name_server_channel_); + QueryRouteRequest request; + request.mutable_topic()->set_name(topic_); + request.mutable_topic()->set_arn(arn_); + auto invocation_context = new InvocationContext(); + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, + const QueryRouteResponse& response) { + if (!status.ok()) { + std::cout << "code: " << status.error_code() << ", message: " << status.error_message() << std::endl; + } + ASSERT_TRUE(status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == response.common().status().code()); + EXPECT_FALSE(response.partitions().empty()); + absl::MutexLock lk(&mtx); + cv.SignalAll(); + }; + + invocation_context->callback_ = callback; + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + for (const auto& item : metadata_) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + client.asyncQueryRoute(request, invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, testSendMessageAsync) { + SendMessageRequest request; + fillSendMessageRequest(request); + auto client = brokerRpcClient(); + auto context = new InvocationContext(); + + for (const auto& entry : metadata_) { + context->context_.AddMetadata(entry.first, entry.second); + } + + context->callback_ = [](const grpc::Status& status, const grpc::ClientContext& client_context, + const SendMessageResponse& response) { + if ((!status.ok())) { + std::cout << "error code: " << status.error_code() << ", error message: " << status.error_message() << std::endl; + } + ASSERT_TRUE(status.ok()); + }; + client->asyncSend(request, context); + std::thread th([&]() { + InvocationContext* ctx; + bool ok = false; + completion_queue_->Next(reinterpret_cast(&ctx), &ok); + if (ok) { + ctx->onCompletion(ok); + } + }); + + if (th.joinable()) { + th.join(); + } +} + +TEST_F(RpcClientTest, testHeartbeat) { + auto client = brokerRpcClient(); + HeartbeatRequest heartbeat_request; + HeartbeatResponse response; + fillHeartbeatRequest(heartbeat_request); + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context_.AddMetadata(entry.first, entry.second); + } + + invocation_context->callback_ = [&](const grpc::Status& status, const grpc::ClientContext& context, + const HeartbeatResponse& response) { + ASSERT_TRUE(status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == response.common().status().code()); + { + completed = true; + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + + client->asyncHeartbeat(heartbeat_request, invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, testQueryAssignment) { + auto client = brokerRpcClient(); + QueryAssignmentRequest request; + request.mutable_topic()->set_name(topic_); + request.mutable_topic()->set_arn(arn_); + request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_name(group_); + QueryAssignmentResponse response; + + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context_.AddMetadata(entry.first, entry.second); + } + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, + const QueryAssignmentResponse& response) { + ASSERT_TRUE(status.ok()); + ASSERT_FALSE(response.assignments().empty()); + completed = true; + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } + }; + invocation_context->callback_ = callback; + client->asyncQueryAssignment(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +TEST_F(RpcClientTest, testHealthCheck) { + auto client = brokerRpcClient(); + HealthCheckRequest request; + const std::string& client_host = UtilAll::getHostIPv4(); + request.set_client_host(client_host); + HealthCheckResponse response; + auto invocation_context = new InvocationContext(); + + for (const auto& entry : metadata_) { + invocation_context->context_.AddMetadata(entry.first, entry.second); + } + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + invocation_context->callback_ = [&](const grpc::Status& status, const grpc::ClientContext& context, + const HealthCheckResponse& response) { + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + }; + client->asyncHealthCheck(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp new file mode 100644 index 000000000..99e3c416c --- /dev/null +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -0,0 +1,62 @@ +#include "TopAddressing.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressingTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} + +protected: + TopAddressing top_addressing; +}; + +TEST_F(TopAddressingTest, testFetchNameServerAddresses) { + std::vector list; + EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); + EXPECT_FALSE(list.empty()); + for (const auto& item : list) { + std::cout << item << std::endl; + } +} + +TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { + int override = 1; + setenv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center", override); + setenv(HostInfo::ENV_LABEL_STAGE, "DAILY", override); + std::vector list; + EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); + EXPECT_FALSE(list.empty()); + for (const auto& item : list) { + std::cout << item << std::endl; + } +} + +TEST_F(TopAddressingTest, testFetchNameServerAddresses_benchmark) { + int override = 1; + setenv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center", override); + setenv(HostInfo::ENV_LABEL_STAGE, "DAILY", override); + auto benchmark = [&]() { + for (int i = 0; i < 256; ++i) { + std::vector list; + EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); + EXPECT_FALSE(list.empty()); + } + }; + + std::vector threads; + threads.reserve(std::thread::hardware_concurrency() + 1); + for (int i = 0; i < std::thread::hardware_concurrency() + 1; i++) { + threads.emplace_back(benchmark); + } + + for (auto& thread : threads) { + thread.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp new file mode 100644 index 000000000..5b6f254e0 --- /dev/null +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -0,0 +1,157 @@ +#include "TopicPublishInfo.h" +#include "IdentifiableMock.h" +#include "LogInterceptorFactory.h" +#include "RpcClient.h" +#include "Signature.h" +#include "TlsHelper.h" +#include "rocketmq/MQMessageQueue.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class TopicPublishInfoTest : public ::testing::Test { +protected: + TopicPublishInfoTest() : completion_queue_(std::make_shared()) { + server_authorization_check_config_ = std::make_shared( + std::make_shared()); + tls_channel_credential_option_.set_server_verification_option(GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); + + std::vector pem_list; + grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, + .certificate_chain = TlsHelper::client_certificate_chain}; + pem_list.emplace_back(pair); + certificate_provider_ = + std::make_shared(TlsHelper::CA, pem_list); + tls_channel_credential_option_.set_certificate_provider(certificate_provider_); + tls_channel_credential_option_.set_server_authorization_check_config(server_authorization_check_config_); + tls_channel_credential_option_.watch_root_certs(); + tls_channel_credential_option_.watch_identity_key_cert_pairs(); + channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_option_); + credentials_provider_ = std::make_shared(); + } + + void SetUp() override { + std::vector name_server_list; + + if (!name_server_list.empty()) { + target_ = *name_server_list.begin(); + } + credentials_observable_ = std::make_shared(); + std::vector> interceptor_factories; + ON_CALL(*credentials_observable_, tenantId).WillByDefault(testing::ReturnRef(tenant_id_)); + ON_CALL(*credentials_observable_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + interceptor_factories.emplace_back(absl::make_unique()); + auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( + target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + client_ = std::make_shared(completion_queue_, channel); + + client_config_.arn(arn_); + client_config_.setCredentialsProvider(std::make_shared()); + client_config_.setIoTimeout(absl::Seconds(3)); + + Signature::sign(&client_config_, metadata_); + } + + void TearDown() override { completion_queue_->Shutdown(); } + + std::string topic_{"yc001"}; + std::string group_{"yc001"}; + std::string arn_{"MQ_INST_1973281269661160_BXmPlOA6"}; + std::string tenant_id_{"sample-tenant"}; + std::string region_id_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + std::string target_{"dns:grpc.dev:9876"}; + ClientConfig client_config_; + absl::flat_hash_map metadata_; + std::shared_ptr completion_queue_; + std::shared_ptr client_; + CredentialsProviderPtr credentials_provider_; + std::shared_ptr certificate_provider_; + grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_option_; + std::shared_ptr server_authorization_check_config_; + std::shared_ptr channel_credential_; + grpc::ChannelArguments channel_arguments_; + std::shared_ptr credentials_observable_; +}; + +TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { + rmq::QueryRouteResponse response; + rmq::QueryRouteRequest request; + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(topic_); + auto invocation_context = new InvocationContext(); + invocation_context->context_.set_deadline(std::chrono::system_clock::now() + + absl::ToChronoMilliseconds(client_config_.getIoTimeout())); + for (const auto& item : metadata_) { + invocation_context->context_.AddMetadata(item.first, item.second); + } + + auto callback = [this](const grpc::Status& status, const grpc::ClientContext& context, + const QueryRouteResponse& response) { + if (!status.ok()) { + std::cout << "code: " << status.error_code() << ", message: " << status.error_message() << std::endl; + } else { + std::cout << "Response debug string:" << response.DebugString() << std::endl; + } + ASSERT_TRUE(status.ok()); + + std::vector partitions; + for (const auto& item : response.partitions()) { + Topic topic(arn_, topic_); + Permission permission; + switch (item.permission()) { + case rmq::Permission::READ: + permission = Permission::READ; + break; + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + permission = Permission::NONE; + break; + } + + AddressScheme scheme; + switch (item.broker().endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + scheme = AddressScheme::IPv4; + } + + std::vector
addresses; + for (const auto& host_port : item.broker().endpoints().addresses()) { + Address address(host_port.host(), host_port.port()); + addresses.emplace_back(address); + } + ServiceAddress service_address(scheme, addresses); + + Broker broker(item.broker().name(), item.broker().id(), service_address); + Partition partition(topic, item.id(), permission, broker); + partitions.emplace_back(partition); + } + auto topic_route_data = std::make_shared(partitions, response.common().DebugString()); + rocketmq::TopicPublishInfo topic_publish_info(topic_, topic_route_data); + rocketmq::MQMessageQueue message_queue; + EXPECT_TRUE(topic_publish_info.selectOneMessageQueue(message_queue)); + + EXPECT_STREQ(topic_.c_str(), message_queue.getTopic().c_str()); + }; + + invocation_context->callback_ = callback; + + client_->asyncQueryRoute(request, invocation_context); +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/AbseilTimeTest.cpp b/src/test/cpp/ut/AbseilTimeTest.cpp new file mode 100644 index 000000000..bcd00b98b --- /dev/null +++ b/src/test/cpp/ut/AbseilTimeTest.cpp @@ -0,0 +1,11 @@ +#include "absl/time/time.h" +#include + +TEST(AbseilTimeTest, testTimeFormat) { + auto now = absl::FromChrono(std::chrono::system_clock::now()); + auto utc_time_zone = absl::UTCTimeZone(); + auto local_time_zone = absl::LocalTimeZone(); + const char* time_format = "%Y%m%dT%H%M%SZ"; + std::cout << "UTC: " << absl::FormatTime(time_format, now, utc_time_zone) << std::endl; + std::cout << "Beijing Time: " << absl::FormatTime(time_format, now, local_time_zone) << std::endl; +} \ No newline at end of file diff --git a/src/test/cpp/ut/AsanDemo.cpp b/src/test/cpp/ut/AsanDemo.cpp new file mode 100644 index 000000000..b4fd0791e --- /dev/null +++ b/src/test/cpp/ut/AsanDemo.cpp @@ -0,0 +1,7 @@ +#include "gtest/gtest.h" +#include + +TEST(AsanTest, testMemoryLeakage) { + auto p = new int(1); + std::cout << *p << std::endl; +} \ No newline at end of file diff --git a/src/test/cpp/ut/BUILD b/src/test/cpp/ut/BUILD new file mode 100644 index 000000000..34c2f6d11 --- /dev/null +++ b/src/test/cpp/ut/BUILD @@ -0,0 +1,98 @@ +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") +cc_test( + name = "gtest_example", + srcs = [ + "GTestExample.cpp", + ], + deps = [ + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "event_test", + srcs = [ + "EventTest.cpp", + ], + deps = [ + "//external:libuv", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_test", + srcs = [ + "LogTest.cpp", + ], + deps = [ + "@com_github_gabime_spdlog//:spdlog", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "asan_demo", + srcs = [ + "AsanDemo.cpp", + ], + deps = [ + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "curl_test", + srcs = [ + "CurlTest.cpp", + ], + deps = [ + "@curl//:curl", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "absl_time_test", + srcs = [ + "AbseilTimeTest.cpp", + ], + deps = [ + "@com_google_googletest//:gtest_main", + "@com_google_absl//absl/time", + ], +) + +cc_test( + name = "hex_test", + srcs = [ + "HexTest.cpp", + ], + deps = [ + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "zlib_test", + srcs = [ + "ZlibTest.cpp", + ], + deps = [ + "@zlib//:zlib", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "thread_pool_test", + srcs = [ + "ThreadPoolTest.cpp", + ], + deps = [ + "@com_github_progschj_ThreadPool//:ThreadPool", + "@com_github_gabime_spdlog//:spdlog", + "@com_google_googletest//:gtest_main", + ] +) \ No newline at end of file diff --git a/src/test/cpp/ut/CMakeLists.txt b/src/test/cpp/ut/CMakeLists.txt new file mode 100644 index 000000000..8b87dcf32 --- /dev/null +++ b/src/test/cpp/ut/CMakeLists.txt @@ -0,0 +1,41 @@ +add_executable(gtest_example GTestExample.cpp) +target_link_libraries(gtest_example gtest_main) +add_test(ut_gtest_example gtest_example) + +add_executable(log_test LogTest.cpp) +target_link_libraries(log_test + spdlog + gtest_main) +add_test(ut_log_test log_test) + +add_executable(thread_pool_test ThreadPoolTest.cpp) +target_link_libraries(thread_pool_test gtest_main spdlog thread_pool) + +add_executable(event_test EventTest.cpp) +target_link_libraries(event_test + gtest_main + uv_a) + +add_executable(zlib_test ZlibTest.cpp) +target_link_libraries(zlib_test + PRIVATE + gtest_main + zlib) + +add_executable(abseil_time_test AbseilTimeTest.cpp) +target_link_libraries(abseil_time_test + PRIVATE + gtest_main + absl::time) + +add_executable(hex_test HexTest.cpp) +target_link_libraries(hex_test + PRIVATE + absl::strings + gtest_main) + +add_subdirectory(base) +add_subdirectory(client) +add_subdirectory(rocketmq) +add_subdirectory(logger) +add_subdirectory(admin) \ No newline at end of file diff --git a/src/test/cpp/ut/CurlTest.cpp b/src/test/cpp/ut/CurlTest.cpp new file mode 100644 index 000000000..b130c2512 --- /dev/null +++ b/src/test/cpp/ut/CurlTest.cpp @@ -0,0 +1,29 @@ +#include "curl/curl.h" +#include + +size_t write_callback(void* contents, size_t size, size_t nmemb, void* user_param) { + std::string result; + result.reserve(size * nmemb); + result.append(reinterpret_cast(contents), size * nmemb); + std::cout << "Callback invoked. Size: " << size << ", nmemb: " << nmemb << ". Content: " << result << std::endl; + return size * nmemb; +} + +int main(int argc, char* argv[]) { + CURL* curl; + CURLcode res; + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + const char* url = "http://www.taobao.com"; + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + std::cerr << "curl_easy_perform() failed" << curl_easy_strerror(res) << std::endl; + } + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + return 0; +} \ No newline at end of file diff --git a/src/test/cpp/ut/EventTest.cpp b/src/test/cpp/ut/EventTest.cpp new file mode 100644 index 000000000..7360f9712 --- /dev/null +++ b/src/test/cpp/ut/EventTest.cpp @@ -0,0 +1,37 @@ +#include "uv.h" +#include "gtest/gtest.h" +#include + +class EventTest : public testing::Test { +public: + static void sample_callback(uv_timer_t* handle) { + if (count++ > 3) { + uv_timer_stop(handle); + } + } + + void SetUp() {} + + void TearDown() {} + +protected: + static int count; +}; +int EventTest::count = 0; + +TEST_F(EventTest, testEvent) { + uv_loop_t* loop = uv_default_loop(); + uv_timer_t timer; + uv_timer_init(loop, &timer); + uv_timer_start(&timer, &EventTest::sample_callback, 10, 20); + uv_run(loop, UV_RUN_DEFAULT); + uv_loop_close(loop); + EXPECT_TRUE(EventTest::count >= 3); +} + +TEST_F(EventTest, test_loop_close) { + uv_loop_t* loop = uv_default_loop(); + if (uv_loop_alive(loop)) { + uv_loop_close(loop); + } +} \ No newline at end of file diff --git a/src/test/cpp/ut/GTestExample.cpp b/src/test/cpp/ut/GTestExample.cpp new file mode 100644 index 000000000..9f76467a5 --- /dev/null +++ b/src/test/cpp/ut/GTestExample.cpp @@ -0,0 +1,4 @@ +#include "gtest/gtest.h" +#include + +TEST(GTestExample, testExample) { std::cout << "GTest works" << std::endl; } \ No newline at end of file diff --git a/src/test/cpp/ut/HexTest.cpp b/src/test/cpp/ut/HexTest.cpp new file mode 100644 index 000000000..f7caf2d4b --- /dev/null +++ b/src/test/cpp/ut/HexTest.cpp @@ -0,0 +1,17 @@ +#include "absl/strings/str_cat.h" +#include "gtest/gtest.h" +#include + +TEST(HexTest, testHex) { + const char* data = "hello"; + const char dict[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + std::string s; + for (std::size_t i = 0; i < strlen(data); i++) { + char c = *(data + i); + s.append(&dict[(0xF0 & c) >> 4], 1); + s.append(&dict[(0x0F & c)], 1); + } + + std::string expect{"68656C6C6F"}; + EXPECT_EQ(expect, s); +} \ No newline at end of file diff --git a/src/test/cpp/ut/LogTest.cpp b/src/test/cpp/ut/LogTest.cpp new file mode 100644 index 000000000..ddd8e1998 --- /dev/null +++ b/src/test/cpp/ut/LogTest.cpp @@ -0,0 +1,29 @@ +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include + +TEST(SpdlogTest, testLog) { + std::cout << "It works" << std::endl; + std::cout << "It works" << std::endl; + spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); + spdlog::set_level(spdlog::level::trace); + spdlog::info("Welcome to the world of spdlog"); + spdlog::info("Code assistant is working"); + spdlog::info("It works in Visual Studio Code"); + + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::trace); + console_sink->set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); + + auto rotating_file_sink = std::make_shared("logs/log.txt", 65536, 3, true); + + spdlog::logger logger("multi-sink-logger", {console_sink, rotating_file_sink}); + logger.set_level(spdlog::level::debug); + logger.info("This log is expected present in both console and file"); + + spdlog::log(spdlog::source_loc(__FILE__, __LINE__, "testLog"), spdlog::level::debug, "Hello {}", "world"); + + SPDLOG_INFO("Hello, macro {}, {}:{}", "world", __FILE__, __LINE__); +} diff --git a/src/test/cpp/ut/README.md b/src/test/cpp/ut/README.md new file mode 100644 index 000000000..f16f14eea --- /dev/null +++ b/src/test/cpp/ut/README.md @@ -0,0 +1,3 @@ +## Introduction +This directory is supposed to contain all unit tests for this project. File hierarchy of unit test files should resemble +those of main sources. Aka, Unit test file for src/main/cpp/base/UtilAll.cpp should be src/test/cpp/base/UtilAllTest.cpp \ No newline at end of file diff --git a/src/test/cpp/ut/ThreadPoolTest.cpp b/src/test/cpp/ut/ThreadPoolTest.cpp new file mode 100644 index 000000000..9e0299c68 --- /dev/null +++ b/src/test/cpp/ut/ThreadPoolTest.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + +#include "ThreadPool.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +TEST(ThreadPoolTest, testLambda) { + ThreadPool pool(4); + std::vector> results; + auto task = [](int i) { + { spdlog::info("hello {}", i); } + std::this_thread::sleep_for(std::chrono::seconds(1)); + { spdlog::info("world {}", i); } + return i * i; + }; + + for (int i = 0; i < 8; ++i) { + results.emplace_back(pool.enqueue(task, i)); + } + + for (auto&& result : results) { + { spdlog::info("result {}", result.get()); } + } +} + +int task(int input) { + { spdlog::info("hello {}", input); } + std::this_thread::sleep_for(std::chrono::seconds(1)); + { spdlog::info("world {}", input); } + return input * input; +} + +TEST(ThreadPoolTest, testFuntion) { + + ThreadPool pool(4); + std::vector> results; + + for (int i = 0; i < 8; ++i) { + results.emplace_back(pool.enqueue(&task, i)); + } + + for (auto&& result : results) { + { spdlog::info("result {}", result.get()); } + } +} diff --git a/src/test/cpp/ut/ZlibTest.cpp b/src/test/cpp/ut/ZlibTest.cpp new file mode 100644 index 000000000..530880150 --- /dev/null +++ b/src/test/cpp/ut/ZlibTest.cpp @@ -0,0 +1,81 @@ +#include "zlib.h" +#include "gtest/gtest.h" +#include + +TEST(ZlibTest, testDeflate) { + + uint32_t len = 1024 * 1024; + auto data = new unsigned char[len]; + + for (uint32_t i = 0; i < len; i++) { + data[i] = static_cast(i % 128); + } + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_in = len; + stream.next_in = data; + + deflateInit(&stream, Z_BEST_COMPRESSION); + uint32_t bound = deflateBound(&stream, len); + auto compressed = new unsigned char[bound]; + stream.avail_out = bound; + stream.next_out = compressed; + + int status = deflate(&stream, Z_FINISH); + if (stream.msg) { + std::cout << stream.msg << std::endl; + } + EXPECT_EQ(Z_STREAM_END, status); + deflateEnd(&stream); + + z_stream inflate_stream; + inflate_stream.zfree = Z_NULL; + inflate_stream.zalloc = Z_NULL; + inflate_stream.opaque = Z_NULL; + + inflate_stream.avail_in = stream.total_out; + inflate_stream.next_in = compressed; + + uint32_t buffer_len = 1024 * 4; + auto buffer = new unsigned char[buffer_len]; + + inflateInit(&inflate_stream); + inflate_stream.next_out = buffer; + inflate_stream.avail_out = buffer_len; + + std::vector raw; + while (true) { + status = inflate(&inflate_stream, Z_SYNC_FLUSH); + + if (Z_STREAM_ERROR == status) { + if (inflate_stream.msg) { + std::cerr << inflate_stream.msg << std::endl; + } + break; + } + raw.reserve(raw.size() + buffer_len - inflate_stream.avail_out); + std::copy(&buffer[0], &buffer[buffer_len - inflate_stream.avail_out], std::back_inserter(raw)); + inflate_stream.avail_out = buffer_len; + inflate_stream.next_out = buffer; + + if (Z_STREAM_END == status) { + std::cout << "Inflation done! Total in: " << inflate_stream.total_in << ", Total out:" << inflate_stream.total_out + << std::endl; + break; + } + + if (Z_OK == status) { + std::cout << "Some data are inflated and flushed. Available in: " << inflate_stream.avail_in + << ", Total out:" << inflate_stream.total_out << std::endl; + } + } + + EXPECT_EQ(std::vector(data, data + len), raw); + + delete[] data; + delete[] compressed; + delete[] buffer; +} \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp new file mode 100644 index 000000000..e6ff67ca9 --- /dev/null +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -0,0 +1,43 @@ +#include + +#include "AdminServerImpl.h" +#include "apache/rocketmq/v1/admin.grpc.pb.h" + +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/spdlog.h" + +#include + +namespace rmq = apache::rocketmq::v1; +using namespace rocketmq::admin; + +TEST(AdminServerTest, testSetUp) { + + auto logger = spdlog::basic_logger_mt("rocketmq_logger", "logs/test.log"); + logger->set_level(spdlog::level::debug); + spdlog::set_default_logger(logger); + + AdminServer* admin_server = new AdminServerImpl; + admin_server->start(); + + std::string address("127.0.0.1:"); + address.append(std::to_string(admin_server->port())); + auto channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials()); + + auto stub = rmq::Admin::NewStub(channel); + + rmq::ChangeLogLevelRequest request; + request.set_level(rmq::ChangeLogLevelRequest_Level_INFO); + rmq::ChangeLogLevelResponse response; + + grpc::ClientContext context; + context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(10)); + + auto status = stub->ChangeLogLevel(&context, request, &response); + + EXPECT_TRUE(status.ok()); + EXPECT_STREQ("OK", response.remark().c_str()); + EXPECT_EQ(spdlog::level::info, logger->level()); + + admin_server->stop(); +} \ No newline at end of file diff --git a/src/test/cpp/ut/admin/BUILD.bazel b/src/test/cpp/ut/admin/BUILD.bazel new file mode 100644 index 000000000..506e27e34 --- /dev/null +++ b/src/test/cpp/ut/admin/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "admin_server_test", + srcs = [ + "AdminServerTest.cpp", + ], + deps = [ + "//src/main/cpp/admin:admin_server_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/admin/CMakeLists.txt b/src/test/cpp/ut/admin/CMakeLists.txt new file mode 100644 index 000000000..d746bef03 --- /dev/null +++ b/src/test/cpp/ut/admin/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(admin_server_test AdminServerTest.cpp) +target_link_libraries(admin_server_test + PRIVATE + gtest_main + admin_library + ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel new file mode 100644 index 000000000..5d55ecb47 --- /dev/null +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -0,0 +1,62 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "histogram_test", + srcs = [ + "HistogramTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "mix_all_test", + srcs = [ + "MixAllTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "rate_limiter_test", + srcs = [ + "RateLimiterTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "util_all_test", + srcs = [ + "UtilAllTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + "@zlib//:zlib", + ], + linkstatic = True, +) + +cc_test( + name = "ut_request_id_generator", + srcs = [ + "RequestIdGeneratorTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/base/CMakeLists.txt b/src/test/cpp/ut/base/CMakeLists.txt new file mode 100644 index 000000000..e537e45dd --- /dev/null +++ b/src/test/cpp/ut/base/CMakeLists.txt @@ -0,0 +1,31 @@ +add_executable(mix_all_test MixAllTest.cpp) +target_link_libraries(mix_all_test + PRIVATE + base_library + gtest_main) + +add_executable(util_all_test UtilAllTest.cpp) +target_link_libraries(util_all_test + PRIVATE + base_library + spdlog::spdlog + absl::base + gtest_main) + +add_executable(rate_limiter_test RateLimiterTest.cpp) +target_link_libraries(rate_limiter_test PRIVATE + gtest_main + base_library) + +add_executable(histogram_test HistogramTest.cpp) +target_link_libraries(histogram_test + PRIVATE + gtest_main + base_library) +add_test(test_histogram histogram_test) + +add_executable(ut_request_id_generator RequestIdGeneratorTest.cpp) +target_link_libraries(ut_request_id_generator + PRIVATE + gtest_main + base_library) \ No newline at end of file diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp new file mode 100644 index 000000000..6ce7a7087 --- /dev/null +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -0,0 +1,41 @@ +#include "Histogram.h" +#include + +namespace rocketmq { +class HistogramTest : public testing::Test { +public: + HistogramTest() : histogram_("Test", 3) {} + + void SetUp() override { + histogram_.labels().emplace_back("Foo: "); + histogram_.labels().emplace_back("Bar: "); + histogram_.labels().emplace_back("Baz: "); + } + +protected: + Histogram histogram_; +}; + +TEST_F(HistogramTest, testHistogram_no_count) { + + std::string result; + histogram_.reportAndReset(result); + + std::string expect("Test:Foo: 0, Bar: 0, Baz: 0"); + EXPECT_STREQ(result.c_str(), expect.c_str()); +} + +TEST_F(HistogramTest, testHistogram) { + histogram_.countIn(0); + histogram_.countIn(-1); + histogram_.countIn(4); + histogram_.countIn(2); + + std::string result; + histogram_.reportAndReset(result); + + std::string expect("Test:Foo: 1, Bar: 0, Baz: 2"); + EXPECT_STREQ(result.c_str(), expect.c_str()); +} + +} // namespace rocketmq diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp new file mode 100644 index 000000000..b0e122510 --- /dev/null +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -0,0 +1,56 @@ +#include "MixAll.h" +#include "rocketmq/MQMessage.h" +#include "gtest/gtest.h" + +using namespace rocketmq; + +class MixAllTest : public testing::Test {}; + +TEST_F(MixAllTest, testValidate_empty_topic) { + MQMessage message; + ASSERT_FALSE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_normal_topic) { + MQMessage message("T_abc-123", "sample_body"); + ASSERT_TRUE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_topic_too_long) { + + std::string topic("T"); + for (int i = 0;; ++i) { + topic.append(std::to_string(i)); + if (topic.length() > 64) { + break; + } + } + MQMessage message(topic, "sample_body"); + ASSERT_FALSE(MixAll::validate(message)); +} + +TEST_F(MixAllTest, testValidate_body_too_large) { + std::string topic("TestTopic"); + std::string body; + + body.reserve(MixAll::MAX_MESSAGE_BODY_SIZE + 1); + for (int i = 0; i <= MixAll::MAX_MESSAGE_BODY_SIZE; ++i) { + body.append("a"); + } + ASSERT_FALSE(MixAll::validate(MQMessage(topic, body))); +} + +TEST_F(MixAllTest, testRandom) { + uint32_t left = 1; + uint32_t right = 100; + uint32_t random_number = MixAll::random(left, right); + EXPECT_TRUE(random_number >= left && random_number <= right); +} + +TEST_F(MixAllTest, testHex) { + const char* data = "abc"; + std::string hex = MixAll::hex(data, strlen(data)); + std::vector bin; + EXPECT_TRUE(MixAll::hexToBinary(hex, bin)); + EXPECT_EQ(hex, MixAll::hex(bin.data(), bin.size())); +} \ No newline at end of file diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp new file mode 100644 index 000000000..0226841c9 --- /dev/null +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -0,0 +1,49 @@ +#include "RateLimiter.h" +#include "gtest/gtest.h" +#include +#include + +namespace rocketmq { +class RateLimiterTest : public ::testing::Test { +public: + void TearDown() override { observer.stop(); } + +protected: + RateLimiterObserver observer; +}; + +TEST_F(RateLimiterTest, basicTest) { + std::shared_ptr> limiter(new RateLimiter<10>(1000)); + observer.subscribe(limiter); + + std::atomic_bool stopped(false); + std::atomic_long acquired(0); + + std::thread timer([&] { + std::this_thread::sleep_for(std::chrono::seconds(10)); + stopped.store(true); + }); + + std::thread report([&] { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long qps = acquired.load(); + while (!acquired.compare_exchange_weak(qps, 0)) { + qps = acquired.load(); + } + std::cout << "QPS: " << qps << ", available=" << limiter->available() << std::endl; + } + }); + + std::thread t([&] { + while (!stopped.load()) { + limiter->acquire(); + acquired++; + } + }); + + timer.join(); + t.join(); + report.join(); +} +} // namespace rocketmq \ No newline at end of file diff --git a/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp b/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp new file mode 100644 index 000000000..2fceb3f0f --- /dev/null +++ b/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp @@ -0,0 +1,16 @@ +#include "RequestIdGenerator.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class RequestIdGeneratorTest : public testing::Test {}; + +TEST_F(RequestIdGeneratorTest, testNextRequestId) { + std::string request_id = RequestIdGenerator::nextRequestId(); + ASSERT_TRUE(!request_id.empty()); + EXPECT_EQ(1, RequestIdGenerator::sequenceOf(request_id)); + std::cout << request_id << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp new file mode 100644 index 000000000..e3989ce23 --- /dev/null +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -0,0 +1,77 @@ +#include "UtilAll.h" +#include "absl/strings/str_split.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include +#include + +namespace rocketmq { + +class UtilAllTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(UtilAllTest, testGetIPv4Addresses) { + spdlog::set_level(spdlog::level::debug); + std::vector addresses = UtilAll::getIPv4Addresses(); + EXPECT_FALSE(addresses.empty()); + for (const auto& ip : addresses) { + spdlog::debug("IP: {}", ip); + } + std::string chosen; + if (UtilAll::pickIPv4Address(addresses, chosen)) { + spdlog::debug("The chosen one is: {}", chosen); + } +} + +TEST_F(UtilAllTest, testGetHostIPv4) { + std::string ip = UtilAll::getHostIPv4(); + EXPECT_TRUE(UtilAll::LOOP_BACK_IP != ip); + spdlog::debug("Host IP: {}", ip); +} + +TEST_F(UtilAllTest, testCompress) { + std::string src("How are you doing?"); + std::string dst; + bool success = UtilAll::compress(src, dst); + EXPECT_TRUE(success); + EXPECT_FALSE(dst.empty()); +} + +TEST_F(UtilAllTest, testUncompress) { + std::string raw("What is your favorite color?"); + std::string compressed; + EXPECT_TRUE(UtilAll::compress(raw, compressed)); + std::string uncompressed; + EXPECT_TRUE(UtilAll::uncompress(compressed, uncompressed)); + EXPECT_EQ(raw.length(), uncompressed.length()); + EXPECT_EQ(raw, uncompressed); +} + +TEST_F(UtilAllTest, benchmarkTest) { + std::string raw; + uint32_t len = 1024 * 1024; + raw.reserve(len); + for (uint32_t i = 0; i < len; i++) { + raw.push_back(i % 128); + } + + std::string compressed; + auto now = std::chrono::steady_clock::now(); + UtilAll::compress(raw, compressed); + auto elapsed = std::chrono::steady_clock::now() - now; + EXPECT_TRUE(elapsed < std::chrono::milliseconds(100)); + EXPECT_TRUE(len / compressed.length() >= 5); +} + +TEST_F(UtilAllTest, split) { + std::string ip("8.8.8.8"); + std::vector segments = absl::StrSplit(ip, '.'); + std::vector expected = {"8", "8", "8", "8"}; + EXPECT_EQ(expected, segments); +} + +} // namespace rocketmq diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel new file mode 100644 index 000000000..d2ee5a1ac --- /dev/null +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -0,0 +1,55 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "scheduler_test", + srcs = [ + "SchedulerTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "//src/main/cpp/scheduler:scheduler_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "countdown_latch_test", + srcs = [ + "CountdownLatchTest.cpp", + ], + deps = [ + "//src/main/cpp/concurrent:countdown_latch_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "tls_helper_test", + srcs = [ + "TlsHelperTest.cpp", + ], + deps = [ + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + "@boringssl//:ssl", + ], + linkstatic = True, +) + +cc_test( + name = "rpc_client_test", + srcs = [ + "RpcClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "//src/main/cpp/client:rpc_client_mock", + "@com_google_googletest//:gtest_main", + "@boringssl//:ssl", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CMakeLists.txt b/src/test/cpp/ut/client/CMakeLists.txt new file mode 100644 index 000000000..bedefc8a1 --- /dev/null +++ b/src/test/cpp/ut/client/CMakeLists.txt @@ -0,0 +1,40 @@ +add_executable(countdown_latch_test CountdownLatchTest.cpp) +target_link_libraries(countdown_latch_test + PRIVATE + gtest_main + countdown_latch_library + spdlog) +add_test(ut_countdown_latch_test countdown_latch_test) + +add_executable(scheduler_test SchedulerTest.cpp) +target_link_libraries(scheduler_test + PRIVATE + absl::synchronization + base_library + gtest_main + scheduler_library) +add_test(ut_scheduler_test scheduler_test) + +if (ENABLE_TRACING) +# add_executable(tracing_utility_test TracingUtilityTest.cpp) +# target_link_libraries(tracing_utility_test +# PRIVATE +# client_library +# gtest_main) +# add_test(ut_tracing_utility_test tracing_utility_test) +endif () + +add_executable(tls_helper_test TlsHelperTest.cpp) +target_link_libraries(tls_helper_test + PRIVATE + client_library + gtest_main) + + +add_executable(rpc_client_unit_test RpcClientTest.cpp) +target_link_libraries(rpc_client_unit_test + PRIVATE + rpc_client_mock + client_library + gtest_main) +add_test(ut_rpc_client rpc_client_unit_test) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CountdownLatchTest.cpp b/src/test/cpp/ut/client/CountdownLatchTest.cpp new file mode 100644 index 000000000..4b9d47eb8 --- /dev/null +++ b/src/test/cpp/ut/client/CountdownLatchTest.cpp @@ -0,0 +1,33 @@ +#include "CountdownLatch.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(CountdownLatchTest, testCountdown) { + CountdownLatch countdown_latch(0); + + countdown_latch.increaseCount(); + + auto start = std::chrono::system_clock::now(); + + std::thread t([&] { + std::this_thread::sleep_for(std::chrono::seconds(3)); + spdlog::info("Counting down now..."); + countdown_latch.countdown(); + }); + + spdlog::info("Start to await"); + countdown_latch.await(); + spdlog::info("Await over"); + auto duration = std::chrono::system_clock::now() - start; + EXPECT_TRUE(std::chrono::duration_cast(duration).count() >= 3); + + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp new file mode 100644 index 000000000..d41d4e576 --- /dev/null +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -0,0 +1,66 @@ +#include "RpcClientMock.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace ut { + +class RpcClientTest : public testing::Test { + +public: + void SetUp() override { grpc::internal::GrpcLibraryInitializer initializer; } + + static void mockQueryRouteInfo(const QueryRouteRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + for (int i = 0; i < 3; ++i) { + auto partition = new rmq::Partition; + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_broker()->set_name(fmt::format("broker-{}", i)); + partition->mutable_broker()->set_id(0); + auto endpoint = partition->mutable_broker()->mutable_endpoints(); + auto address = new rmq::Address; + address->set_host(fmt::format("10.0.0.{}", i)); + address->set_port(10911); + endpoint->mutable_addresses()->AddAllocated(address); + invocation_context->response_.mutable_partitions()->AddAllocated(partition); + } + + // Mock invoke callback. + invocation_context->callback_(invocation_context->status_, invocation_context->context_, + invocation_context->response_); + } +}; + +TEST_F(RpcClientTest, testMockedGetRouteInfo) { + RpcClientMock rpc_client_mock; + ON_CALL(rpc_client_mock, asyncQueryRoute(testing::_, testing::_)).WillByDefault(testing::Invoke(mockQueryRouteInfo)); + std::string topic = "sample_topic"; + QueryRouteRequest request; + request.mutable_topic()->set_name(topic); + QueryRouteResponse response; + absl::flat_hash_map metadata; + auto invocation_context = new InvocationContext(); + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, + const QueryRouteResponse& response) { + EXPECT_TRUE(status.ok()); + EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + EXPECT_EQ(3, response.partitions().size()); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + invocation_context->callback_ = callback; + rpc_client_mock.asyncQueryRoute(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +} // namespace ut +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/SchedulerTest.cpp b/src/test/cpp/ut/client/SchedulerTest.cpp new file mode 100644 index 000000000..50bbba1b3 --- /dev/null +++ b/src/test/cpp/ut/client/SchedulerTest.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +#include "Functional.h" +#include "Scheduler.h" +#include "gtest/gtest.h" + +#include "absl/synchronization/mutex.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class SchedulerTest : public testing::Test { +public: + void SetUp() override { + spdlog::set_level(spdlog::level::debug); + std::cout << "SetUp invoked" << std::endl; + } + + void TearDown() override { std::cout << "TearDown invoked" << std::endl; } + +protected: + Scheduler scheduler; +}; + +TEST_F(SchedulerTest, testStartShutdown) { + scheduler.start(); + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testStartReentrancy) { + scheduler.start(); + scheduler.start(); + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testShutdownReentrancy) { + scheduler.start(); + scheduler.shutdown(); + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testShutdownWithoutStart) { scheduler.shutdown(); } + +absl::Mutex mtx; +absl::CondVar cv; +int callback_fire_count = 0; + +std::function callback = []() { + { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + } + std::cout << "It works from callback" << std::endl; +}; + +TEST_F(SchedulerTest, testScheduler) { + scheduler.start(); + FunctionalSharePtr functor = std::make_shared(&callback); + + bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(10), std::chrono::milliseconds(0)); + + EXPECT_TRUE(scheduled); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + EXPECT_TRUE(!cv.WaitWithTimeout(&mtx, absl::Seconds(3))); + } + + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testCancel) { + scheduler.start(); + FunctionalSharePtr functor = std::make_shared(&callback); + bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(10), std::chrono::seconds(1)); + EXPECT_TRUE(scheduled); + std::this_thread::sleep_for(std::chrono::seconds(3)); + bool cancelled = scheduler.cancel(&callback); + EXPECT_TRUE(cancelled); + + { + absl::MutexLock lock(&mtx); + callback_fire_count = 0; + } + + std::this_thread::sleep_for(std::chrono::seconds(3)); + ASSERT_EQ(0, callback_fire_count); + + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testSchedulerManyTimes) { + scheduler.start(); + FunctionalSharePtr functor = std::make_shared(&callback); + bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(0), std::chrono::seconds(1)); + EXPECT_TRUE(scheduled); + + // Wait till callback is executed. + std::this_thread::sleep_for(std::chrono::seconds(5)); + + scheduler.shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TlsHelperTest.cpp b/src/test/cpp/ut/client/TlsHelperTest.cpp new file mode 100644 index 000000000..845f84116 --- /dev/null +++ b/src/test/cpp/ut/client/TlsHelperTest.cpp @@ -0,0 +1,15 @@ +#include "TlsHelper.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(TlsHelperTest, testSign) { + const char* data = "some random data for test purpose only"; + const char* access_secret = "arbitrary-access-key"; + const std::string& signature = TlsHelper::sign(access_secret, data); + const char* expect = "567868dc8e81f1e8095f88958edff1e07db4290e"; + EXPECT_STRCASEEQ(expect, signature.c_str()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp new file mode 100644 index 000000000..ec1ec2d9a --- /dev/null +++ b/src/test/cpp/ut/client/TracingUtilityTest.cpp @@ -0,0 +1,33 @@ +#include "TracingUtility.h" +#include "gtest/gtest.h" +#include + +using namespace opentelemetry::trace; + +ROCKETMQ_NAMESPACE_BEGIN + +template static std::string hex(const T& id_item) { + char buf[T::kSize * 2]; + id_item.ToLowerBase16(buf); + return std::string(buf, sizeof(buf)); +} + +TEST(TracingUtilityTest, testInject) { + constexpr uint8_t buf_span[] = {1, 2, 3, 4, 5, 6, 7, 8}; + constexpr uint8_t buf_trace[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + SpanContext span_context{TraceId{buf_trace}, SpanId{buf_span}, TraceFlags{true}, true}; + EXPECT_EQ(TracingUtility::injectSpanContextToTraceParent(span_context), + "00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); +} + +TEST(TracingUtilityTest, testExtract) { + SpanContext span_context = + TracingUtility::extractContextFromTraceParent("00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); + + EXPECT_EQ(hex(span_context.trace_id()), "0102030405060708090a0b0c0d0e0f10"); + EXPECT_EQ(hex(span_context.span_id()), "0102030405060708"); + EXPECT_TRUE(span_context.IsSampled()); + EXPECT_TRUE(span_context.IsRemote()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/test/cpp/ut/logger/BUILD.bazel new file mode 100644 index 000000000..fe03dc8f1 --- /dev/null +++ b/src/test/cpp/ut/logger/BUILD.bazel @@ -0,0 +1,17 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "logger_test", + srcs = [ + "LoggerTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/log:log_library", + "@com_google_googletest//:gtest_main", + ], + defines = [ + "BAZEL", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/logger/CMakeLists.txt b/src/test/cpp/ut/logger/CMakeLists.txt new file mode 100644 index 000000000..00b056d27 --- /dev/null +++ b/src/test/cpp/ut/logger/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(logger_test LoggerTest.cpp) +target_link_libraries(logger_test + PRIVATE gtest_main + PRIVATE logger_library + PRIVATE spdlog::spdlog) \ No newline at end of file diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp new file mode 100644 index 000000000..7aabb0db1 --- /dev/null +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -0,0 +1,25 @@ +#include "rocketmq/Logger.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +using namespace rocketmq; + +class LoggerTest : public testing::Test {}; + +TEST_F(LoggerTest, testDefaultLogger) { + Logger& logger = getLogger(); + logger.setFileSize(1024); + logger.setLevel(Level::Info); + +// TODO: Figure out constraints of file system when building with bazel +#ifndef BAZEL + logger.init(); +#endif + + for (int i = 0; i < 32; ++i) { + SPDLOG_DEBUG("Should not appear by default"); + SPDLOG_INFO("Should show up!!"); + SPDLOG_WARN("Warn message is here"); + SPDLOG_ERROR("This is an error message"); + } +} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel new file mode 100644 index 000000000..9cdf75f09 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -0,0 +1,140 @@ +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_library") + +cc_test( + name = "executor_test", + srcs = [ + "ExecutorTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "message_client_id_setter_test", + srcs = [ + "MessageClientIDSetterTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "message_test", + srcs = [ + "MessageTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_library( + name = "semaphore_library", + hdrs = [ + "include/Semaphore.h", + ], + srcs = [ + "Semaphore.cpp", + ], + strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", + deps = [ + "@com_google_absl//absl/base", + "@com_google_absl//absl/synchronization", + ], +) + +cc_test( + name = "semaphore_test", + srcs = [ + "SemaphoreTest.cpp", + ], + deps = [ + ":semaphore_library", + "@com_github_gabime_spdlog//:spdlog", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "process_queue_test", + srcs = [ + "ProcessQueueTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_library( + name = "client_interface", + hdrs = [ + "include/MQClientTest.h", + ], + srcs = [], + strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", + deps = [ + "//src/main/cpp/client:rpc_client_mock", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "default_mq_producer_test", + srcs = [ + "DefaultMQProducerTest.cpp", + ], + deps = [ + ":client_interface", + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/rocketmq:rocketmq_library", + ], + linkstatic = True, +) + +cc_test( + name = "default_mq_push_consumer_test", + srcs = [ + "DefaultMQPushConsumerTest.cpp", + ], + deps = [ + ":client_interface", + "//src/main/cpp/admin:admin_server_library", + "//src/main/cpp/rocketmq:rocketmq_library", + ], + linkstatic = True, +) + +cc_test( + name = "ut_credentials_provider", + srcs = [ + "CredentialsProviderTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) + +cc_test( + name = "ut_send_callbacks", + srcs = [ + "SendCallbacksTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt new file mode 100644 index 000000000..d4498a094 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/CMakeLists.txt @@ -0,0 +1,71 @@ +add_library(semaphore_library + Semaphore.cpp) +target_include_directories(semaphore_library + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(semaphore_library + absl::synchronization) + +add_library(client_test_interface INTERFACE) +target_include_directories(client_test_interface + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(client_test_interface + INTERFACE + rpc_client_mock + gtest_main) + +add_executable(semaphore_test SemaphoreTest.cpp) +target_link_libraries(semaphore_test + gtest_main + semaphore_library + spdlog::spdlog) +add_test(ut_semaphore_test semaphore_test) + +add_executable(message_client_id_setter_test MessageClientIDSetterTest.cpp) +target_link_libraries(message_client_id_setter_test + PRIVATE + gtest_main + rocketmq_library) +add_test(ut_message_client_id_setter_test message_client_id_setter_test) + +add_executable(default_mq_producer_test DefaultMQProducerTest.cpp) +target_link_libraries(default_mq_producer_test + PRIVATE + client_test_interface + rocketmq_library + semaphore_library) +add_test(ut_default_mq_producer_test default_mq_producer_test) + +add_executable(default_mq_push_consumer_test DefaultMQPushConsumerTest.cpp) +target_link_libraries(default_mq_push_consumer_test + rocketmq_library + client_test_interface) + +add_executable(executor_test ExecutorTest.cpp) +target_link_libraries(executor_test + PRIVATE + rocketmq_interface + gtest_main) + +add_executable(message_test MessageTest.cpp) +target_link_libraries(message_test + PRIVATE + rocketmq_library + gtest_main) + +add_executable(process_queue_test ProcessQueueTest.cpp) +target_link_libraries(process_queue_test + gtest_main + rocketmq_library) + +add_executable(ut_credentials_provider CredentialsProviderTest.cpp) +target_link_libraries(ut_credentials_provider + gtest_main + rocketmq_library) + +add_executable(ut_send_callbacks SendCallbacksTest.cpp) +target_link_libraries(ut_send_callbacks + PRIVATE + gtest_main + rocketmq_library) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp new file mode 100644 index 000000000..80c6aab5b --- /dev/null +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -0,0 +1,79 @@ +#include "rocketmq/CredentialsProvider.h" +#include "MixAll.h" +#include "ghc/filesystem.hpp" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class CredentialsProviderTest : public testing::Test { +public: + void SetUp() override { + MixAll::homeDirectory(config_file); + config_file.push_back(ghc::filesystem::path::preferred_separator); + config_file.append(ConfigFileCredentialsProvider::credentialFile()); + ghc::filesystem::path config_file_path(config_file); + if (!ghc::filesystem::exists(config_file_path)) { + delete_config_file_ = true; + writeConfigFile(); + } + } + + void TearDown() override { + ghc::filesystem::path config_file_path(config_file); + if (delete_config_file_ && ghc::filesystem::exists(config_file_path)) { + ghc::filesystem::remove(config_file_path); + } + } + +private: + std::string config_file; + bool delete_config_file_{false}; + + void writeConfigFile() { + ghc::filesystem::path config_file_path(config_file); + if (!ghc::filesystem::exists(config_file_path.parent_path())) { + ghc::filesystem::create_directories(config_file_path.parent_path()); + } + + if (!ghc::filesystem::exists(config_file_path)) { + std::ofstream stream; + stream.open(config_file.c_str(), std::ios::binary | std::ios::out | std::ios::app | std::ios::ate); + google::protobuf::Struct root; + auto fields = root.mutable_fields(); + + google::protobuf::Value access_key_value; + access_key_value.set_string_value("abc"); + fields->insert({"AccessKey", access_key_value}); + + google::protobuf::Value access_secret_value; + access_secret_value.set_string_value("def"); + fields->insert({"AccessSecret", access_secret_value}); + + std::string json; + google::protobuf::util::MessageToJsonString(root, &json); + stream.write(json.c_str(), json.length()); + } + } +}; + +TEST_F(CredentialsProviderTest, testSetUp) { + ConfigFileCredentialsProvider provider; + Credentials credentials = provider.getCredentials(); + ASSERT_FALSE(credentials.accessKey().empty()); + ASSERT_FALSE(credentials.accessSecret().empty()); +} + +TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { + std::string access_key("abc"); + std::string access_secret("def"); + StaticCredentialsProvider credentials_provider(access_key, access_secret); + Credentials&& credentials = credentials_provider.getCredentials(); + ASSERT_EQ(credentials.accessKey(), access_key); + ASSERT_EQ(credentials.accessSecret(), access_secret); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp new file mode 100644 index 000000000..08de01fd2 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -0,0 +1,150 @@ +#include "rocketmq/DefaultMQProducer.h" + +#include "DefaultMQProducerImpl.h" +#include "MQClientTest.h" +#include "rocketmq/MQSelector.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQProducerUnitTest : public MQClientTest { +public: + DefaultMQProducerUnitTest() : MQClientTest() { + // More set-up + rpc_client_broker_ = std::make_shared>(); + + ON_CALL(*rpc_client_broker_, needHeartbeat).WillByDefault(testing::Return(true)); + + ON_CALL(*rpc_client_broker_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); + + ON_CALL(*rpc_client_broker_, asyncHealthCheck) + .WillByDefault(testing::Invoke( + [this](const HealthCheckRequest& request, InvocationContext* invocation_context) { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->onCompletion(true); + })); + + ON_CALL(*rpc_client_broker_, asyncHeartbeat(testing::_, testing::_)) + .WillByDefault(testing::Invoke( + [this](const HeartbeatRequest& request, InvocationContext* invocation_context) { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->onCompletion(true); + })); + + ON_CALL(*rpc_client_broker_, asyncSend) + .WillByDefault(testing::Invoke( + [this](const SendMessageRequest& request, InvocationContext* invocation_context) { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response_.set_message_id(message_id_); + invocation_context->onCompletion(true); + })); + + const char* address_format = "ipv4:10.0.0.{}:10911"; + for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { + std::string&& address = fmt::format(address_format, i); + client_instance_->addRpcClient(address, rpc_client_broker_); + } + } + +protected: + absl::flat_hash_map metadata_; + std::shared_ptr> rpc_client_broker_; + std::string message_id_{"msg_id_0"}; +}; + +TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { + QueryRouteRequest request; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto invocation_context = new InvocationContext(); + auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, + const QueryRouteResponse& response) { + ASSERT_TRUE(status.ok()); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + invocation_context->callback_ = callback; + rpc_client_ns_->asyncQueryRoute(request, invocation_context); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + + completed = false; + SendMessageRequest send_message_request; + auto send_message_invocation_context = new InvocationContext(); + auto send_callback = [&](const grpc::Status& status, const grpc::ClientContext& context, + const SendMessageResponse& response) { + ASSERT_TRUE(response.message_id() == message_id_); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + send_message_invocation_context->callback_ = send_callback; + + rpc_client_broker_->asyncSend(send_message_request, send_message_invocation_context); + + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } +} + +class UnitTestSendCallback : public SendCallback { +public: + UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) + : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) {} + void onSuccess(const SendResult& send_result) override { + absl::MutexLock lk(&mtx_); + msg_id_ = send_result.getMsgId(); + completed_ = true; + cv_.SignalAll(); + } + void onException(const MQException& e) override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + +private: + absl::Mutex& mtx_; + absl::CondVar& cv_; + std::string& msg_id_; + bool& completed_; +}; + +TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { + auto producer = std::make_shared(group_name_); + producer->arn(arn_); + producer->setNameServerList(name_server_list_); + producer->start(); + MQMessage message; + message.setTopic(topic_); + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + std::string msg_id; + auto send_callback = new UnitTestSendCallback(mtx, cv, msg_id, completed); + producer->send(message, send_callback); + while (!completed) { + absl::MutexLock lk(&mtx); + cv.Wait(&mtx); + } + ASSERT_EQ(msg_id, message_id_); +} + +TEST_F(DefaultMQProducerUnitTest, testSendMessage) { + auto producer = std::make_shared(group_name_); + producer->arn(arn_); + producer->setNameServerList(name_server_list_); + producer->start(); + MQMessage message; + SendResult send_result = producer->send(message); + ASSERT_EQ(send_result.getMsgId(), message_id_); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp new file mode 100644 index 000000000..6d05c8c2e --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -0,0 +1,163 @@ +#include "rocketmq/DefaultMQPushConsumer.h" +#include "ClientManager.h" +#include "DefaultMQPushConsumerImpl.h" +#include "MQClientTest.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageListener.h" +#include "spdlog/spdlog.h" +#include + +using namespace testing; + +ROCKETMQ_NAMESPACE_BEGIN + +std::mutex mtx_; +std::condition_variable cv; +int max_consumer_message_size = 50; +std::atomic_int message_count_(max_consumer_message_size); +std::atomic_int message_index(0); + +class DefaultMQPushConsumerUnitTest : public MQClientTest { +public: + DefaultMQPushConsumerUnitTest() { + rpc_client_for_broker_ = std::make_shared>(); + + ON_CALL(*rpc_client_for_broker_, needHeartbeat).WillByDefault(testing::Return(true)); + + ON_CALL(*rpc_client_for_broker_, ok()).WillByDefault(Invoke([this]() { + std::cout << "Check ok()" << std::endl; + return client_ok_; + })); + + ON_CALL(*rpc_client_for_broker_, asyncQueryOffset(_, _)) + .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockQueryOffset, this, std::placeholders::_1, + std::placeholders::_2))); + + ON_CALL(*rpc_client_for_broker_, asyncPull(_, _)) + .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockAsyncPull, this, std::placeholders::_1, + std::placeholders::_2))); + + ON_CALL(*rpc_client_for_broker_, asyncHeartbeat(_, _)) + .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockHeartbeat, this, std::placeholders::_1, + std::placeholders::_2))); + + ON_CALL(*rpc_client_for_broker_, asyncQueryAssignment(testing::_, testing::_)) + .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockQueryAssignment, this, + std::placeholders::_1, std::placeholders::_2))); + + const char* address_format = "ipv4:10.0.0.{}:10911"; + for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { + std::string&& address = fmt::format(address_format, i); + client_instance_->addRpcClient(address, rpc_client_for_broker_); + } + } + + void mockHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) const { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->onCompletion(true); + } + + void mockQueryOffset(const QueryOffsetRequest& request, + InvocationContext* invocation_context) const { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response_.set_offset(max_offset_); + + invocation_context->onCompletion(true); + } + + void mockAsyncPull(const PullMessageRequest& request, + InvocationContext* invocation_context) const { + invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response_.set_next_offset(max_offset_); + for (int i = 0; i < pull_batch_size_; i++) { + auto message = new rmq::Message; + message->mutable_topic()->set_arn(arn_); + message->mutable_topic()->set_name(topic_); + message->mutable_system_attribute()->set_message_id(std::to_string(++message_index_)); + message->set_body(body_); + message->mutable_system_attribute()->set_body_encoding(rmq::Encoding::IDENTITY); + message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); + std::string checksum; + MixAll::md5(body_, checksum); + message->mutable_system_attribute()->mutable_body_digest()->set_checksum(checksum); + invocation_context->response_.mutable_messages()->AddAllocated(message); + } + invocation_context->onCompletion(true); + } + + void mockQueryAssignment(const QueryAssignmentRequest& request, + InvocationContext* invocation_context) { + for (int i = 0; i < 16; ++i) { + auto load_assignment = new rmq::Assignment; + load_assignment->set_mode(rmq::ConsumeMessageType::POP); + load_assignment->mutable_partition()->set_id(i % 8); + load_assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); + load_assignment->mutable_partition()->mutable_topic()->set_name(request.topic().name()); + load_assignment->mutable_partition()->mutable_topic()->set_arn(request.topic().arn()); + std::string broker_name{"broker-"}; + broker_name.push_back('a' + i / 8); + load_assignment->mutable_partition()->mutable_broker()->set_name(broker_name); + load_assignment->mutable_partition()->mutable_broker()->set_id(0); + auto endpoints = load_assignment->mutable_partition()->mutable_broker()->mutable_endpoints(); + endpoints->set_scheme(rmq::AddressScheme::IPv4); + auto addresses = endpoints->mutable_addresses(); + auto address = new rmq::Address; + address->set_host(fmt::format("10.0.0.{}", i / 8)); + address->set_port(10911); + addresses->AddAllocated(address); + invocation_context->response_.mutable_assignments()->AddAllocated(load_assignment); + } + invocation_context->onCompletion(true); + } + +protected: + std::shared_ptr> rpc_client_for_broker_; + const int64_t max_offset_{100}; + std::string name_server_address_{"127.0.0.1:9876"}; + int32_t pull_batch_size_{32}; + mutable int64_t message_index_{0}; + std::string body_{"Sample message body"}; +}; + +std::atomic_bool completed_{false}; +std::mutex completion_mtx_; +std::condition_variable completion_cv_; + +class MessageListenerUnitTests : public ROCKETMQ_NAMESPACE::MessageListenerConcurrently { +public: + ConsumeStatus consumeMessage(const std::vector& msgs) override { + if (!msgs.empty()) { + std::unique_lock lk(completion_mtx_); + bool expected = false; + if (completed_.compare_exchange_strong(expected, true)) { + completion_cv_.notify_all(); + SPDLOG_INFO("Notify to stop unit test execution."); + } + } + for (const auto& msg : msgs) { + SPDLOG_INFO("Received a message[Topic={}, MessageId={}]", msg.getTopic(), msg.getMsgId()); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + return ConsumeStatus::CONSUME_SUCCESS; + } +}; + +TEST_F(DefaultMQPushConsumerUnitTest, testBroadcasting) { + spdlog::set_level(spdlog::level::debug); + auto push_consumer = std::make_shared(group_name_); + push_consumer->setMessageModel(MessageModel::BROADCASTING); + push_consumer->arn(arn_); + push_consumer->setNameServerList(name_server_list_); + push_consumer->subscribe(topic_, "*"); + auto listener = new MessageListenerUnitTests; + push_consumer->registerMessageListener(listener); + push_consumer->start(); + { + std::unique_lock lk(completion_mtx_); + completion_cv_.wait(lk, [&]() { return completed_.load(); }); + } + push_consumer->shutdown(); + delete listener; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp new file mode 100644 index 000000000..85da2b82f --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -0,0 +1,135 @@ +#include "rocketmq/State.h" +#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include + +namespace rocketmq { +class ExecutorImpl { +public: + explicit ExecutorImpl(unsigned int core) : state_(State::CREATED), core_(core) { + if (core_ <= 0) { + core_ = std::thread::hardware_concurrency(); + } + } + + virtual ~ExecutorImpl() { + switch (state_.load(std::memory_order_relaxed)) { + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + for (auto& worker : workers_) { + if (worker.joinable()) { + worker.join(); + } + } + break; + } + } + + void submit(const std::function& task) { + if (State::STOPPED == state_.load(std::memory_order_relaxed)) { + return; + } + + { + std::unique_lock lock(task_mtx_); + tasks_.push_back(task); + } + cv_.notify_one(); + } + + void start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING)) { + for (unsigned int i = 0; i < core_; i++) { + workers_.emplace_back(std::bind(&ExecutorImpl::loop, this)); + } + state_.store(State::STARTED); + } + } + + void stop() { + state_.store(State::STOPPED); + for (auto& worker : workers_) { + if (worker.joinable()) { + worker.join(); + } + } + } + +private: + void loop() { + while (state_.load(std::memory_order_relaxed) != State::STOPPED) { + std::function func; + { + std::unique_lock lk(task_mtx_); + if (!tasks_.empty()) { + func = tasks_.back(); + } + } + + if (func) { + func(); + } else { + std::unique_lock lk(task_mtx_); + cv_.wait_for(lk, std::chrono::seconds(3), + [&]() { return state_.load(std::memory_order_relaxed) == State::STOPPED || !tasks_.empty(); }); + } + } + + std::stringstream ss; + ss << "ThreadId=" << std::this_thread::get_id() << " quit."; + printf("%s\n", ss.str().c_str()); + } + + std::atomic state_; + std::vector> tasks_; + std::mutex task_mtx_; + std::condition_variable cv_; + unsigned int core_; + std::vector workers_; +}; + +class ExecutorTest : public ::testing::Test { +public: + void SetUp() override { executor_.start(); } + + void TearDown() override { executor_.stop(); } + +protected: + ExecutorImpl executor_{0}; +}; + +TEST_F(ExecutorTest, testBasic) {} + +std::mutex submit_mtx_; +std::condition_variable submit_cv_; +std::atomic_bool submit_status(false); + +void f(int n) { + std::this_thread::sleep_for(std::chrono::milliseconds(n)); + submit_status.store(true, std::memory_order_relaxed); + submit_cv_.notify_all(); +} + +TEST_F(ExecutorTest, testSubmit) { + auto task = std::bind(f, 100); + executor_.submit(task); + { + std::unique_lock lock(submit_mtx_); + submit_cv_.wait(lock, [] { return submit_status.load(std::memory_order_relaxed); }); + } + EXPECT_EQ(true, submit_status.load()); +} + +} // namespace rocketmq diff --git a/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp b/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp new file mode 100644 index 000000000..c0e27f6a4 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp @@ -0,0 +1,25 @@ +#include "MessageClientIDSetter.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(MessageClientIDSetterTest, testMessageClientIDSetter) { + std::set msg_id_set; + int64_t total = 500000; + int64_t count = 0; + + while (total--) { + std::string msgId = MessageClientIDSetter::createUniqId(); + if (0 == total % 10000) { + SPDLOG_INFO("MsgId: {}", msgId); + } + msg_id_set.insert(msgId); + ++count; + } + EXPECT_EQ(count, msg_id_set.size()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp new file mode 100644 index 000000000..60b09091f --- /dev/null +++ b/src/test/cpp/ut/rocketmq/MessageTest.cpp @@ -0,0 +1,10 @@ +#include +#include +#include +#include "rocketmq/MQMessageExt.h" + +TEST(MessageTest, testExpire) { + auto expire_time_point = std::chrono::steady_clock::now() + std::chrono::milliseconds(300); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + EXPECT_TRUE(std::chrono::steady_clock::now() < expire_time_point); +} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp new file mode 100644 index 000000000..75ca49c4e --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -0,0 +1,17 @@ +#include "ProcessQueue.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProcessQueueTest : public testing::Test {}; + +TEST_F(ProcessQueueTest, testRequestId) { + std::weak_ptr owner; + ProcessQueue process_queue(MQMessageQueue(), FilterExpression("*"), ConsumeMessageType::POP, 1, owner, nullptr); + std::string&& request_id = process_queue.requestId(); + std::vector bin; + EXPECT_TRUE(MixAll::hexToBinary(request_id, bin)); + EXPECT_EQ(request_id, MixAll::hex(bin.data(), bin.size())); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/Semaphore.cpp b/src/test/cpp/ut/rocketmq/Semaphore.cpp new file mode 100644 index 000000000..b2f5e42fc --- /dev/null +++ b/src/test/cpp/ut/rocketmq/Semaphore.cpp @@ -0,0 +1,25 @@ +#include "include/Semaphore.h" + +using namespace rocketmq; + +void Semaphore::acquire() { + absl::MutexLock lock(&mtx_); + if (value_ > 0) { + value_--; + return; + } else { + while (value_ <= 0) { + cv_.Wait(&mtx_); + } + value_--; + return; + } +} + +void Semaphore::release() { + { + absl::MutexLock lock(&mtx_); + value_++; + } + cv_.Signal(); +} diff --git a/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp b/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp new file mode 100644 index 000000000..1fdab567b --- /dev/null +++ b/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp @@ -0,0 +1,27 @@ +#include "Semaphore.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" +#include + +using namespace rocketmq; + +TEST(SempahoreTest, testSemaphore) { + Semaphore limiter(0); + std::thread t([&] { + auto start = std::chrono::system_clock::now(); + spdlog::info("Start to acquire"); + limiter.acquire(); + spdlog::info("Acquired"); + auto duration = std::chrono::system_clock::now() - start; + EXPECT_TRUE(std::chrono::duration_cast(duration).count() >= 1); + }); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + spdlog::info("Releasing token..."); + limiter.release(); + spdlog::info("Token released"); + + if (t.joinable()) { + t.join(); + } +} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp new file mode 100644 index 000000000..cc40de843 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp @@ -0,0 +1,28 @@ +#include "gtest/gtest.h" +#include "SendCallbacks.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class SendCallbacksTest : public testing::Test { +}; + +TEST_F(SendCallbacksTest, testAwait) { + std::string msg_id("Msg-0"); + AwaitSendCallback callback; + std::thread waker([&]() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + SendResult send_result; + send_result.setMsgId(msg_id); + callback.onSuccess(send_result); + }); + + callback.await(); + EXPECT_TRUE(callback); + EXPECT_EQ(callback.sendResult().getMsgId(), msg_id); + if (waker.joinable()) { + waker.join(); + } +} + + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h new file mode 100644 index 000000000..3df2b8619 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -0,0 +1,76 @@ +#pragma once + +#include "RpcClientMock.h" +#include "ClientManager.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MQClientTest : public testing::Test { +public: + + MQClientTest() { + name_server_list_.emplace_back(name_server_address_); + client_instance_ = std::make_shared(arn_); + rpc_client_ns_ = std::make_shared>(); + + ON_CALL(*rpc_client_ns_, needHeartbeat).WillByDefault(testing::Return(false)); + + ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); + + ON_CALL(*rpc_client_ns_, asyncQueryRoute(testing::_, testing::_)) + .WillByDefault(testing::Invoke( + std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); + client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); + ClientManager::getInstance().addClientInstance(arn_, client_instance_); + } + +protected: + grpc::internal::GrpcLibraryInitializer initializer_; + std::shared_ptr client_instance_; + std::shared_ptr> rpc_client_ns_; + const int16_t port_{10911}; + const int32_t partition_num_{24}; + const int32_t avg_partition_per_host_{8}; + std::string name_server_address_{"ipv4:127.0.0.1:9876"}; + std::string group_name_{"CID_Test"}; + std::string topic_{"Topic_Test"}; + std::string arn_{"arn:mq::test"}; + google::rpc::Code ok_{google::rpc::Code::OK}; + bool client_ok_{true}; + std::vector name_server_list_; + +private: + void mockQueryRoute(const QueryRouteRequest& request, + InvocationContext* invocation_context) const { + invocation_context->response_.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + auto partitions = invocation_context->response_.mutable_partitions(); + for (int i = 0; i < partition_num_; i++) { + auto partition = new rmq::Partition(); + partition->set_id(i % avg_partition_per_host_); + partition->set_permission(rmq::Permission::READ_WRITE); + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_topic()->set_arn(request.topic().arn()); + std::string broker_name{"broker-"}; + broker_name.push_back('a' + i / avg_partition_per_host_); + partition->mutable_broker()->set_name(broker_name); + + auto endpoint = partition->mutable_broker()->mutable_endpoints(); + endpoint->set_scheme(rmq::AddressScheme::IPv4); + auto addresses = endpoint->mutable_addresses(); + auto address = new rmq::Address; + address->set_host(fmt::format("10.0.0.{}", i / avg_partition_per_host_)); + address->set_port(port_); + addresses->AddAllocated(address); + partitions->AddAllocated(partition); + } + // Mock invoke callback. + invocation_context->onCompletion(true); + } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/Semaphore.h b/src/test/cpp/ut/rocketmq/include/Semaphore.h new file mode 100644 index 000000000..ab24e554b --- /dev/null +++ b/src/test/cpp/ut/rocketmq/include/Semaphore.h @@ -0,0 +1,20 @@ +#pragma once + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +namespace rocketmq { +class Semaphore { +public: + Semaphore(int value) : value_(value) {} + + void acquire(); + + void release(); + +private: + int value_; + absl::Mutex mtx_; + absl::CondVar cv_; +}; +} // namespace rocketmq diff --git a/third_party/BUILD b/third_party/BUILD new file mode 100644 index 000000000..e69de29bb diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 000000000..d5704891e --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(date) +add_subdirectory(thread_pool) +add_subdirectory(crc) \ No newline at end of file diff --git a/third_party/ThreadPool.BUILD b/third_party/ThreadPool.BUILD new file mode 100644 index 000000000..451d193f0 --- /dev/null +++ b/third_party/ThreadPool.BUILD @@ -0,0 +1,8 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "ThreadPool", + hdrs = [ + "ThreadPool.h", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/third_party/cpp_httplib.BUILD b/third_party/cpp_httplib.BUILD new file mode 100644 index 000000000..2fdd5db35 --- /dev/null +++ b/third_party/cpp_httplib.BUILD @@ -0,0 +1,6 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "httplib", + hdrs = ["httplib.h"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/third_party/crc/BUILD.bazel b/third_party/crc/BUILD.bazel new file mode 100644 index 000000000..4eba5cb86 --- /dev/null +++ b/third_party/crc/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "crc_interface", + hdrs = [ + "include/CRC.h" + ], + strip_include_prefix = "//third_party/crc/include", + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/third_party/crc/CMakeLists.txt b/third_party/crc/CMakeLists.txt new file mode 100644 index 000000000..3f97aebd4 --- /dev/null +++ b/third_party/crc/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(crc_library INTERFACE) +target_include_directories(crc_library + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/third_party/crc/include/CRC.h b/third_party/crc/include/CRC.h new file mode 100644 index 000000000..e20db354a --- /dev/null +++ b/third_party/crc/include/CRC.h @@ -0,0 +1,2066 @@ +/** + @file CRC.h + @author Daniel Bahr + @version 1.1.0.0 + @copyright + @parblock + CRC++ + Copyright (c) 2021, Daniel Bahr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of CRC++ nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + @endparblock +*/ + +/* + CRC++ can be configured by setting various #defines before #including this header file: + + #define crcpp_uint8 - Specifies the type used to store CRCs that have a width of 8 bits or less. + This type is not used in CRC calculations. Defaults to ::std::uint8_t. + #define crcpp_uint16 - Specifies the type used to store CRCs that have a width between 9 and 16 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint16_t. + #define crcpp_uint32 - Specifies the type used to store CRCs that have a width between 17 and 32 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint32_t. + #define crcpp_uint64 - Specifies the type used to store CRCs that have a width between 33 and 64 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint64_t. + #define crcpp_size - This type is used for loop iteration and function signatures only. Defaults to ::std::size_t. + #define CRCPP_USE_NAMESPACE - Define to place all CRC++ code within the ::CRCPP namespace. + #define CRCPP_BRANCHLESS - Define to enable a branchless CRC implementation. The branchless implementation uses a single integer + multiplication in the bit-by-bit calculation instead of a small conditional. The branchless implementation + may be faster on processor architectures which support single-instruction integer multiplication. + #define CRCPP_USE_CPP11 - Define to enables C++11 features (move semantics, constexpr, static_assert, etc.). + #define CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - Define to include definitions for little-used CRCs. +*/ + +#ifndef CRCPP_CRC_H_ +#define CRCPP_CRC_H_ + +#include // Includes CHAR_BIT +#ifdef CRCPP_USE_CPP11 +#include // Includes ::std::size_t +#include // Includes ::std::uint8_t, ::std::uint16_t, ::std::uint32_t, ::std::uint64_t +#else +#include // Includes size_t +#include // Includes uint8_t, uint16_t, uint32_t, uint64_t +#endif +#include // Includes ::std::numeric_limits +#include // Includes ::std::move + +#ifndef crcpp_uint8 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint8 ::std::uint8_t +# else + /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint8 uint8_t +# endif +#endif + +#ifndef crcpp_uint16 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint16 ::std::uint16_t +# else + /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint16 uint16_t +# endif +#endif + +#ifndef crcpp_uint32 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint32 ::std::uint32_t +# else + /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint32 uint32_t +# endif +#endif + +#ifndef crcpp_uint64 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint64 ::std::uint64_t +# else + /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint64 uint64_t +# endif +#endif + +#ifndef crcpp_size +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned size definition, used for specifying data sizes. +# define crcpp_size ::std::size_t +# else + /// @brief Unsigned size definition, used for specifying data sizes. +# define crcpp_size size_t +# endif +#endif + +#ifdef CRCPP_USE_CPP11 + /// @brief Compile-time expression definition. +# define crcpp_constexpr constexpr +#else + /// @brief Compile-time expression definition. +# define crcpp_constexpr const +#endif + +#ifdef CRCPP_USE_NAMESPACE +namespace CRCPP +{ +#endif + +/** + @brief Static class for computing CRCs. + @note This class supports computation of full and multi-part CRCs, using a bit-by-bit algorithm or a + byte-by-byte lookup table. The CRCs are calculated using as many optimizations as is reasonable. + If compiling with C++11, the constexpr keyword is used liberally so that many calculations are + performed at compile-time instead of at runtime. +*/ +class CRC +{ +public: + // Forward declaration + template + struct Table; + + /** + @brief CRC parameters. + */ + template + struct Parameters + { + CRCType polynomial; ///< CRC polynomial + CRCType initialValue; ///< Initial CRC value + CRCType finalXOR; ///< Value to XOR with the final CRC + bool reflectInput; ///< true to reflect all input bytes + bool reflectOutput; ///< true to reflect the output CRC (reflection occurs before the final XOR) + + Table MakeTable() const; + }; + + /** + @brief CRC lookup table. After construction, the CRC parameters are fixed. + @note A CRC table can be used for multiple CRC calculations. + */ + template + struct Table + { + // Constructors are intentionally NOT marked explicit. + Table(const Parameters & parameters); + +#ifdef CRCPP_USE_CPP11 + Table(Parameters && parameters); +#endif + + const Parameters & GetParameters() const; + + const CRCType * GetTable() const; + + CRCType operator[](unsigned char index) const; + + private: + void InitTable(); + + Parameters parameters; ///< CRC parameters used to construct the table + CRCType table[1 << CHAR_BIT]; ///< CRC lookup table + }; + + // The number of bits in CRCType must be at least as large as CRCWidth. + // CRCType must be an unsigned integer type or a custom type with operator overloads. + template + static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); + + // Common CRCs up to 64 bits. + // Note: Check values are the computed CRCs when given an ASCII input of "123456789" (without null terminator) +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters< crcpp_uint8, 4> & CRC_4_ITU(); + static const Parameters< crcpp_uint8, 5> & CRC_5_EPC(); + static const Parameters< crcpp_uint8, 5> & CRC_5_ITU(); + static const Parameters< crcpp_uint8, 5> & CRC_5_USB(); + static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000A(); + static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000B(); + static const Parameters< crcpp_uint8, 6> & CRC_6_ITU(); + static const Parameters< crcpp_uint8, 6> & CRC_6_NR(); + static const Parameters< crcpp_uint8, 7> & CRC_7(); +#endif + static const Parameters< crcpp_uint8, 8> & CRC_8(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters< crcpp_uint8, 8> & CRC_8_EBU(); + static const Parameters< crcpp_uint8, 8> & CRC_8_MAXIM(); + static const Parameters< crcpp_uint8, 8> & CRC_8_WCDMA(); + static const Parameters< crcpp_uint8, 8> & CRC_8_LTE(); + static const Parameters & CRC_10(); + static const Parameters & CRC_10_CDMA2000(); + static const Parameters & CRC_11(); + static const Parameters & CRC_11_NR(); + static const Parameters & CRC_12_CDMA2000(); + static const Parameters & CRC_12_DECT(); + static const Parameters & CRC_12_UMTS(); + static const Parameters & CRC_13_BBC(); + static const Parameters & CRC_15(); + static const Parameters & CRC_15_MPT1327(); +#endif + static const Parameters & CRC_16_ARC(); + static const Parameters & CRC_16_BUYPASS(); + static const Parameters & CRC_16_CCITTFALSE(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_16_CDMA2000(); + static const Parameters & CRC_16_CMS(); + static const Parameters & CRC_16_DECTR(); + static const Parameters & CRC_16_DECTX(); + static const Parameters & CRC_16_DNP(); +#endif + static const Parameters & CRC_16_GENIBUS(); + static const Parameters & CRC_16_KERMIT(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_16_MAXIM(); + static const Parameters & CRC_16_MODBUS(); + static const Parameters & CRC_16_T10DIF(); + static const Parameters & CRC_16_USB(); +#endif + static const Parameters & CRC_16_X25(); + static const Parameters & CRC_16_XMODEM(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_17_CAN(); + static const Parameters & CRC_21_CAN(); + static const Parameters & CRC_24(); + static const Parameters & CRC_24_FLEXRAYA(); + static const Parameters & CRC_24_FLEXRAYB(); + static const Parameters & CRC_24_LTEA(); + static const Parameters & CRC_24_LTEB(); + static const Parameters & CRC_24_NRC(); + static const Parameters & CRC_30(); +#endif + static const Parameters & CRC_32(); + static const Parameters & CRC_32_BZIP2(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_32_C(); +#endif + static const Parameters & CRC_32_MPEG2(); + static const Parameters & CRC_32_POSIX(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_32_Q(); + static const Parameters & CRC_40_GSM(); + static const Parameters & CRC_64(); +#endif + +#ifdef CRCPP_USE_CPP11 + CRC() = delete; + CRC(const CRC & other) = delete; + CRC & operator=(const CRC & other) = delete; + CRC(CRC && other) = delete; + CRC & operator=(CRC && other) = delete; +#endif + +private: +#ifndef CRCPP_USE_CPP11 + CRC(); + CRC(const CRC & other); + CRC & operator=(const CRC & other); +#endif + + template + static IntegerType Reflect(IntegerType value, crcpp_uint16 numBits); + + template + static CRCType Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); + + template + static CRCType UndoFinalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); + + template + static CRCType CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder); + + template + static CRCType CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder); + + template + static CRCType CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder); +}; + +/** + @brief Returns a CRC lookup table construct using these CRC parameters. + @note This function primarily exists to allow use of the auto keyword instead of instantiating + a table directly, since template parameters are not inferred in constructors. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC lookup table +*/ +template +inline CRC::Table CRC::Parameters::MakeTable() const +{ + // This should take advantage of RVO and optimize out the copy. + return CRC::Table(*this); +} + +/** + @brief Constructs a CRC table from a set of CRC parameters + @param[in] params CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline CRC::Table::Table(const Parameters & params) : + parameters(params) +{ + InitTable(); +} + +#ifdef CRCPP_USE_CPP11 +/** + @brief Constructs a CRC table from a set of CRC parameters + @param[in] params CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline CRC::Table::Table(Parameters && params) : + parameters(::std::move(params)) +{ + InitTable(); +} +#endif + +/** + @brief Gets the CRC parameters used to construct the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC parameters +*/ +template +inline const CRC::Parameters & CRC::Table::GetParameters() const +{ + return parameters; +} + +/** + @brief Gets the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC table +*/ +template +inline const CRCType * CRC::Table::GetTable() const +{ + return table; +} + +/** + @brief Gets an entry in the CRC table + @param[in] index Index into the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC table entry +*/ +template +inline CRCType CRC::Table::operator[](unsigned char index) const +{ + return table[index]; +} + +/** + @brief Initializes a CRC table. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline void CRC::Table::InitTable() +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK((CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1))); + + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType crc; + unsigned char byte = 0; + + // Loop over each dividend (each possible number storable in an unsigned char) + do + { + crc = CRC::CalculateRemainder(&byte, sizeof(byte), parameters, CRCType(0)); + + // This mask might not be necessary; all unit tests pass with this line commented out, + // but that might just be a coincidence based on the CRC parameters used for testing. + // In any case, this is harmless to leave in and only adds a single machine instruction per loop iteration. + crc &= BIT_MASK; + + if (!parameters.reflectInput && CRCWidth < CHAR_BIT) + { + // Undo the special operation at the end of the CalculateRemainder() + // function for non-reflected CRCs < CHAR_BIT. + crc = static_cast(crc << SHIFT); + } + + table[byte] = crc; + } + while (++byte); +} + +/** + @brief Computes a CRC. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters) +{ + CRCType remainder = CalculateRemainder(data, size, parameters, parameters.initialValue); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} +/** + @brief Appends additional data to a previous CRC calculation. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) +{ + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + remainder = CalculateRemainder(data, size, parameters, remainder); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC via a lookup table. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = CalculateRemainder(data, size, lookupTable, parameters.initialValue); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Appends additional data to a previous CRC calculation using a lookup table. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + remainder = CalculateRemainder(data, size, lookupTable, remainder); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] parameters CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters) +{ + CRCType remainder = parameters.initialValue; + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, remainder); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} +/** + @brief Appends additional data to a previous CRC calculation. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] parameters CRC parameters + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) +{ + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, parameters.initialValue); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC via a lookup table. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] lookupTable CRC lookup table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = parameters.initialValue; + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, remainder); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Appends additional data to a previous CRC calculation using a lookup table. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] lookupTable CRC lookup table + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, parameters.initialValue); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits > 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Reflects (i.e. reverses the bits within) an integer value. + @param[in] value Value to reflect + @param[in] numBits Number of bits in the integer which will be reflected + @tparam IntegerType Integer type of the value being reflected + @return Reflected value +*/ +template +inline IntegerType CRC::Reflect(IntegerType value, crcpp_uint16 numBits) +{ + IntegerType reversedValue(0); + + for (crcpp_uint16 i = 0; i < numBits; ++i) + { + reversedValue = static_cast((reversedValue << 1) | (value & 1)); + value = static_cast(value >> 1); + } + + return reversedValue; +} + +/** + @brief Computes the final reflection and XOR of a CRC remainder. + @param[in] remainder CRC remainder to reflect and XOR + @param[in] finalXOR Final value to XOR with the remainder + @param[in] reflectOutput true to reflect each byte of the remainder before the XOR + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return Final CRC +*/ +template +inline CRCType CRC::Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput) +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); + + if (reflectOutput) + { + remainder = Reflect(remainder, CRCWidth); + } + + return (remainder ^ finalXOR) & BIT_MASK; +} + +/** + @brief Undoes the process of computing the final reflection and XOR of a CRC remainder. + @note This function allows for computation of multi-part CRCs + @note Calling UndoFinalize() followed by Finalize() (or vice versa) will always return the original remainder value: + + CRCType x = ...; + CRCType y = Finalize(x, finalXOR, reflectOutput); + CRCType z = UndoFinalize(y, finalXOR, reflectOutput); + assert(x == z); + + @param[in] crc Reflected and XORed CRC + @param[in] finalXOR Final value XORed with the remainder + @param[in] reflectOutput true if the remainder is to be reflected + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return Un-finalized CRC remainder +*/ +template +inline CRCType CRC::UndoFinalize(CRCType crc, CRCType finalXOR, bool reflectOutput) +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); + + crc = (crc & BIT_MASK) ^ finalXOR; + + if (reflectOutput) + { + crc = Reflect(crc, CRCWidth); + } + + return crc; +} + +/** + @brief Computes a CRC remainder. + @param[in] data Data over which the remainder will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC remainder +*/ +template +inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder) +{ +#ifdef CRCPP_USE_CPP11 + // This static_assert is put here because this function will always be compiled in no matter what + // the template parameters are and whether or not a table lookup or bit-by-bit algorithm is used. + static_assert(::std::numeric_limits::digits >= CRCWidth, "CRCType is too small to contain a CRC of width CRCWidth."); +#else + // Catching this compile-time error is very important. Sadly, the compiler error will be very cryptic, but it's + // better than nothing. + enum { static_assert_failed_CRCType_is_too_small_to_contain_a_CRC_of_width_CRCWidth = 1 / (::std::numeric_limits::digits >= CRCWidth ? 1 : 0) }; +#endif + + const unsigned char * current = reinterpret_cast(data); + + // Slightly different implementations based on the parameters. The current implementations try to eliminate as much + // computation from the inner loop (looping over each bit) as possible. + if (parameters.reflectInput) + { + CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); + while (size--) + { + remainder = static_cast(remainder ^ *current++); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & 1) + // remainder = (remainder >> 1) ^ polynomial; + // else + // remainder >>= 1; + remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); +#else + remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); +#endif + } + } + } + else if (CRCWidth >= CHAR_BIT) + { + static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + while (size--) + { + remainder = static_cast(remainder ^ (static_cast(*current++) << SHIFT)); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CRC_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ parameters.polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); +#else + remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); +#endif + } + } + } + else + { + static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType polynomial = static_cast(parameters.polynomial << SHIFT); + remainder = static_cast(remainder << SHIFT); + + while (size--) + { + remainder = static_cast(remainder ^ *current++); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); +#else + remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); +#endif + } + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +/** + @brief Computes a CRC remainder using lookup table. + @param[in] data Data over which the remainder will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC remainder +*/ +template +inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder) +{ + const unsigned char * current = reinterpret_cast(data); + + if (lookupTable.GetParameters().reflectInput) + { + while (size--) + { +#if defined(WIN32) || defined(_WIN32) || defined(WINCE) + // Disable warning about data loss when doing (remainder >> CHAR_BIT) when + // remainder is one byte long. The algorithm is still correct in this case, + // though it's possible that one additional machine instruction will be executed. +# pragma warning (push) +# pragma warning (disable : 4333) +#endif + remainder = static_cast((remainder >> CHAR_BIT) ^ lookupTable[static_cast(remainder ^ *current++)]); +#if defined(WIN32) || defined(_WIN32) || defined(WINCE) +# pragma warning (pop) +#endif + } + } + else if (CRCWidth >= CHAR_BIT) + { + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + while (size--) + { + remainder = static_cast((remainder << CHAR_BIT) ^ lookupTable[static_cast((remainder >> SHIFT) ^ *current++)]); + } + } + else + { + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + remainder = static_cast(remainder << SHIFT); + + while (size--) + { + // Note: no need to mask here since remainder is guaranteed to fit in a single byte. + remainder = lookupTable[static_cast(remainder ^ *current++)]; + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +template +inline CRCType CRC::CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder) +{ + // Slightly different implementations based on the parameters. The current implementations try to eliminate as much + // computation from the inner loop (looping over each bit) as possible. + if (parameters.reflectInput) + { + CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); + remainder = static_cast(remainder ^ byte); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & 1) + // remainder = (remainder >> 1) ^ polynomial; + // else + // remainder >>= 1; + remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); +#else + remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); +#endif + } + } + else if (CRCWidth >= CHAR_BIT) + { + static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + remainder = static_cast(remainder ^ (static_cast(byte) << SHIFT)); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CRC_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ parameters.polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); +#else + remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); +#endif + } + } + else + { + static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType polynomial = static_cast(parameters.polynomial << SHIFT); + remainder = static_cast((remainder << SHIFT) ^ byte); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); +#else + remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); +#endif + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-4 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-4 ITU has the following parameters and check value: + - polynomial = 0x3 + - initial value = 0x0 + - final XOR = 0x0 + - reflect input = true + - reflect output = true + - check value = 0x7 + @return CRC-4 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_4_ITU() +{ + static const Parameters parameters = { 0x3, 0x0, 0x0, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 EPC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 EPC has the following parameters and check value: + - polynomial = 0x09 + - initial value = 0x09 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x00 + @return CRC-5 EPC parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_EPC() +{ + static const Parameters parameters = { 0x09, 0x09, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 ITU has the following parameters and check value: + - polynomial = 0x15 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x07 + @return CRC-5 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_ITU() +{ + static const Parameters parameters = { 0x15, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 USB. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 USB has the following parameters and check value: + - polynomial = 0x05 + - initial value = 0x1F + - final XOR = 0x1F + - reflect input = true + - reflect output = true + - check value = 0x19 + @return CRC-5 USB parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_USB() +{ + static const Parameters parameters = { 0x05, 0x1F, 0x1F, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 CDMA2000-A. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 CDMA2000-A has the following parameters and check value: + - polynomial = 0x27 + - initial value = 0x3F + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x0D + @return CRC-6 CDMA2000-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_CDMA2000A() +{ + static const Parameters parameters = { 0x27, 0x3F, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 CDMA2000-B. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 CDMA2000-A has the following parameters and check value: + - polynomial = 0x07 + - initial value = 0x3F + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x3B + @return CRC-6 CDMA2000-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_CDMA2000B() +{ + static const Parameters parameters = { 0x07, 0x3F, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 ITU has the following parameters and check value: + - polynomial = 0x03 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x06 + @return CRC-6 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_ITU() +{ + static const Parameters parameters = { 0x03, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 NR. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-6 NR has the following parameters and check value: + - polynomial = 0x21 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x15 + @return CRC-6 NR parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_NR() +{ + static const Parameters parameters = { 0x21, 0x00, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-7 JEDEC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-7 JEDEC has the following parameters and check value: + - polynomial = 0x09 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x75 + @return CRC-7 JEDEC parameters +*/ +inline const CRC::Parameters & CRC::CRC_7() +{ + static const Parameters parameters = { 0x09, 0x00, 0x00, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-8 SMBus. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 SMBus has the following parameters and check value: + - polynomial = 0x07 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0xF4 + @return CRC-8 SMBus parameters +*/ +inline const CRC::Parameters & CRC::CRC_8() +{ + static const Parameters parameters = { 0x07, 0x00, 0x00, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-8 EBU (aka CRC-8 AES). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 EBU has the following parameters and check value: + - polynomial = 0x1D + - initial value = 0xFF + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x97 + @return CRC-8 EBU parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_EBU() +{ + static const Parameters parameters = { 0x1D, 0xFF, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 MAXIM (aka CRC-8 DOW-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 MAXIM has the following parameters and check value: + - polynomial = 0x31 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0xA1 + @return CRC-8 MAXIM parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_MAXIM() +{ + static const Parameters parameters = { 0x31, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 WCDMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 WCDMA has the following parameters and check value: + - polynomial = 0x9B + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x25 + @return CRC-8 WCDMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_WCDMA() +{ + static const Parameters parameters = { 0x9B, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 LTE. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 LTE has the following parameters and check value: + - polynomial = 0x9B + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0xEA + @return CRC-8 LTE parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_LTE() +{ + static const Parameters parameters = { 0x9B, 0x00, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-10 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-10 ITU has the following parameters and check value: + - polynomial = 0x233 + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x199 + @return CRC-10 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_10() +{ + static const Parameters parameters = { 0x233, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-10 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-10 CDMA2000 has the following parameters and check value: + - polynomial = 0x3D9 + - initial value = 0x3FF + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x233 + @return CRC-10 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_10_CDMA2000() +{ + static const Parameters parameters = { 0x3D9, 0x3FF, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-11 FlexRay. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-11 FlexRay has the following parameters and check value: + - polynomial = 0x385 + - initial value = 0x01A + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x5A3 + @return CRC-11 FlexRay parameters +*/ +inline const CRC::Parameters & CRC::CRC_11() +{ + static const Parameters parameters = { 0x385, 0x01A, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-11 NR. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-11 NR has the following parameters and check value: + - polynomial = 0x621 + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x5CA + @return CRC-11 NR parameters +*/ +inline const CRC::Parameters & CRC::CRC_11_NR() +{ + static const Parameters parameters = { 0x621, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 CDMA2000 has the following parameters and check value: + - polynomial = 0xF13 + - initial value = 0xFFF + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0xD4D + @return CRC-12 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_CDMA2000() +{ + static const Parameters parameters = { 0xF13, 0xFFF, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 DECT (aka CRC-12 X-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 DECT has the following parameters and check value: + - polynomial = 0x80F + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0xF5B + @return CRC-12 DECT parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_DECT() +{ + static const Parameters parameters = { 0x80F, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 UMTS (aka CRC-12 3GPP). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 UMTS has the following parameters and check value: + - polynomial = 0x80F + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = true + - check value = 0xDAF + @return CRC-12 UMTS parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_UMTS() +{ + static const Parameters parameters = { 0x80F, 0x000, 0x000, false, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-13 BBC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-13 BBC has the following parameters and check value: + - polynomial = 0x1CF5 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x04FA + @return CRC-13 BBC parameters +*/ +inline const CRC::Parameters & CRC::CRC_13_BBC() +{ + static const Parameters parameters = { 0x1CF5, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-15 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-15 CAN has the following parameters and check value: + - polynomial = 0x4599 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x059E + @return CRC-15 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_15() +{ + static const Parameters parameters = { 0x4599, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-15 MPT1327. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-15 MPT1327 has the following parameters and check value: + - polynomial = 0x6815 + - initial value = 0x0000 + - final XOR = 0x0001 + - reflect input = false + - reflect output = false + - check value = 0x2566 + @return CRC-15 MPT1327 parameters +*/ +inline const CRC::Parameters & CRC::CRC_15_MPT1327() +{ + static const Parameters parameters = { 0x6815, 0x0000, 0x0001, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 ARC (aka CRC-16 IBM, CRC-16 LHA). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 ARC has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0xBB3D + @return CRC-16 ARC parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_ARC() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0x0000, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 BUYPASS (aka CRC-16 VERIFONE, CRC-16 UMTS). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 BUYPASS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xFEE8 + @return CRC-16 BUYPASS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_BUYPASS() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 CCITT FALSE. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CCITT FALSE has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x29B1 + @return CRC-16 CCITT FALSE parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CCITTFALSE() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-16 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CDMA2000 has the following parameters and check value: + - polynomial = 0xC867 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x4C06 + @return CRC-16 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CDMA2000() +{ + static const Parameters parameters = { 0xC867, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 CMS. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CMS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xAEE7 + @return CRC-16 CMS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CMS() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DECT-R (aka CRC-16 R-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DECT-R has the following parameters and check value: + - polynomial = 0x0589 + - initial value = 0x0000 + - final XOR = 0x0001 + - reflect input = false + - reflect output = false + - check value = 0x007E + @return CRC-16 DECT-R parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DECTR() +{ + static const Parameters parameters = { 0x0589, 0x0000, 0x0001, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DECT-X (aka CRC-16 X-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DECT-X has the following parameters and check value: + - polynomial = 0x0589 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x007F + @return CRC-16 DECT-X parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DECTX() +{ + static const Parameters parameters = { 0x0589, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DNP. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DNP has the following parameters and check value: + - polynomial = 0x3D65 + - initial value = 0x0000 + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0xEA82 + @return CRC-16 DNP parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DNP() +{ + static const Parameters parameters = { 0x3D65, 0x0000, 0xFFFF, true, true }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 GENIBUS (aka CRC-16 EPC, CRC-16 I-CODE, CRC-16 DARC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 GENIBUS has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = false + - reflect output = false + - check value = 0xD64E + @return CRC-16 GENIBUS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_GENIBUS() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 KERMIT (aka CRC-16 CCITT, CRC-16 CCITT-TRUE). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 KERMIT has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0x2189 + @return CRC-16 KERMIT parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_KERMIT() +{ + static const Parameters parameters = { 0x1021, 0x0000, 0x0000, true, true }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-16 MAXIM. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 MAXIM has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0x44C2 + @return CRC-16 MAXIM parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_MAXIM() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0xFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 MODBUS. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 MODBUS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0x4B37 + @return CRC-16 MODBUS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_MODBUS() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 T10-DIF. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 T10-DIF has the following parameters and check value: + - polynomial = 0x8BB7 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xD0DB + @return CRC-16 T10-DIF parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_T10DIF() +{ + static const Parameters parameters = { 0x8BB7, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 USB. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 USB has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0xB4C8 + @return CRC-16 USB parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_USB() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0xFFFF, true, true }; + return parameters; +} + +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 X-25 (aka CRC-16 IBM-SDLC, CRC-16 ISO-HDLC, CRC-16 B). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 X-25 has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0x906E + @return CRC-16 X-25 parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_X25() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 XMODEM (aka CRC-16 ZMODEM, CRC-16 ACORN, CRC-16 LTE). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 XMODEM has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x31C3 + @return CRC-16 XMODEM parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_XMODEM() +{ + static const Parameters parameters = { 0x1021, 0x0000, 0x0000, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-17 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-17 CAN has the following parameters and check value: + - polynomial = 0x1685B + - initial value = 0x00000 + - final XOR = 0x00000 + - reflect input = false + - reflect output = false + - check value = 0x04F03 + @return CRC-17 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_17_CAN() +{ + static const Parameters parameters = { 0x1685B, 0x00000, 0x00000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-21 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-21 CAN has the following parameters and check value: + - polynomial = 0x102899 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x0ED841 + @return CRC-21 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_21_CAN() +{ + static const Parameters parameters = { 0x102899, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 OPENPGP. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 OPENPGP has the following parameters and check value: + - polynomial = 0x864CFB + - initial value = 0xB704CE + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x21CF02 + @return CRC-24 OPENPGP parameters +*/ +inline const CRC::Parameters & CRC::CRC_24() +{ + static const Parameters parameters = { 0x864CFB, 0xB704CE, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 FlexRay-A. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 FlexRay-A has the following parameters and check value: + - polynomial = 0x5D6DCB + - initial value = 0xFEDCBA + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x7979BD + @return CRC-24 FlexRay-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_FLEXRAYA() +{ + static const Parameters parameters = { 0x5D6DCB, 0xFEDCBA, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 FlexRay-B. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 FlexRay-B has the following parameters and check value: + - polynomial = 0x5D6DCB + - initial value = 0xABCDEF + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x1F23B8 + @return CRC-24 FlexRay-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_FLEXRAYB() +{ + static const Parameters parameters = { 0x5D6DCB, 0xABCDEF, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 LTE-A/NR-A. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 LTE-A has the following parameters and check value: + - polynomial = 0x864CFB + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0xCDE703 + @return CRC-24 LTE-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_LTEA() +{ + static const Parameters parameters = { 0x864CFB, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 LTE-B/NR-B. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 LTE-B has the following parameters and check value: + - polynomial = 0x800063 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x23EF52 + @return CRC-24 LTE-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_LTEB() +{ + static const Parameters parameters = { 0x800063, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 NR-C. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 NR-C has the following parameters and check value: + - polynomial = 0xB2B117 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0xF48279 + @return CRC-24 NR-C parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_NRC() +{ + static const Parameters parameters = { 0xB2B117, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-30 CDMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-30 CDMA has the following parameters and check value: + - polynomial = 0x2030B9C7 + - initial value = 0x3FFFFFFF + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x3B3CB540 + @return CRC-30 CDMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_30() +{ + static const Parameters parameters = { 0x2030B9C7, 0x3FFFFFFF, 0x00000000, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-32 (aka CRC-32 ADCCP, CRC-32 PKZip). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = true + - reflect output = true + - check value = 0xCBF43926 + @return CRC-32 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-32 BZIP2 (aka CRC-32 AAL5, CRC-32 DECT-B, CRC-32 B-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 BZIP2 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0xFC891918 + @return CRC-32 BZIP2 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_BZIP2() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-32 C (aka CRC-32 ISCSI, CRC-32 Castagnoli, CRC-32 Interlaken). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 C has the following parameters and check value: + - polynomial = 0x1EDC6F41 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = true + - reflect output = true + - check value = 0xE3069283 + @return CRC-32 C parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_C() +{ + static const Parameters parameters = { 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; + return parameters; +} +#endif + +/** + @brief Returns a set of parameters for CRC-32 MPEG-2. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 MPEG-2 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x0376E6E7 + @return CRC-32 MPEG-2 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_MPEG2() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-32 POSIX. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 POSIX has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0x00000000 + - final XOR = 0xFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0x765E7680 + @return CRC-32 POSIX parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_POSIX() +{ + static const Parameters parameters = { 0x04C11DB7, 0x00000000, 0xFFFFFFFF, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-32 Q. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 Q has the following parameters and check value: + - polynomial = 0x814141AB + - initial value = 0x00000000 + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x3010BF7F + @return CRC-32 Q parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_Q() +{ + static const Parameters parameters = { 0x814141AB, 0x00000000, 0x00000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-40 GSM. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-40 GSM has the following parameters and check value: + - polynomial = 0x0004820009 + - initial value = 0x0000000000 + - final XOR = 0xFFFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0xD4164FC646 + @return CRC-40 GSM parameters +*/ +inline const CRC::Parameters & CRC::CRC_40_GSM() +{ + static const Parameters parameters = { 0x0004820009, 0x0000000000, 0xFFFFFFFFFF, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-64 ECMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-64 ECMA has the following parameters and check value: + - polynomial = 0x42F0E1EBA9EA3693 + - initial value = 0x0000000000000000 + - final XOR = 0x0000000000000000 + - reflect input = false + - reflect output = false + - check value = 0x6C40DF5F0B497347 + @return CRC-64 ECMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_64() +{ + static const Parameters parameters = { 0x42F0E1EBA9EA3693, 0x0000000000000000, 0x0000000000000000, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +#ifdef CRCPP_USE_NAMESPACE +} +#endif + +#endif // CRCPP_CRC_H_ diff --git a/third_party/curl.BUILD b/third_party/curl.BUILD new file mode 100644 index 000000000..dbbe3e8e2 --- /dev/null +++ b/third_party/curl.BUILD @@ -0,0 +1,37 @@ +# Builds CURL from a distribution. + +load("@org_apache_rocketmq//bazel:curl.bzl", "CURL_COPTS") + +package(features = ["no_copts_tokenization"]) + +config_setting( + name = "windows", + values = {"cpu": "x64_windows"}, + visibility = ["//visibility:private"], +) + +config_setting( + name = "osx", + values = {"cpu": "darwin"}, + visibility = ["//visibility:private"], +) + +cc_library( + name = "curl", + srcs = glob([ + "lib/**/*.c", + ]), + hdrs = glob([ + "include/curl/*.h", + "lib/**/*.h", + ]), + copts = CURL_COPTS + [ + "-DOS=\"os\"", + "-DCURL_EXTERN_SYMBOL=__attribute__((__visibility__(\"default\")))", + ], + includes = [ + "include/", + "lib/", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/third_party/date/CMakeLists.txt b/third_party/date/CMakeLists.txt new file mode 100644 index 000000000..3e9b809ee --- /dev/null +++ b/third_party/date/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(date_library INTERFACE) +target_include_directories(date_library + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +add_library(date::date ALIAS date_library) \ No newline at end of file diff --git a/third_party/date/include/date/chrono_io.h b/third_party/date/include/date/chrono_io.h new file mode 100644 index 000000000..21be40409 --- /dev/null +++ b/third_party/date/include/date/chrono_io.h @@ -0,0 +1,34 @@ +#ifndef CHRONO_IO_H +#define CHRONO_IO_H + +// The MIT License (MIT) +// +// Copyright (c) 2016, 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +// This functionality has moved to "date.h" + +#include "date.h" + +#endif // CHRONO_IO_H diff --git a/third_party/date/include/date/date.h b/third_party/date/include/date/date.h new file mode 100644 index 000000000..7b6b4e4cb --- /dev/null +++ b/third_party/date/include/date/date.h @@ -0,0 +1,8200 @@ +#ifndef DATE_H +#define DATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2016 Adrian Colomitchi +// Copyright (c) 2017 Florian Dang +// Copyright (c) 2017 Paul Thompson +// Copyright (c) 2018, 2019 Tomasz Kamiński +// Copyright (c) 2019 Jiangang Zhuang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#ifndef HAS_STRING_VIEW +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_STRING_VIEW 1 +# else +# define HAS_STRING_VIEW 0 +# endif +#endif // HAS_STRING_VIEW + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAS_STRING_VIEW +# include +#endif +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) +# pragma GCC diagnostic ignored "-Wpedantic" +# endif +# if __GNUC__ < 5 + // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +// warning C4127: conditional expression is constant +# pragma warning(disable : 4127) +#endif + +namespace date +{ + +//---------------+ +// Configuration | +//---------------+ + +#ifndef ONLY_C_LOCALE +# define ONLY_C_LOCALE 0 +#endif + +#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) +// MSVC +# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING +# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING +# endif +# if _MSC_VER < 1910 +// before VS2017 +# define CONSTDATA const +# define CONSTCD11 +# define CONSTCD14 +# define NOEXCEPT _NOEXCEPT +# else +// VS2017 and later +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +# endif + +#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 +// Oracle Developer Studio 12.6 and earlier +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept + +#elif __cplusplus >= 201402 +// C++14 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +#else +// C++11 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept +#endif + +#ifndef HAS_UNCAUGHT_EXCEPTIONS +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_UNCAUGHT_EXCEPTIONS 1 +# else +# define HAS_UNCAUGHT_EXCEPTIONS 0 +# endif +#endif // HAS_UNCAUGHT_EXCEPTIONS + +#ifndef HAS_VOID_T +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_VOID_T 1 +# else +# define HAS_VOID_T 0 +# endif +#endif // HAS_VOID_T + +// Protect from Oracle sun macro +#ifdef sun +# undef sun +#endif + +// Work around for a NVCC compiler bug which causes it to fail +// to compile std::ratio_{multiply,divide} when used directly +// in the std::chrono::duration template instantiations below +namespace detail { +template +using ratio_multiply = decltype(std::ratio_multiply{}); + +template +using ratio_divide = decltype(std::ratio_divide{}); +} // namespace detail + +//-----------+ +// Interface | +//-----------+ + +// durations + +using days = std::chrono::duration + , std::chrono::hours::period>>; + +using weeks = std::chrono::duration + , days::period>>; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +template + using sys_time = std::chrono::time_point; + +using sys_days = sys_time; +using sys_seconds = sys_time; + +struct local_t {}; + +template + using local_time = std::chrono::time_point; + +using local_seconds = local_time; +using local_days = local_time; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + short y_; + +public: + year() = default; + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 year operator-() const NOEXCEPT; + CONSTCD11 year operator+() const NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } + static CONSTCD11 year max() NOEXCEPT { return year{32767}; } +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + CONSTCD14 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 unsigned c_encoding() const NOEXCEPT; + CONSTCD11 unsigned iso_encoding() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT; + + friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const weekday& wd); + friend class weekday_indexed; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + weekday_indexed() = default; + CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + date::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +namespace detail +{ + +struct unspecified_month_disambiguator {}; + +} // namespace detail + +// year_month + +class year_month +{ + date::year y_; + date::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + + template + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + template + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +template +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +template +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +template +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + date::month m_; + date::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + date::month m_; + +public: + CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + date::month m_; + date::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const date::month& m, + const date::weekday_last& wd) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + date::year y_; + date::month m_; + date::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + template + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +template +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +template +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +template +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + date::year y_; + date::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT; + + template + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + date::year y_; + date::month m_; + date::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + template + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + date::year y_; + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, + const date::weekday_last& wdl) NOEXCEPT; + + template + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +// CONSTDATA date::month January{1}; +// CONSTDATA date::month February{2}; +// CONSTDATA date::month March{3}; +// CONSTDATA date::month April{4}; +// CONSTDATA date::month May{5}; +// CONSTDATA date::month June{6}; +// CONSTDATA date::month July{7}; +// CONSTDATA date::month August{8}; +// CONSTDATA date::month September{9}; +// CONSTDATA date::month October{10}; +// CONSTDATA date::month November{11}; +// CONSTDATA date::month December{12}; +// +// CONSTDATA date::weekday Sunday{0u}; +// CONSTDATA date::weekday Monday{1u}; +// CONSTDATA date::weekday Tuesday{2u}; +// CONSTDATA date::weekday Wednesday{3u}; +// CONSTDATA date::weekday Thursday{4u}; +// CONSTDATA date::weekday Friday{5u}; +// CONSTDATA date::weekday Saturday{6u}; + +#if HAS_VOID_T + +template > +struct is_clock + : std::false_type +{}; + +template +struct is_clock> + : std::true_type +{}; + +template inline constexpr bool is_clock_v = is_clock::value; + +#endif // HAS_VOID_T + +//----------------+ +// Implementation | +//----------------+ + +// utilities +namespace detail { + +template> +class save_istream +{ +protected: + std::basic_ios& is_; + CharT fill_; + std::ios::fmtflags flags_; + std::streamsize precision_; + std::streamsize width_; + std::basic_ostream* tie_; + std::locale loc_; + +public: + ~save_istream() + { + is_.fill(fill_); + is_.flags(flags_); + is_.precision(precision_); + is_.width(width_); + is_.imbue(loc_); + is_.tie(tie_); + } + + save_istream(const save_istream&) = delete; + save_istream& operator=(const save_istream&) = delete; + + explicit save_istream(std::basic_ios& is) + : is_(is) + , fill_(is.fill()) + , flags_(is.flags()) + , precision_(is.precision()) + , width_(is.width(0)) + , tie_(is.tie(nullptr)) + , loc_(is.getloc()) + { + if (tie_ != nullptr) + tie_->flush(); + } +}; + +template> +class save_ostream + : private save_istream +{ +public: + ~save_ostream() + { + if ((this->flags_ & std::ios::unitbuf) && +#if HAS_UNCAUGHT_EXCEPTIONS + std::uncaught_exceptions() == 0 && +#else + !std::uncaught_exception() && +#endif + this->is_.good()) + this->is_.rdbuf()->pubsync(); + } + + save_ostream(const save_ostream&) = delete; + save_ostream& operator=(const save_ostream&) = delete; + + explicit save_ostream(std::basic_ios& os) + : save_istream(os) + { + } +}; + +template +struct choose_trunc_type +{ + static const int digits = std::numeric_limits::digits; + using type = typename std::conditional + < + digits < 32, + std::int32_t, + typename std::conditional + < + digits < 64, + std::int64_t, +#ifdef __SIZEOF_INT128__ + __int128 +#else + std::int64_t +#endif + >::type + >::type; +}; + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + using std::numeric_limits; + using I = typename choose_trunc_type::type; + CONSTDATA auto digits = numeric_limits::digits; + static_assert(digits < numeric_limits::digits, ""); + CONSTDATA auto max = I{1} << (digits-1); + CONSTDATA auto min = -max; + const auto negative = t < T{0}; + if (min <= t && t <= max && t != 0 && t == t) + { + t = static_cast(static_cast(t)); + if (t == 0 && negative) + t = -t; + } + return t; +} + +template +struct static_gcd +{ + static const std::intmax_t value = static_gcd::value; +}; + +template +struct static_gcd +{ + static const std::intmax_t value = Xp; +}; + +template <> +struct static_gcd<0, 0> +{ + static const std::intmax_t value = 1; +}; + +template +struct no_overflow +{ +private: + static const std::intmax_t gcd_n1_n2 = static_gcd::value; + static const std::intmax_t gcd_d1_d2 = static_gcd::value; + static const std::intmax_t n1 = R1::num / gcd_n1_n2; + static const std::intmax_t d1 = R1::den / gcd_d1_d2; + static const std::intmax_t n2 = R2::num / gcd_n1_n2; + static const std::intmax_t d2 = R2::den / gcd_d1_d2; +#ifdef __cpp_constexpr + static const std::intmax_t max = std::numeric_limits::max(); +#else + static const std::intmax_t max = LLONG_MAX; +#endif + + template + struct mul // overflow == false + { + static const std::intmax_t value = Xp * Yp; + }; + + template + struct mul + { + static const std::intmax_t value = 1; + }; + +public: + static const bool value = (n1 <= max / d2) && (n2 <= max / d1); + typedef std::ratio::value, + mul::value> type; +}; + +} // detail + +// trunc towards zero +template +CONSTCD11 +inline +typename std::enable_if +< + detail::no_overflow::value, + To +>::type +trunc(const std::chrono::duration& d) +{ + return To{detail::trunc(std::chrono::duration_cast(d).count())}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +trunc(const std::chrono::duration& d) +{ + using std::chrono::duration_cast; + using std::chrono::duration; + using rep = typename std::common_type::type; + return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; +} + +#ifndef HAS_CHRONO_ROUNDING +# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) +# define HAS_CHRONO_ROUNDING 1 +# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 +# define HAS_CHRONO_ROUNDING 1 +# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 +# define HAS_CHRONO_ROUNDING 1 +# else +# define HAS_CHRONO_ROUNDING 0 +# endif +#endif // HAS_CHRONO_ROUNDING + +#if HAS_CHRONO_ROUNDING == 0 + +// round down +template +CONSTCD14 +inline +typename std::enable_if +< + detail::no_overflow::value, + To +>::type +floor(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t > d) + return t - To{1}; + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +floor(const std::chrono::duration& d) +{ + using rep = typename std::common_type::type; + return floor(floor>(d)); +} + +// round to nearest, to even on tie +template +CONSTCD14 +inline +To +round(const std::chrono::duration& d) +{ + auto t0 = floor(d); + auto t1 = t0 + To{1}; + if (t1 == To{0} && t0 < To{0}) + t1 = -t1; + auto diff0 = d - t0; + auto diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0 - trunc(t0/2)*2 == To{0}) + return t0; + return t1; + } + if (diff0 < diff1) + return t0; + return t1; +} + +// round up +template +CONSTCD14 +inline +To +ceil(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t < d) + return t + To{1}; + return t; +} + +template ::is_signed + >::type> +CONSTCD11 +std::chrono::duration +abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +// round down +template +CONSTCD11 +inline +std::chrono::time_point +floor(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{date::floor(tp.time_since_epoch())}; +} + +// round to nearest, to even on tie +template +CONSTCD11 +inline +std::chrono::time_point +round(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{round(tp.time_since_epoch())}; +} + +// round up +template +CONSTCD11 +inline +std::chrono::time_point +ceil(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{ceil(tp.time_since_epoch())}; +} + +#else // HAS_CHRONO_ROUNDING == 1 + +using std::chrono::floor; +using std::chrono::ceil; +using std::chrono::round; +using std::chrono::abs; + +#endif // HAS_CHRONO_ROUNDING + +namespace detail +{ + +template +CONSTCD14 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value, + To +>::type +round_i(const std::chrono::duration& d) +{ + return round(d); +} + +template +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point::value, + To +>::type +round_i(const std::chrono::duration& d) +{ + return d; +} + +template +CONSTCD11 +inline +std::chrono::time_point +round_i(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{round_i(tp.time_since_epoch())}; +} + +} // detail + +// trunc towards zero +template +CONSTCD11 +inline +std::chrono::time_point +trunc(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{trunc(tp.time_since_epoch())}; +} + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const day& d) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + return os; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + detail::low_level_fmt(os, d); + if (!d.ok()) + os << " is not a valid day"; + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) + y.count() - 1; + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const month& m) +{ + if (m.ok()) + { + CharT fmt[] = {'%', 'b', 0}; + os << format(os.getloc(), fmt, m); + } + else + os << static_cast(m); + return os; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + detail::low_level_fmt(os, m); + if (!m.ok()) + os << " is not a valid month"; + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} +CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} +CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} + +CONSTCD11 +inline +bool +year::ok() const NOEXCEPT +{ + return y_ != std::numeric_limits::min(); +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const year& y) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os.imbue(std::locale::classic()); + os << static_cast(y); + return os; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + detail::low_level_fmt(os, y); + if (!y.ok()) + os << " is not a valid year"; + return os; +} + +// weekday + +CONSTCD14 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + auto u = static_cast(z); + return static_cast(z >= -4 ? (u+4) % 7 : u % 7); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd != 7 ? wd : 0)) + {} + +CONSTCD14 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +unsigned weekday::c_encoding() const NOEXCEPT +{ + return unsigned{wd_}; +} + +CONSTCD11 +inline +unsigned weekday::iso_encoding() const NOEXCEPT +{ + return unsigned{((wd_ == 0u) ? 7u : wd_)}; +} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return x.wd_ == y.wd_; +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const wdu = x.wd_ - y.wd_; + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return days{wdu - wk * 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x.wd_)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const weekday& wd) +{ + if (wd.ok()) + { + CharT fmt[] = {'%', 'a', 0}; + os << format(fmt, wd); + } + else + os << wd.c_encoding(); + return os; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + detail::low_level_fmt(os, wd); + if (!wd.ok()) + os << " is not a valid weekday"; + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +date::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return date::day{static_cast(d)}; +} + +CONSTCD11 +inline +date::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return date::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA date::last_spec last{}; + +CONSTDATA date::month jan{1}; +CONSTDATA date::month feb{2}; +CONSTDATA date::month mar{3}; +CONSTDATA date::month apr{4}; +CONSTDATA date::month may{5}; +CONSTDATA date::month jun{6}; +CONSTDATA date::month jul{7}; +CONSTDATA date::month aug{8}; +CONSTDATA date::month sep{9}; +CONSTDATA date::month oct{10}; +CONSTDATA date::month nov{11}; +CONSTDATA date::month dec{12}; + +CONSTDATA date::weekday sun{0u}; +CONSTDATA date::weekday mon{1u}; +CONSTDATA date::weekday tue{2u}; +CONSTDATA date::weekday wed{3u}; +CONSTDATA date::weekday thu{4u}; +CONSTDATA date::weekday fri{5u}; +CONSTDATA date::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +CONSTDATA date::month January{1}; +CONSTDATA date::month February{2}; +CONSTDATA date::month March{3}; +CONSTDATA date::month April{4}; +CONSTDATA date::month May{5}; +CONSTDATA date::month June{6}; +CONSTDATA date::month July{7}; +CONSTDATA date::month August{8}; +CONSTDATA date::month September{9}; +CONSTDATA date::month October{10}; +CONSTDATA date::month November{11}; +CONSTDATA date::month December{12}; + +CONSTDATA date::weekday Monday{1}; +CONSTDATA date::weekday Tuesday{2}; +CONSTDATA date::weekday Wednesday{3}; +CONSTDATA date::weekday Thursday{4}; +CONSTDATA date::weekday Friday{5}; +CONSTDATA date::weekday Saturday{6}; +CONSTDATA date::weekday Sunday{7}; + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return date::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif // __GNUC__ + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd.wd_))) + , index_(static_cast(index)) + {} + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif // __GNUC__ + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const weekday_indexed& wdi) +{ + return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']'; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + detail::low_level_fmt(os, wdi); + if (!wdi.ok()) + os << " is not a valid weekday_indexed"; + return os; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const weekday_last& wdl) +{ + return low_level_fmt(os, wdl.weekday()) << "[last]"; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + detail::low_level_fmt(os, wdl); + if (!wdl.ok()) + os << " is not a valid weekday_last"; + return os; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +template +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +template +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +template +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +template +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +template +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const year_month& ym) +{ + low_level_fmt(os, ym.year()) << '/'; + return low_level_fmt(os, ym.month()); +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + detail::low_level_fmt(os, ym); + if (!ym.ok()) + os << " is not a valid year_month"; + return os; +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(29), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const month_day& md) +{ + low_level_fmt(os, md.month()) << '/'; + return low_level_fmt(os, md.day()); +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + detail::low_level_fmt(os, md); + if (!md.ok()) + os << " is not a valid month_day"; + return os; +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const month_day_last& mdl) +{ + return low_level_fmt(os, mdl.month()) << "/last"; +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + detail::low_level_fmt(os, mdl); + if (!mdl.ok()) + os << " is not a valid month_day_last"; + return os; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const month_weekday& mwd) +{ + low_level_fmt(os, mwd.month()) << '/'; + return low_level_fmt(os, mwd.weekday_indexed()); +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + detail::low_level_fmt(os, mwd); + if (!mwd.ok()) + os << " is not a valid month_weekday"; + return os; +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + low_level_fmt(os, mwdl.month()) << '/'; + return low_level_fmt(os, mwdl.weekday_last()); +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + detail::low_level_fmt(os, mwdl); + if (!mwdl.ok()) + os << " is not a valid month_weekday_last"; + return os; +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +template +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(28), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return (month() != February || !y_.is_leap()) && mdl_.ok() ? + d[static_cast(month()) - 1] : date::day{29}; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template +std::basic_ostream& +low_level_fmt(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + low_level_fmt(os, ymdl.year()) << '/'; + return low_level_fmt(os, ymdl.month_day_last()); +} + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + detail::low_level_fmt(os, ymdl); + if (!ymdl.ok()) + os << " is not a valid year_month_day_last"; + return os; +} + +template +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +template +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +template +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +template +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast(y_) - (m_ <= February); + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era = (y >= 0 ? y : y-399) / 400; + auto const yoe = static_cast(y - era * 400); // [0, 399] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] + return days{era * 146097 + static_cast(doe) - 719468}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.imbue(std::locale::classic()); + os << static_cast(ymd.year()) << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os.width(2); + os << static_cast(ymd.day()); + if (!ymd.ok()) + os << " is not a valid year_month_day"; + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719468; + auto const era = (z >= 0 ? z : z - 146096) / 146097; + auto const doe = static_cast(z - era * 146097); // [0, 146096] + auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] + auto const y = static_cast(yoe) + era * 400; + auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; +} + +template +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +template +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +template +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +template +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = date::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + detail::low_level_fmt(os, ymwdi.year()) << '/'; + detail::low_level_fmt(os, ymwdi.month()) << '/'; + detail::low_level_fmt(os, ymwdi.weekday_indexed()); + if (!ymwdi.ok()) + os << " is not a valid year_month_weekday"; + return os; +} + +template +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +template +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +template +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const date::year& y, + const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +template +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + detail::low_level_fmt(os, ymwdl.year()) << '/'; + detail::low_level_fmt(os, ymwdl.month()) << '/'; + detail::low_level_fmt(os, ymwdl.weekday_last()); + if (!ymwdl.ok()) + os << " is not a valid year_month_weekday_last"; + return os; +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +template +struct fields; + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr); + +template +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr); + +// hh_mm_ss + +namespace detail +{ + +struct undocumented {explicit undocumented() = default;}; + +// width::value is the number of fractional decimal digits in 1/n +// width<0>::value and width<1>::value are defined to be 0 +// If 1/n takes more than 18 fractional decimal digits, +// the result is truncated to 19. +// Example: width<2>::value == 1 +// Example: width<3>::value == 19 +// Example: width<4>::value == 2 +// Example: width<10>::value == 1 +// Example: width<1000>::value == 3 +template +struct width +{ + static_assert(d > 0, "width called with zero denominator"); + static CONSTDATA unsigned value = 1 + width::value; +}; + +template +struct width +{ + static CONSTDATA unsigned value = 0; +}; + +template +struct static_pow10 +{ +private: + static CONSTDATA std::uint64_t h = static_pow10::value; +public: + static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); +}; + +template <> +struct static_pow10<0> +{ + static CONSTDATA std::uint64_t value = 1; +}; + +template +class decimal_format_seconds +{ + using CT = typename std::common_type::type; + using rep = typename CT::rep; + static unsigned CONSTDATA trial_width = + detail::width::value; +public: + static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u; + using precision = std::chrono::duration::value>>; + +private: + std::chrono::seconds s_; + precision sub_s_; + +public: + CONSTCD11 decimal_format_seconds() + : s_() + , sub_s_() + {} + + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT + : s_(std::chrono::duration_cast(d)) + , sub_s_(std::chrono::duration_cast(d - s_)) + {} + + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return s_ + sub_s_; + } + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1}; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const decimal_format_seconds& x) + { + return x.print(os, std::chrono::treat_as_floating_point{}); + } + + template + std::basic_ostream& + print(std::basic_ostream& os, std::true_type) const + { + date::detail::save_ostream _(os); + std::chrono::duration d = s_ + sub_s_; + if (d < std::chrono::seconds{10}) + os << '0'; + os.precision(width+6); + os << std::fixed << d.count(); + return os; + } + + template + std::basic_ostream& + print(std::basic_ostream& os, std::false_type) const + { + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << s_.count(); + if (width > 0) + { +#if !ONLY_C_LOCALE + os << std::use_facet>(os.getloc()).decimal_point(); +#else + os << '.'; +#endif + date::detail::save_ostream _s(os); + os.imbue(std::locale::classic()); + os.width(width); + os << sub_s_.count(); + } + return os; + } +}; + +template +inline +CONSTCD11 +typename std::enable_if + < + std::numeric_limits::is_signed, + std::chrono::duration + >::type +abs(std::chrono::duration d) +{ + return d >= d.zero() ? +d : -d; +} + +template +inline +CONSTCD11 +typename std::enable_if + < + !std::numeric_limits::is_signed, + std::chrono::duration + >::type +abs(std::chrono::duration d) +{ + return d; +} + +} // namespace detail + +template +class hh_mm_ss +{ + using dfs = detail::decimal_format_seconds::type>; + + std::chrono::hours h_; + std::chrono::minutes m_; + dfs s_; + bool neg_; + +public: + static unsigned CONSTDATA fractional_width = dfs::width; + using precision = typename dfs::precision; + + CONSTCD11 hh_mm_ss() NOEXCEPT + : hh_mm_ss(Duration::zero()) + {} + + CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT + : h_(std::chrono::duration_cast(detail::abs(d))) + , m_(std::chrono::duration_cast(detail::abs(d)) - h_) + , s_(detail::abs(d) - h_ - m_) + , neg_(d < Duration::zero()) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} + CONSTCD14 std::chrono::seconds& + seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} + CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} + CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} + + CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} + CONSTCD11 precision to_duration() const NOEXCEPT + {return (s_.to_duration() + m_ + h_) * (1-2*neg_);} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + +private: + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, hh_mm_ss const& tod) + { + if (tod.is_negative()) + os << '-'; + if (tod.h_ < std::chrono::hours{10}) + os << '0'; + os << tod.h_.count() << ':'; + if (tod.m_ < std::chrono::minutes{10}) + os << '0'; + os << tod.m_.count() << ':' << tod.s_; + return os; + } + + template + friend + std::basic_ostream& + date::to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template + friend + std::basic_istream& + date::from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset); +}; + +inline +CONSTCD14 +bool +is_am(std::chrono::hours const& h) NOEXCEPT +{ + using std::chrono::hours; + return hours{0} <= h && h < hours{12}; +} + +inline +CONSTCD14 +bool +is_pm(std::chrono::hours const& h) NOEXCEPT +{ + using std::chrono::hours; + return hours{12} <= h && h < hours{24}; +} + +inline +CONSTCD14 +std::chrono::hours +make12(std::chrono::hours h) NOEXCEPT +{ + using std::chrono::hours; + if (h < hours{12}) + { + if (h == hours{0}) + h = hours{12}; + } + else + { + if (h != hours{12}) + h = h - hours{12}; + } + return h; +} + +inline +CONSTCD14 +std::chrono::hours +make24(std::chrono::hours h, bool is_pm) NOEXCEPT +{ + using std::chrono::hours; + if (is_pm) + { + if (h != hours{12}) + h = h + hours{12}; + } + else if (h == hours{12}) + h = hours{0}; + return h; +} + +template +using time_of_day = hh_mm_ss; + +template +CONSTCD11 +inline +hh_mm_ss> +make_time(const std::chrono::duration& d) +{ + return hh_mm_ss>(d); +} + +template +inline +typename std::enable_if +< + std::ratio_less::value + , std::basic_ostream& +>::type +operator<<(std::basic_ostream& os, const sys_time& tp) +{ + auto const dp = date::floor(tp); + return os << year_month_day(dp) << ' ' << make_time(tp-dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const sys_days& dp) +{ + return os << year_month_day(dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& ut) +{ + return (os << sys_time{ut.time_since_epoch()}); +} + +namespace detail +{ + +template +class string_literal; + +template +inline +CONSTCD14 +string_literal::type, + N1 + N2 - 1> +operator+(const string_literal& x, const string_literal& y) NOEXCEPT; + +template +class string_literal +{ + CharT p_[N]; + + CONSTCD11 string_literal() NOEXCEPT + : p_{} + {} + +public: + using const_iterator = const CharT*; + + string_literal(string_literal const&) = default; + string_literal& operator=(string_literal const&) = delete; + + template ::type> + CONSTCD11 string_literal(CharT c) NOEXCEPT + : p_{c} + { + } + + template ::type> + CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT + : p_{c1, c2} + { + } + + template ::type> + CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT + : p_{c1, c2, c3} + { + } + + CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template ::type> + CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template ::value>::type> + CONSTCD14 string_literal(string_literal const& a) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} + CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} + + CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} + CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} + + CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT + { + return p_[n]; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const string_literal& s) + { + return os << s.p_; + } + + template + friend + CONSTCD14 + string_literal::type, + N1 + N2 - 1> + operator+(const string_literal& x, const string_literal& y) NOEXCEPT; +}; + +template +CONSTCD11 +inline +string_literal +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + return string_literal(x[0], y[0]); +} + +template +CONSTCD11 +inline +string_literal +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + return string_literal(x[0], x[1], y[0]); +} + +template +CONSTCD14 +inline +string_literal::type, + N1 + N2 - 1> +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + using CT = typename std::conditional::type; + + string_literal r; + std::size_t i = 0; + for (; i < N1-1; ++i) + r.p_[i] = CT(x.p_[i]); + for (std::size_t j = 0; j < N2; ++j, ++i) + r.p_[i] = CT(y.p_[j]); + + return r; +} + + +template +inline +std::basic_string +operator+(std::basic_string x, const string_literal& y) +{ + x.append(y.data(), y.size()); + return x; +} + +#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ + && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) + +template ::value || + std::is_same::value || + std::is_same::value || + std::is_same::value>> +CONSTCD14 +inline +string_literal +msl(CharT c) NOEXCEPT +{ + return string_literal{c}; +} + +CONSTCD14 +inline +std::size_t +to_string_len(std::intmax_t i) +{ + std::size_t r = 0; + do + { + i /= 10; + ++r; + } while (i > 0); + return r; +} + +template +CONSTCD14 +inline +std::enable_if_t +< + N < 10, + string_literal +> +msl() NOEXCEPT +{ + return msl(char(N % 10 + '0')); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + 10 <= N, + string_literal +> +msl() NOEXCEPT +{ + return msl() + msl(char(N % 10 + '0')); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + std::ratio::type::den != 1, + string_literal::type::num) + + to_string_len(std::ratio::type::den) + 4> +> +msl(std::ratio) NOEXCEPT +{ + using R = typename std::ratio::type; + return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + + msl() + msl(CharT{']'}); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + std::ratio::type::den == 1, + string_literal::type::num) + 3> +> +msl(std::ratio) NOEXCEPT +{ + using R = typename std::ratio::type; + return msl(CharT{'['}) + msl() + msl(CharT{']'}); +} + + +#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +inline +std::string +to_string(std::uint64_t x) +{ + return std::to_string(x); +} + +template +inline +std::basic_string +to_string(std::uint64_t x) +{ + auto y = std::to_string(x); + return std::basic_string(y.begin(), y.end()); +} + +template +inline +typename std::enable_if +< + std::ratio::type::den != 1, + std::basic_string +>::type +msl(std::ratio) +{ + using R = typename std::ratio::type; + return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + + to_string(R::den) + CharT{']'}; +} + +template +inline +typename std::enable_if +< + std::ratio::type::den == 1, + std::basic_string +>::type +msl(std::ratio) +{ + using R = typename std::ratio::type; + return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; +} + +#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +template +CONSTCD11 +inline +string_literal +msl(std::atto) NOEXCEPT +{ + return string_literal{'a'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::femto) NOEXCEPT +{ + return string_literal{'f'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::pico) NOEXCEPT +{ + return string_literal{'p'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::nano) NOEXCEPT +{ + return string_literal{'n'}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + std::is_same::value, + string_literal +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal{'\xC2', '\xB5'}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::is_same::value, + string_literal +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal{CharT{static_cast('\xB5')}}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::milli) NOEXCEPT +{ + return string_literal{'m'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::centi) NOEXCEPT +{ + return string_literal{'c'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::deca) NOEXCEPT +{ + return string_literal{'d', 'a'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::deci) NOEXCEPT +{ + return string_literal{'d'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::hecto) NOEXCEPT +{ + return string_literal{'h'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::kilo) NOEXCEPT +{ + return string_literal{'k'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::mega) NOEXCEPT +{ + return string_literal{'M'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::giga) NOEXCEPT +{ + return string_literal{'G'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::tera) NOEXCEPT +{ + return string_literal{'T'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::peta) NOEXCEPT +{ + return string_literal{'P'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::exa) NOEXCEPT +{ + return string_literal{'E'}; +} + +template +CONSTCD11 +inline +auto +get_units(Period p) + -> decltype(msl(p) + string_literal{'s'}) +{ + return msl(p) + string_literal{'s'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<1>) +{ + return string_literal{'s'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<3600>) +{ + return string_literal{'h'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<60>) +{ + return string_literal{'m', 'i', 'n'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<86400>) +{ + return string_literal{'d'}; +} + +template > +struct make_string; + +template <> +struct make_string +{ + template + static + std::string + from(Rep n) + { + return std::to_string(n); + } +}; + +template +struct make_string +{ + template + static + std::basic_string + from(Rep n) + { + auto s = std::to_string(n); + return std::basic_string(s.begin(), s.end()); + } +}; + +template <> +struct make_string +{ + template + static + std::wstring + from(Rep n) + { + return std::to_wstring(n); + } +}; + +template +struct make_string +{ + template + static + std::basic_string + from(Rep n) + { + auto s = std::to_wstring(n); + return std::basic_string(s.begin(), s.end()); + } +}; + +} // namespace detail + +// to_stream + +CONSTDATA year nanyear{-32768}; + +template +struct fields +{ + year_month_day ymd{nanyear/0/0}; + weekday wd{8u}; + hh_mm_ss tod{}; + bool has_tod = false; + + fields() = default; + + fields(year_month_day ymd_) : ymd(ymd_) {} + fields(weekday wd_) : wd(wd_) {} + fields(hh_mm_ss tod_) : tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} + fields(year_month_day ymd_, hh_mm_ss tod_) : ymd(ymd_), tod(tod_), + has_tod(true) {} + + fields(weekday wd_, hh_mm_ss tod_) : wd(wd_), tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_, hh_mm_ss tod_) + : ymd(ymd_) + , wd(wd_) + , tod(tod_) + , has_tod(true) + {} +}; + +namespace detail +{ + +template +unsigned +extract_weekday(std::basic_ostream& os, const fields& fds) +{ + if (!fds.ymd.ok() && !fds.wd.ok()) + { + // fds does not contain a valid weekday + os.setstate(std::ios::failbit); + return 8; + } + weekday wd; + if (fds.ymd.ok()) + { + wd = weekday{sys_days(fds.ymd)}; + if (fds.wd.ok() && wd != fds.wd) + { + // fds.ymd and fds.wd are inconsistent + os.setstate(std::ios::failbit); + return 8; + } + } + else + wd = fds.wd; + return static_cast((wd - Sunday).count()); +} + +template +unsigned +extract_month(std::basic_ostream& os, const fields& fds) +{ + if (!fds.ymd.month().ok()) + { + // fds does not contain a valid month + os.setstate(std::ios::failbit); + return 0; + } + return static_cast(fds.ymd.month()); +} + +} // namespace detail + +#if ONLY_C_LOCALE + +namespace detail +{ + +inline +std::pair +weekday_names() +{ + static const std::string nm[] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair +month_names() +{ + static const std::string nm[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair +ampm_names() +{ + static const std::string nm[] = + { + "AM", + "PM" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +template +FwdIter +scan_keyword(std::basic_istream& is, FwdIter kb, FwdIter ke) +{ + size_t nkw = static_cast(std::distance(kb, ke)); + const unsigned char doesnt_match = '\0'; + const unsigned char might_match = '\1'; + const unsigned char does_match = '\2'; + unsigned char statbuf[100]; + unsigned char* status = statbuf; + std::unique_ptr stat_hold(0, free); + if (nkw > sizeof(statbuf)) + { + status = (unsigned char*)std::malloc(nkw); + if (status == nullptr) + throw std::bad_alloc(); + stat_hold.reset(status); + } + size_t n_might_match = nkw; // At this point, any keyword might match + size_t n_does_match = 0; // but none of them definitely do + // Initialize all statuses to might_match, except for "" keywords are does_match + unsigned char* st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (!ky->empty()) + *st = might_match; + else + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + // While there might be a match, test keywords against the next CharT + for (size_t indx = 0; is && n_might_match > 0; ++indx) + { + // Peek at the next CharT but don't consume it + auto ic = is.peek(); + if (ic == EOF) + { + is.setstate(std::ios::eofbit); + break; + } + auto c = static_cast(toupper(static_cast(ic))); + bool consume = false; + // For each keyword which might match, see if the indx character is c + // If a match if found, consume c + // If a match is found, and that is the last character in the keyword, + // then that keyword matches. + // If the keyword doesn't match this character, then change the keyword + // to doesn't match + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == might_match) + { + if (c == static_cast(toupper(static_cast((*ky)[indx])))) + { + consume = true; + if (ky->size() == indx+1) + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + else + { + *st = doesnt_match; + --n_might_match; + } + } + } + // consume if we matched a character + if (consume) + { + (void)is.get(); + // If we consumed a character and there might be a matched keyword that + // was marked matched on a previous iteration, then such keywords + // are now marked as not matching. + if (n_might_match + n_does_match > 1) + { + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == does_match && ky->size() != indx+1) + { + *st = doesnt_match; + --n_does_match; + } + } + } + } + } + // We've exited the loop because we hit eof and/or we have no more "might matches". + // Return the first matching result + for (st = status; kb != ke; ++kb, ++st) + if (*st == does_match) + break; + if (kb == ke) + is.setstate(std::ios::failbit); + return kb; +} + +} // namespace detail + +#endif // ONLY_C_LOCALE + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec) +{ +#if ONLY_C_LOCALE + using detail::weekday_names; + using detail::month_names; + using detail::ampm_names; +#endif + using detail::save_ostream; + using detail::get_units; + using detail::extract_weekday; + using detail::extract_month; + using std::ios; + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + date::detail::save_ostream ss(os); + os.fill(' '); + os.flags(std::ios::skipws | std::ios::dec); + os.width(0); + tm tm{}; + bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); +#if !ONLY_C_LOCALE + auto& facet = std::use_facet>(os.getloc()); +#endif + const CharT* command = nullptr; + CharT modified = CharT{}; + for (; *fmt; ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + if (command) + { + if (modified == CharT{}) + { + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else // ONLY_C_LOCALE + os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + tm.tm_mon = static_cast(extract_month(os, fds)) - 1; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else // ONLY_C_LOCALE + os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'c': + case 'x': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + if (*fmt == 'c' && !fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + auto const& ymd = fds.ymd; + auto ld = local_days(ymd); + if (*fmt == 'c') + { + tm.tm_sec = static_cast(fds.tod.seconds().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_hour = static_cast(fds.tod.hours().count()); + } + tm.tm_mday = static_cast(static_cast(ymd.day())); + tm.tm_mon = static_cast(extract_month(os, fds) - 1); + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + CharT f[3] = {'%'}; + auto fe = std::begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, std::begin(f), fe); +#else // ONLY_C_LOCALE + if (*fmt == 'c') + { + auto wd = static_cast(extract_weekday(os, fds)); + os << weekday_names().first[static_cast(wd)+7] + << ' '; + os << month_names().first[extract_month(os, fds)-1+12] << ' '; + auto d = static_cast(static_cast(fds.ymd.day())); + if (d < 10) + os << ' '; + os << d << ' ' + << make_time(duration_cast(fds.tod.to_duration())) + << ' ' << fds.ymd.year(); + + } + else // *fmt == 'x' + { + auto const& ymd = fds.ymd; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.year()) % 100; + } +#endif // ONLY_C_LOCALE + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'C': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (y >= 0) + { + os.width(2); + os << y/100; + } + else + { + os << CharT{'-'}; + os.width(2); + os << -(y-99)/100; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + tm.tm_year = y - 1900; + CharT f[3] = {'%', 'E', 'C'}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'd': + case 'e': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.day().ok()) + os.setstate(std::ios::failbit); + auto d = static_cast(static_cast(fds.ymd.day())); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream _(os); + if (*fmt == CharT{'d'}) + os.fill('0'); + else + os.fill(' '); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << d; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + tm.tm_mday = d; + CharT f[3] = {'%', 'O', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.year()) % 100; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream _(os); + os.imbue(std::locale::classic()); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(4); + os << static_cast(ymd.year()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.month()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.day()); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'g': + case 'G': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); + auto y = year_month_day{ld + days{3}}.year(); + auto start = local_days((y-years{1})/December/Thursday[last]) + + (Monday-Thursday); + if (ld < start) + --y; + if (*fmt == CharT{'G'}) + os << y; + else + { + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << std::abs(static_cast(y)) % 100; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'H': + case 'I': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } + auto hms = fds.tod; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + if (fds.ymd.ok() || fds.has_tod) + { + days doy; + if (fds.ymd.ok()) + { + auto ld = local_days(fds.ymd); + auto y = fds.ymd.year(); + doy = ld - local_days(y/January/1) + days{1}; + } + else + { + doy = duration_cast(fds.tod.to_duration()); + } + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(3); + os << doy.count(); + } + else + { + os.setstate(std::ios::failbit); + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'm': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.month().ok()) + os.setstate(std::ios::failbit); + auto m = static_cast(fds.ymd.month()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (m < 10) + os << CharT{'0'}; + os << m; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_mon = static_cast(m-1); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'M': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_min = static_cast(fds.tod.minutes().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'n': + if (command) + { + if (modified == CharT{}) + os << CharT{'\n'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(fds.tod.hours().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else + if (date::is_am(fds.tod.hours())) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'Q': + case 'q': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + auto d = fds.tod.to_duration(); + if (*fmt == 'q') + os << get_units(typename decltype(d)::period::type{}); + else + os << d.count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(fds.tod.hours().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_sec = static_cast(fds.tod.seconds().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else + hh_mm_ss tod(duration_cast(fds.tod.to_duration())); + save_ostream _(os); + os.fill('0'); + os.width(2); + os << date::make12(tod.hours()).count() << CharT{':'}; + os.width(2); + os << tod.minutes().count() << CharT{':'}; + os.width(2); + os << tod.seconds().count() << CharT{' '}; + if (date::is_am(tod.hours())) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (fds.tod.hours() < hours{10}) + os << CharT{'0'}; + os << fds.tod.hours().count() << CharT{':'}; + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'S': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << fds.tod.s_; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 't': + if (command) + { + if (modified == CharT{}) + os << CharT{'\t'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + os << fds.tod; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'u': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto wd = extract_weekday(os, fds); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << (wd != 0 ? wd : 7u); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'U': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Sunday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } + #if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'V': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto y = year_month_day{ld + days{3}}.year(); + auto st = local_days((y-years{1})/12/Thursday[last]) + + (Monday-Thursday); + if (ld < st) + { + --y; + st = local_days((y - years{1})/12/Thursday[last]) + + (Monday-Thursday); + } + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + auto const& ymd = fds.ymd; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'w': + if (command) + { + auto wd = extract_weekday(os, fds); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + os << wd; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'W': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Monday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'X': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_sec = static_cast(fds.tod.seconds().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_hour = static_cast(fds.tod.hours().count()); + CharT f[3] = {'%'}; + auto fe = std::begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, std::begin(f), fe); +#else + os << fds.tod; +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'y': + if (command) + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + y = std::abs(y) % 100; + if (y < 10) + os << CharT{'0'}; + os << y; +#if !ONLY_C_LOCALE + } + else + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = y - 1900; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'Y': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = fds.ymd.year(); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream _(os); + os.imbue(std::locale::classic()); + os << y; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(y) - 1900; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'z': + if (command) + { + if (offset_sec == nullptr) + { + // Can not format %z with unknown offset + os.setstate(ios::failbit); + return os; + } + auto m = duration_cast(*offset_sec); + auto neg = m < minutes{0}; + m = date::abs(m); + auto h = duration_cast(m); + m -= h; + if (neg) + os << CharT{'-'}; + else + os << CharT{'+'}; + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + if (modified != CharT{}) + os << CharT{':'}; + if (m < minutes{10}) + os << CharT{'0'}; + os << m.count(); + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + if (abbrev == nullptr) + { + // Can not format %Z with unknown time_zone + os.setstate(ios::failbit); + return os; + } + for (auto c : *abbrev) + os << CharT(c); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + os << CharT{'%'} << modified << *fmt; + command = nullptr; + modified = CharT{}; + } + } + else + os << *fmt; + break; + case '%': + if (command) + { + if (modified == CharT{}) + { + os << CharT{'%'}; + command = nullptr; + } + else + { + os << CharT{'%'} << modified << CharT{'%'}; + command = nullptr; + modified = CharT{}; + } + } + else + command = fmt; + break; + default: + if (command) + { + os << CharT{'%'}; + command = nullptr; + } + if (modified != CharT{}) + { + os << modified; + modified = CharT{}; + } + os << *fmt; + break; + } + } + if (command) + os << CharT{'%'}; + if (modified != CharT{}) + os << modified; + return os; +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const year& y) +{ + using CT = std::chrono::seconds; + fields fds{y/0/0}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const month& m) +{ + using CT = std::chrono::seconds; + fields fds{m/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const day& d) +{ + using CT = std::chrono::seconds; + fields fds{d/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const weekday& wd) +{ + using CT = std::chrono::seconds; + fields fds{wd}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const year_month& ym) +{ + using CT = std::chrono::seconds; + fields fds{ym/0}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const month_day& md) +{ + using CT = std::chrono::seconds; + fields fds{md/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const year_month_day& ymd) +{ + using CT = std::chrono::seconds; + fields fds{ymd}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const std::chrono::duration& d) +{ + using Duration = std::chrono::duration; + using CT = typename std::common_type::type; + fields fds{hh_mm_ss{d}}; + return to_stream(os, fmt, fds); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const local_time& tp, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr) +{ + using CT = typename std::common_type::type; + auto ld = floor(tp); + fields fds{year_month_day{ld}, hh_mm_ss{tp-local_seconds{ld}}}; + return to_stream(os, fmt, fds, abbrev, offset_sec); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const sys_time& tp) +{ + using std::chrono::seconds; + using CT = typename std::common_type::type; + const std::string abbrev("UTC"); + CONSTDATA seconds offset{0}; + auto sd = floor(tp); + fields fds{year_month_day{sd}, hh_mm_ss{tp-sys_seconds{sd}}}; + return to_stream(os, fmt, fds, &abbrev, &offset); +} + +// format + +template +auto +format(const std::locale& loc, const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt, tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template +auto +format(const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt, tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt, tp); + return os.str(); +} + +template +auto +format(const std::locale& loc, const std::basic_string& fmt, + const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +auto +format(const std::basic_string& fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +// parse + +namespace detail +{ + +template +bool +read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) +{ + auto ic = is.get(); + if (Traits::eq_int_type(ic, Traits::eof()) || + !Traits::eq(Traits::to_char_type(ic), fmt)) + { + err |= std::ios::failbit; + is.setstate(std::ios::failbit); + return false; + } + return true; +} + +template +unsigned +read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + unsigned x = 0; + unsigned count = 0; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + (void)is.get(); + ++count; + x = 10*x + static_cast(c - '0'); + if (count == M) + break; + } + if (count < m) + is.setstate(std::ios::failbit); + return x; +} + +template +int +read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (('0' <= c && c <= '9') || c == '-' || c == '+') + { + if (c == '-' || c == '+') + (void)is.get(); + auto x = static_cast(read_unsigned(is, std::max(m, 1u), M)); + if (!is.fail()) + { + if (c == '-') + x = -x; + return x; + } + } + } + if (m > 0) + is.setstate(std::ios::failbit); + return 0; +} + +template +long double +read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + unsigned count = 0; + unsigned fcount = 0; + unsigned long long i = 0; + unsigned long long f = 0; + bool parsing_fraction = false; +#if ONLY_C_LOCALE + typename Traits::int_type decimal_point = '.'; +#else + auto decimal_point = Traits::to_int_type( + std::use_facet>(is.getloc()).decimal_point()); +#endif + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + if (Traits::eq_int_type(ic, decimal_point)) + { + decimal_point = Traits::eof(); + parsing_fraction = true; + } + else + { + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + if (!parsing_fraction) + { + i = 10*i + static_cast(c - '0'); + } + else + { + f = 10*f + static_cast(c - '0'); + ++fcount; + } + } + (void)is.get(); + if (++count == M) + break; + } + if (count < m) + { + is.setstate(std::ios::failbit); + return 0; + } + return i + f/std::pow(10.L, fcount); +} + +struct rs +{ + int& i; + unsigned m; + unsigned M; +}; + +struct ru +{ + int& i; + unsigned m; + unsigned M; +}; + +struct rld +{ + long double& i; + unsigned m; + unsigned M; +}; + +template +void +read(std::basic_istream&) +{ +} + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args); + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args); + +template +void +read(std::basic_istream& is, int a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args); + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args) +{ + // No-op if a0 == CharT{} + if (a0 != CharT{}) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + is.setstate(std::ios::failbit | std::ios::eofbit); + return; + } + if (!Traits::eq(Traits::to_char_type(ic), a0)) + { + is.setstate(std::ios::failbit); + return; + } + (void)is.get(); + } + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args) +{ + auto x = read_signed(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args) +{ + auto x = read_unsigned(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = static_cast(x); + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, int a0, Args&& ...args) +{ + if (a0 != -1) + { + auto u = static_cast(a0); + CharT buf[std::numeric_limits::digits10+2u] = {}; + auto e = buf; + do + { + *e++ = static_cast(CharT(u % 10) + CharT{'0'}); + u /= 10; + } while (u > 0); + std::reverse(buf, e); + for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) + read(is, *p); + } + if (is.rdstate() == std::ios::goodbit) + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args) +{ + auto x = read_long_double(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +inline +void +checked_set(T& value, T from, T not_a_value, std::basic_ios& is) +{ + if (!is.fail()) + { + if (value == not_a_value) + value = std::move(from); + else if (value != from) + is.setstate(std::ios::failbit); + } +} + +} // namespace detail; + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, std::basic_string* abbrev, + std::chrono::minutes* offset) +{ + using std::numeric_limits; + using std::ios; + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using detail::round_i; + typename std::basic_istream::sentry ok{is, true}; + if (ok) + { + date::detail::save_istream ss(is); + is.fill(' '); + is.flags(std::ios::skipws | std::ios::dec); + is.width(0); +#if !ONLY_C_LOCALE + auto& f = std::use_facet>(is.getloc()); + std::tm tm{}; +#endif + const CharT* command = nullptr; + auto modified = CharT{}; + auto width = -1; + + CONSTDATA int not_a_year = numeric_limits::min(); + CONSTDATA int not_a_2digit_year = 100; + CONSTDATA int not_a_century = not_a_year / 100; + CONSTDATA int not_a_month = 0; + CONSTDATA int not_a_day = 0; + CONSTDATA int not_a_hour = numeric_limits::min(); + CONSTDATA int not_a_hour_12_value = 0; + CONSTDATA int not_a_minute = not_a_hour; + CONSTDATA Duration not_a_second = Duration::min(); + CONSTDATA int not_a_doy = -1; + CONSTDATA int not_a_weekday = 8; + CONSTDATA int not_a_week_num = 100; + CONSTDATA int not_a_ampm = -1; + CONSTDATA minutes not_a_offset = minutes::min(); + + int Y = not_a_year; // c, F, Y * + int y = not_a_2digit_year; // D, x, y * + int g = not_a_2digit_year; // g * + int G = not_a_year; // G * + int C = not_a_century; // C * + int m = not_a_month; // b, B, h, m, c, D, F, x * + int d = not_a_day; // c, d, D, e, F, x * + int j = not_a_doy; // j * + int wd = not_a_weekday; // a, A, u, w * + int H = not_a_hour; // c, H, R, T, X * + int I = not_a_hour_12_value; // I, r * + int p = not_a_ampm; // p, r * + int M = not_a_minute; // c, M, r, R, T, X * + Duration s = not_a_second; // c, r, S, T, X * + int U = not_a_week_num; // U * + int V = not_a_week_num; // V * + int W = not_a_week_num; // W * + std::basic_string temp_abbrev; // Z * + minutes temp_offset = not_a_offset; // z * + + using detail::read; + using detail::rs; + using detail::ru; + using detail::rld; + using detail::checked_set; + for (; *fmt != CharT{} && !is.fail(); ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + case 'u': + case 'w': // wd: a, A, u, w + if (command) + { + int trial_wd = not_a_weekday; + if (*fmt == 'a' || *fmt == 'A') + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; +#else + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + trial_wd = i % 7; +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + } + else // *fmt == 'u' || *fmt == 'w' + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + read(is, ru{trial_wd, 1, width == -1 ? + 1u : static_cast(width)}); + if (!is.fail()) + { + if (*fmt == 'u') + { + if (!(1 <= trial_wd && trial_wd <= 7)) + { + trial_wd = not_a_weekday; + is.setstate(ios::failbit); + } + else if (trial_wd == 7) + trial_wd = 0; + } + else // *fmt == 'w' + { + if (!(0 <= trial_wd && trial_wd <= 6)) + { + trial_wd = not_a_weekday; + is.setstate(ios::failbit); + } + } + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + } + if (trial_wd != not_a_weekday) + checked_set(wd, trial_wd, not_a_weekday, is); + } + else // !command + read(is, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + int ttm = not_a_month; +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + ttm = tm.tm_mon + 1; + is.setstate(err); +#else + auto nm = detail::month_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + ttm = i % 12 + 1; +#endif + checked_set(m, ttm, not_a_month, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'c': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%a %b %e %T %Y" + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(wd, static_cast(i % 7), not_a_weekday, is); + ws(is); + nm = detail::month_names(); + i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(m, static_cast(i % 12 + 1), not_a_month, is); + ws(is); + int td = not_a_day; + read(is, rs{td, 1, 2}); + checked_set(d, td, not_a_day, is); + ws(is); + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH; + int tM; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i(duration{S}), + not_a_second, is); + ws(is); + int tY = not_a_year; + read(is, rs{tY, 1, 4u}); + checked_set(Y, tY, not_a_year, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'x': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + } + is.setstate(err); +#else + // "%m/%d/%y" + int ty = not_a_2digit_year; + int tm = not_a_month; + int td = not_a_day; + read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tm, not_a_month, is); + checked_set(d, td, not_a_day, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'X': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%T" + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i(duration{S}), + not_a_second, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'C': + if (command) + { + int tC = not_a_century; +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); +#if !ONLY_C_LOCALE + } + else + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + auto tY = tm.tm_year + 1900; + tC = (tY >= 0 ? tY : tY-99) / 100; + } + is.setstate(err); + } +#endif + checked_set(C, tC, not_a_century, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + int tn = not_a_month; + int td = not_a_day; + int ty = not_a_2digit_year; + read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + int tY = not_a_year; + int tn = not_a_month; + int td = not_a_day; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, + CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); + checked_set(Y, tY, not_a_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'd': + case 'e': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int td = not_a_day; + read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(d, td, not_a_day, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + checked_set(d, tm.tm_mday, not_a_day, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'H': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tH = not_a_hour; + read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(H, tH, not_a_hour, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(H, tm.tm_hour, not_a_hour, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'I': + if (command) + { + if (modified == CharT{}) + { + int tI = not_a_hour_12_value; + // reads in an hour into I, but most be in [1, 12] + read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); + if (!(1 <= tI && tI <= 12)) + is.setstate(ios::failbit); + checked_set(I, tI, not_a_hour_12_value, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + int tj = not_a_doy; + read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); + checked_set(j, tj, not_a_doy, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'M': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tM = not_a_minute; + read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(M, tM, not_a_minute, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(M, tm.tm_min, not_a_minute, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'm': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tn = not_a_month; + read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(m, tn, not_a_month, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(m, tm.tm_mon + 1, not_a_month, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'n': + case 't': + if (command) + { + if (modified == CharT{}) + { + // %n matches a single white space character + // %t matches 0 or 1 white space characters + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + ios::iostate err = ios::eofbit; + if (*fmt == 'n') + err |= ios::failbit; + is.setstate(err); + break; + } + if (isspace(ic)) + { + (void)is.get(); + } + else if (*fmt == 'n') + is.setstate(ios::failbit); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + int tp = not_a_ampm; +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_hour = 1; + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (tm.tm_hour == 1) + tp = 0; + else if (tm.tm_hour == 13) + tp = 1; + else + is.setstate(err); +#else + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + tp = static_cast(i); +#endif + checked_set(p, tp, not_a_ampm, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_hour, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%I:%M:%S %p" + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + int tI = not_a_hour_12_value; + int tM = not_a_minute; + read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(I, tI, not_a_hour_12_value, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i(duration{S}), + not_a_second, is); + ws(is); + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(p, static_cast(i), not_a_ampm, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + int tH = not_a_hour; + int tM = not_a_minute; + read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, + ru{tM, 1, 2}, CharT{'\0'}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'S': + if (command) + { + #if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + read(is, rld{S, 1, width == -1 ? w : static_cast(width)}); + checked_set(s, round_i(duration{S}), + not_a_second, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i(duration{S}), + not_a_second, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'O'}) +#endif + { + int tY = not_a_year; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); + checked_set(Y, tY, not_a_year, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + int ty = not_a_2digit_year; + read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(y, ty, not_a_2digit_year, is); + } +#if !ONLY_C_LOCALE + else + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'g': + if (command) + { + if (modified == CharT{}) + { + int tg = not_a_2digit_year; + read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(g, tg, not_a_2digit_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'G': + if (command) + { + if (modified == CharT{}) + { + int tG = not_a_year; + read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); + checked_set(G, tG, not_a_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'U': + if (command) + { + if (modified == CharT{}) + { + int tU = not_a_week_num; + read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(U, tU, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'V': + if (command) + { + if (modified == CharT{}) + { + int tV = not_a_week_num; + read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(V, tV, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'W': + if (command) + { + if (modified == CharT{}) + { + int tW = not_a_week_num; + read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(W, tW, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else + read(is, *fmt); + break; + case '%': + if (command) + { + if (modified == CharT{}) + read(is, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + command = fmt; + break; + case 'z': + if (command) + { + int tH, tM; + minutes toff = not_a_offset; + bool neg = false; + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (c == '-') + neg = true; + } + if (modified == CharT{}) + { + read(is, rs{tH, 2, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if ('0' <= c && c <= '9') + { + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + else + { + read(is, rs{tH, 1, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (c == ':') + { + (void)is.get(); + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + if (neg) + toff = -toff; + checked_set(temp_offset, toff, not_a_offset, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + std::basic_string buf; + while (is.rdstate() == std::ios::goodbit) + { + auto i = is.rdbuf()->sgetc(); + if (Traits::eq_int_type(i, Traits::eof())) + { + is.setstate(ios::eofbit); + break; + } + auto wc = Traits::to_char_type(i); + auto c = static_cast(wc); + // is c a valid time zone name or abbreviation character? + if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || + c == '_' || c == '/' || c == '-' || c == '+')) + break; + buf.push_back(c); + is.rdbuf()->sbumpc(); + } + if (buf.empty()) + is.setstate(ios::failbit); + checked_set(temp_abbrev, buf, {}, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + default: + if (command) + { + if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') + { + width = static_cast(*fmt) - '0'; + while ('0' <= fmt[1] && fmt[1] <= '9') + width = 10*width + static_cast(*++fmt) - '0'; + } + else + { + if (modified == CharT{}) + read(is, CharT{'%'}, width, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else // !command + { + if (isspace(static_cast(*fmt))) + { + // space matches 0 or more white space characters + if (is.good()) + ws(is); + } + else + read(is, *fmt); + } + break; + } + } + // is.fail() || *fmt == CharT{} + if (is.rdstate() == ios::goodbit && command) + { + if (modified == CharT{}) + read(is, CharT{'%'}, width); + else + read(is, CharT{'%'}, width, modified); + } + if (!is.fail()) + { + if (y != not_a_2digit_year) + { + // Convert y and an optional C to Y + if (!(0 <= y && y <= 99)) + goto broken; + if (C == not_a_century) + { + if (Y == not_a_year) + { + if (y >= 69) + C = 19; + else + C = 20; + } + else + { + C = (Y >= 0 ? Y : Y-100) / 100; + } + } + int tY; + if (C >= 0) + tY = 100*C + y; + else + tY = 100*(C+1) - (y == 0 ? 100 : y); + if (Y != not_a_year && Y != tY) + goto broken; + Y = tY; + } + if (g != not_a_2digit_year) + { + // Convert g and an optional C to G + if (!(0 <= g && g <= 99)) + goto broken; + if (C == not_a_century) + { + if (G == not_a_year) + { + if (g >= 69) + C = 19; + else + C = 20; + } + else + { + C = (G >= 0 ? G : G-100) / 100; + } + } + int tG; + if (C >= 0) + tG = 100*C + g; + else + tG = 100*(C+1) - (g == 0 ? 100 : g); + if (G != not_a_year && G != tG) + goto broken; + G = tG; + } + if (Y < static_cast(year::min()) || Y > static_cast(year::max())) + Y = not_a_year; + bool computed = false; + if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + + (Monday-Thursday) + weeks{V-1} + + (weekday{static_cast(wd)}-Monday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + + weeks{U-1} + + (weekday{static_cast(wd)} - Sunday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + + weeks{W-1} + + (weekday{static_cast(wd)} - Monday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (j != not_a_doy && Y != not_a_year) + { + auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; + if (m == 0) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + j = not_a_doy; + } + auto ymd = year{Y}/m/d; + if (ymd.ok()) + { + if (wd == not_a_weekday) + wd = static_cast((weekday(sys_days(ymd)) - Sunday).count()); + else if (wd != static_cast((weekday(sys_days(ymd)) - Sunday).count())) + goto broken; + if (!computed) + { + if (G != not_a_year || V != not_a_week_num) + { + sys_days sd = ymd; + auto G_trial = year_month_day{sd + days{3}}.year(); + auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + if (sd < start) + { + --G_trial; + if (V != not_a_week_num) + start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + } + if (G != not_a_year && G != static_cast(G_trial)) + goto broken; + if (V != not_a_week_num) + { + auto V_trial = duration_cast(sd - start).count() + 1; + if (V != V_trial) + goto broken; + } + } + if (U != not_a_week_num) + { + auto start = sys_days(Sunday[1]/January/ymd.year()); + auto U_trial = floor(sys_days(ymd) - start).count() + 1; + if (U != U_trial) + goto broken; + } + if (W != not_a_week_num) + { + auto start = sys_days(Monday[1]/January/ymd.year()); + auto W_trial = floor(sys_days(ymd) - start).count() + 1; + if (W != W_trial) + goto broken; + } + } + } + fds.ymd = ymd; + if (I != not_a_hour_12_value) + { + if (!(1 <= I && I <= 12)) + goto broken; + if (p != not_a_ampm) + { + // p is in [0, 1] == [AM, PM] + // Store trial H in I + if (I == 12) + --p; + I += p*12; + // Either set H from I or make sure H and I are consistent + if (H == not_a_hour) + H = I; + else if (I != H) + goto broken; + } + else // p == not_a_ampm + { + // if H, make sure H and I could be consistent + if (H != not_a_hour) + { + if (I == 12) + { + if (H != 0 && H != 12) + goto broken; + } + else if (!(I == H || I == H+12)) + { + goto broken; + } + } + else // I is ambiguous, AM or PM? + goto broken; + } + } + if (H != not_a_hour) + { + fds.has_tod = true; + fds.tod = hh_mm_ss{hours{H}}; + } + if (M != not_a_minute) + { + fds.has_tod = true; + fds.tod.m_ = minutes{M}; + } + if (s != not_a_second) + { + fds.has_tod = true; + fds.tod.s_ = detail::decimal_format_seconds{s}; + } + if (j != not_a_doy) + { + fds.has_tod = true; + fds.tod.h_ += hours{days{j}}; + } + if (wd != not_a_weekday) + fds.wd = weekday{static_cast(wd)}; + if (abbrev != nullptr) + *abbrev = std::move(temp_abbrev); + if (offset != nullptr && temp_offset != not_a_offset) + *offset = temp_offset; + } + return is; + } +broken: + is.setstate(ios::failbit); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, year& y, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.year().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + y = fds.ymd.year(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, month& m, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + m = fds.ymd.month(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, day& d, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.day().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + d = fds.ymd.day(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, weekday& wd, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.wd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + wd = fds.wd; + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, year_month& ym, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + ym = fds.ymd.year()/fds.ymd.month(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, month_day& md, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + md = fds.ymd.month()/fds.ymd.day(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + year_month_day& ymd, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + ymd = fds.ymd; + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + sys_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = typename std::common_type::type; + using detail::round_i; + std::chrono::minutes offset_local{}; + auto offptr = offset ? offset : &offset_local; + fields fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offptr); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(std::ios::failbit); + if (!is.fail()) + tp = round_i(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + local_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = typename std::common_type::type; + using detail::round_i; + fields fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(std::ios::failbit); + if (!is.fail()) + tp = round_i(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + std::chrono::duration& d, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using Duration = std::chrono::duration; + using CT = typename std::common_type::type; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.has_tod) + is.setstate(std::ios::failbit); + if (!is.fail()) + d = std::chrono::duration_cast(fds.tod.to_duration()); + return is; +} + +template , + class Alloc = std::allocator> +struct parse_manip +{ + const std::basic_string format_; + Parsable& tp_; + std::basic_string* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_manip(std::basic_string format, Parsable& tp, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template +std::basic_istream& +operator>>(std::basic_istream& is, + const parse_manip& x) +{ + return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp), + parse_manip{format, tp}) +{ + return {format, tp}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::basic_string& abbrev) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, &abbrev), + parse_manip{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, + std::declval*>(), + &offset), + parse_manip{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, &abbrev, &offset), + parse_manip{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// const CharT* formats + +template +inline +auto +parse(const CharT* format, Parsable& tp) + -> decltype(from_stream(std::declval&>(), format, tp), + parse_manip{format, tp}) +{ + return {format, tp}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, std::basic_string& abbrev) + -> decltype(from_stream(std::declval&>(), format, + tp, &abbrev), + parse_manip{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), format, + tp, std::declval*>(), &offset), + parse_manip{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), format, + tp, &abbrev, &offset), + parse_manip{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// duration streaming + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, + const std::chrono::duration& d) +{ + return os << detail::make_string::from(d.count()) + + detail::get_units(typename Period::type{}); +} + +} // namespace date + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#endif // DATE_H diff --git a/third_party/date/include/date/ios.h b/third_party/date/include/date/ios.h new file mode 100644 index 000000000..ee54b9d08 --- /dev/null +++ b/third_party/date/include/date/ios.h @@ -0,0 +1,50 @@ +// +// ios.h +// DateTimeLib +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Alexander Kormanovsky +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ios_hpp +#define ios_hpp + +#if __APPLE__ +# include +# if TARGET_OS_IPHONE +# include + + namespace date + { + namespace iOSUtils + { + + std::string get_tzdata_path(); + std::string get_current_timezone(); + + } // namespace iOSUtils + } // namespace date + +# endif // TARGET_OS_IPHONE +#else // !__APPLE__ +# define TARGET_OS_IPHONE 0 +#endif // !__APPLE__ +#endif // ios_hpp diff --git a/third_party/date/include/date/islamic.h b/third_party/date/include/date/islamic.h new file mode 100644 index 000000000..82ed6597b --- /dev/null +++ b/third_party/date/include/date/islamic.h @@ -0,0 +1,3031 @@ +#ifndef ISLAMIC_H +#define ISLAMIC_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace islamic +{ + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD14 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + CONSTCD11 weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + islamic::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const islamic::weekday& wd) NOEXCEPT; + + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + islamic::year y_; + islamic::month m_; + +public: + CONSTCD11 year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + islamic::month m_; + islamic::day d_; + +public: + CONSTCD11 month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + islamic::month m_; + +public: + CONSTCD11 explicit month_day_last(const islamic::month& m) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + islamic::month m_; + islamic::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + islamic::month m_; + islamic::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const islamic::month& m, + const islamic::weekday_last& wd) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + islamic::year y_; + islamic::month m_; + islamic::day d_; + +public: + CONSTCD11 year_month_day(const islamic::year& y, const islamic::month& m, + const islamic::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + islamic::year y_; + islamic::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const islamic::year& y, + const islamic::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 islamic::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + islamic::year y_; + islamic::month m_; + islamic::weekday_indexed wdi_; + +public: + CONSTCD11 year_month_weekday(const islamic::year& y, const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + islamic::year y_; + islamic::month m_; + islamic::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const islamic::year& y, const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 islamic::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 islamic::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + switch (static_cast(m)) + { + case 1: + os << "Muharram"; + break; + case 2: + os << "Safar"; + break; + case 3: + os << "Rabi' al-awwal"; + break; + case 4: + os << "Rabi' al-thani"; + break; + case 5: + os << "Jumada al-awwal"; + break; + case 6: + os << "Jumada al-Thani"; + break; + case 7: + os << "Rajab"; + break; + case 8: + os << "Sha'ban"; + break; + case 9: + os << "Ramadan"; + break; + case 10: + os << "Shawwal"; + break; + case 11: + os << "Dhu al-Qi'dah"; + break; + case 12: + os << "Dhu al-Hijjah"; + break; + default: + os << static_cast(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD14 +inline +bool +year::is_leap() const NOEXCEPT +{ + int y = y_ - 1; + const int era = (y >= 0 ? y : y-29) / 30; + const unsigned yoe = static_cast(y - era * 30); + switch (yoe) + { + case 1: + case 4: + case 6: + case 9: + case 12: + case 15: + case 17: + case 20: + case 23: + case 25: + case 28: + return true; + default: + return false; + } +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast(static_cast( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast(x) - static_cast(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + switch (static_cast(wd)) + { + case 0: + os << "al-Aḥad"; + break; + case 1: + os << "al-Ithnayn"; + break; + case 2: + os << "ath-Thulāthā’"; + break; + case 3: + os << "al-Arba‘ā’"; + break; + case 4: + os << "al-Khamīs"; + break; + case 5: + os << "al-Jum‘ah"; + break; + case 6: + os << "as-Sabt"; + break; + default: + os << static_cast(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +islamic::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return islamic::day{static_cast(d)}; +} + +CONSTCD11 +inline +islamic::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return islamic::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA islamic::last_spec last{}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return islamic::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd))) + , index_(static_cast(index)) + {} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline islamic::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const islamic::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline islamic::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline islamic::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA islamic::day d[] = + {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 30_d}; + return m_.ok() && 1_d <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const islamic::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const islamic::year& y, + const islamic::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA islamic::day d[] = + {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d}; + return month() != islamic::month(12) || !y_.is_leap() ? + d[static_cast(month())-1] : 30_d; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const islamic::year& y, const islamic::month& m, + const islamic::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast(y_) - 1; + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era = (y >= 0 ? y : y-29) / 30; + auto const yoe = static_cast(y - era * 30); // [0, 29] + auto const doy = 29*(m-1) + m/2 + d-1; // [0, 354] + auto const doe = yoe * 354 + (11*(yoe+1)+3)/30 + doy; // [0, 10630] + return days{era * 10631 + static_cast(doe) - 492148}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return 1_d <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 492148; + auto const era = (z >= 0 ? z : z - 10630) / 10631; + auto const doe = static_cast(z - era * 10631); // [0, 10630] + auto const yoe = (30*doe + 10646)/10631 - 1; // [0, 29] + auto const y = static_cast(yoe) + era * 30 + 1; + auto const doy = doe - (yoe * 354 + (11*(yoe+1)+3)/30); // [0, 354] + auto const m = (11*doy + 330) / 325; // [1, 12] + auto const d = doy - (29*(m-1) + m/2) + 1; // [1, 30] + return year_month_day{islamic::year{y}, islamic::month(m), islamic::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const islamic::year& y, const islamic::month& m, + const islamic::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - islamic::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = islamic::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - islamic::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const islamic::year& y, + const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (islamic::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace islamic + +#endif // ISLAMIC_H diff --git a/third_party/date/include/date/iso_week.h b/third_party/date/include/date/iso_week.h new file mode 100644 index 000000000..4a0a4a9b9 --- /dev/null +++ b/third_party/date/include/date/iso_week.h @@ -0,0 +1,1751 @@ +#ifndef ISO_WEEK_H +#define ISO_WEEK_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "date.h" + +#include + +namespace iso_week +{ + +// y/wn/wd +// wn/wd/y +// wd/wn/y + +using days = date::days; +using weeks = date::weeks; +using years = date::years; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_week +{ + explicit last_week() = default; +}; + +class weekday; +class weeknum; +class year; + +class year_weeknum; +class year_lastweek; +class weeknum_weekday; +class lastweek_weekday; + +class year_weeknum_weekday; +class year_lastweek_weekday; + +// date composition operators + +CONSTCD11 year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT; +CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT; + +CONSTCD11 year_lastweek operator/(const year& y, last_week wn) NOEXCEPT; + +CONSTCD11 weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT; + +CONSTCD11 lastweek_weekday operator/(const last_week& wn, const weekday& wd) NOEXCEPT; +CONSTCD11 lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT; +CONSTCD11 lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT; + +CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT; + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + CONSTCD11 weekday(date::weekday wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + weekday& operator++() NOEXCEPT; + weekday operator++(int) NOEXCEPT; + weekday& operator--() NOEXCEPT; + weekday operator--(int) NOEXCEPT; + + weekday& operator+=(const days& d) NOEXCEPT; + weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 operator date::weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; + static CONSTCD11 unsigned char to_iso_encoding(unsigned char) NOEXCEPT; + static CONSTCD11 unsigned from_iso_encoding(unsigned) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + year& operator++() NOEXCEPT; + year operator++(int) NOEXCEPT; + year& operator--() NOEXCEPT; + year operator--(int) NOEXCEPT; + + year& operator+=(const years& y) NOEXCEPT; + year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weeknum + +class weeknum +{ + unsigned char wn_; + +public: + explicit CONSTCD11 weeknum(unsigned wn) NOEXCEPT; + + weeknum& operator++() NOEXCEPT; + weeknum operator++(int) NOEXCEPT; + weeknum& operator--() NOEXCEPT; + weeknum operator--(int) NOEXCEPT; + + weeknum& operator+=(const weeks& y) NOEXCEPT; + weeknum& operator-=(const weeks& y) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator< (const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator> (const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT; + +CONSTCD11 weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT; +CONSTCD11 weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT; +CONSTCD11 weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT; +CONSTCD11 weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weeknum& wn); + +// year_weeknum + +class year_weeknum +{ + iso_week::year y_; + iso_week::weeknum wn_; + +public: + CONSTCD11 year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + + year_weeknum& operator+=(const years& dy) NOEXCEPT; + year_weeknum& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; + +CONSTCD11 year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT; +CONSTCD11 year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_weeknum& ym); + +// year_lastweek + +class year_lastweek +{ + iso_week::year y_; + +public: + CONSTCD11 explicit year_lastweek(const iso_week::year& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; + + year_lastweek& operator+=(const years& dy) NOEXCEPT; + year_lastweek& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; + +CONSTCD11 year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT; +CONSTCD11 year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_lastweek& ym); + +// weeknum_weekday + +class weeknum_weekday +{ + iso_week::weeknum wn_; + iso_week::weekday wd_; + +public: + CONSTCD11 weeknum_weekday(const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT; + + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weeknum_weekday& md); + +// lastweek_weekday + +class lastweek_weekday +{ + iso_week::weekday wd_; + +public: + CONSTCD11 explicit lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT; + + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const lastweek_weekday& md); + +// year_lastweek_weekday + +class year_lastweek_weekday +{ + iso_week::year y_; + iso_week::weekday wd_; + +public: + CONSTCD11 year_lastweek_weekday(const iso_week::year& y, + const iso_week::weekday& wd) NOEXCEPT; + + year_lastweek_weekday& operator+=(const years& y) NOEXCEPT; + year_lastweek_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd); + +// class year_weeknum_weekday + +class year_weeknum_weekday +{ + iso_week::year y_; + iso_week::weeknum wn_; + iso_week::weekday wd_; + +public: + CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT; + CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; + CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_weeknum_weekday(const local_days& dp) NOEXCEPT; + + year_weeknum_weekday& operator+=(const years& y) NOEXCEPT; + year_weeknum_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_weeknum_weekday from_days(days dp) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; + +CONSTCD11 year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd); + +//----------------+ +// Implementation | +//----------------+ + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::to_iso_encoding(unsigned char z) NOEXCEPT +{ + return z != 0 ? z : (unsigned char)7; +} + +CONSTCD11 +inline +unsigned +weekday::from_iso_encoding(unsigned z) NOEXCEPT +{ + return z != 7 ? z : 0u; +} + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return to_iso_encoding(static_cast(static_cast( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6))); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(date::weekday wd) NOEXCEPT + : wd_(wd.iso_encoding()) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 8) wd_ = 1; return *this;} +inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 1) wd_ = 7; return *this;} +inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return wd_; +} + +CONSTCD11 +inline +weekday::operator date::weekday() const NOEXCEPT +{ + return date::weekday{from_iso_encoding(unsigned{wd_})}; +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return 1 <= wd_ && wd_ <= 7;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast(x) - static_cast(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x) - 1u) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7) + 1u}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + switch (static_cast(wd)) + { + case 7: + os << "Sun"; + break; + case 1: + os << "Mon"; + break; + case 2: + os << "Tue"; + break; + case 3: + os << "Wed"; + break; + case 4: + os << "Thu"; + break; + case 5: + os << "Fri"; + break; + case 6: + os << "Sat"; + break; + default: + os << static_cast(wd) << " is not a valid weekday"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +inline year& year::operator++() NOEXCEPT {++y_; return *this;} +inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline year& year::operator--() NOEXCEPT {--y_; return *this;} +inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using std::chrono::duration_cast; + static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); + static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); + return sizeof(minutes)*CHAR_BIT < 34 ? + year{1970} + duration_cast(minutes::min()) : + year{std::numeric_limits::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using std::chrono::duration_cast; + static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); + static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); + return sizeof(minutes)*CHAR_BIT < 34 ? + year{1969} + duration_cast(minutes::max()) : + year{std::numeric_limits::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +iso_week::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return iso_week::year(static_cast(y)); +} + +CONSTCD11 +inline +iso_week::weeknum +operator "" _w(unsigned long long wn) NOEXCEPT +{ + return iso_week::weeknum(static_cast(wn)); +} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA iso_week::last_week last{}; + +CONSTDATA iso_week::weekday sun{7u}; +CONSTDATA iso_week::weekday mon{1u}; +CONSTDATA iso_week::weekday tue{2u}; +CONSTDATA iso_week::weekday wed{3u}; +CONSTDATA iso_week::weekday thu{4u}; +CONSTDATA iso_week::weekday fri{5u}; +CONSTDATA iso_week::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weeknum + +CONSTCD11 +inline +weeknum::weeknum(unsigned wn) NOEXCEPT + : wn_(static_cast(wn)) + {} + +inline weeknum& weeknum::operator++() NOEXCEPT {++wn_; return *this;} +inline weeknum weeknum::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline weeknum& weeknum::operator--() NOEXCEPT {--wn_; return *this;} +inline weeknum weeknum::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +inline +weeknum& +weeknum::operator+=(const weeks& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +weeknum& +weeknum::operator-=(const weeks& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline weeknum::operator unsigned() const NOEXCEPT {return wn_;} +CONSTCD11 inline bool weeknum::ok() const NOEXCEPT {return 1 <= wn_ && wn_ <= 53;} + +CONSTCD11 +inline +bool +operator==(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +weeks +operator-(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return weeks{static_cast(static_cast(x)) - + static_cast(static_cast(y))}; +} + +CONSTCD11 +inline +weeknum +operator+(const weeknum& x, const weeks& y) NOEXCEPT +{ + return weeknum{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +weeknum +operator+(const weeks& x, const weeknum& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +weeknum +operator-(const weeknum& x, const weeks& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weeknum& wn) +{ + date::detail::save_ostream _(os); + os << 'W'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(wn); + return os; +} + +// year_weeknum + +CONSTCD11 +inline +year_weeknum::year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT + : y_(y) + , wn_(wn) + {} + +CONSTCD11 inline year year_weeknum::year() const NOEXCEPT {return y_;} +CONSTCD11 inline weeknum year_weeknum::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline bool year_weeknum::ok() const NOEXCEPT +{ + return y_.ok() && 1u <= static_cast(wn_) && wn_ <= (y_/last).weeknum(); +} + +inline +year_weeknum& +year_weeknum::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +inline +year_weeknum& +year_weeknum::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return x.year() == y.year() && x.weeknum() == y.weeknum(); +} + +CONSTCD11 +inline +bool +operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.weeknum() < y.weeknum())); +} + +CONSTCD11 +inline +bool +operator>(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_weeknum +operator+(const year_weeknum& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.weeknum(); +} + +CONSTCD11 +inline +year_weeknum +operator+(const years& dy, const year_weeknum& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_weeknum +operator-(const year_weeknum& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_weeknum& ywn) +{ + return os << ywn.year() << '-' << ywn.weeknum(); +} + + +// year_lastweek + +CONSTCD11 +inline +year_lastweek::year_lastweek(const iso_week::year& y) NOEXCEPT + : y_(y) + {} + +CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;} + +CONSTCD14 +inline +weeknum +year_lastweek::weeknum() const NOEXCEPT +{ + const auto y = date::year{static_cast(y_)}; + const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]); + const auto s1 = sys_days(y/12/date::thu[date::last]); + return iso_week::weeknum(static_cast(date::trunc(s1-s0).count())); +} + +CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();} + +inline +year_lastweek& +year_lastweek::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +inline +year_lastweek& +year_lastweek::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return x.year() == y.year(); +} + +CONSTCD11 +inline +bool +operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return x.year() < y.year(); +} + +CONSTCD11 +inline +bool +operator>(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_lastweek +operator+(const year_lastweek& ym, const years& dy) NOEXCEPT +{ + return year_lastweek{ym.year() + dy}; +} + +CONSTCD11 +inline +year_lastweek +operator+(const years& dy, const year_lastweek& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_lastweek +operator-(const year_lastweek& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_lastweek& ywn) +{ + return os << ywn.year() << "-W last"; +} + +// weeknum_weekday + +CONSTCD11 +inline +weeknum_weekday::weeknum_weekday(const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT + : wn_(wn) + , wd_(wd) + {} + +CONSTCD11 inline weeknum weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline weekday weeknum_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +bool +weeknum_weekday::ok() const NOEXCEPT +{ + return wn_.ok() && wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return x.weeknum() < y.weeknum() ? true + : (x.weeknum() > y.weeknum() ? false + : (static_cast(x.weekday()) < static_cast(y.weekday()))); +} + +CONSTCD11 +inline +bool +operator>(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weeknum_weekday& md) +{ + return os << md.weeknum() << '-' << md.weekday(); +} + +// lastweek_weekday + +CONSTCD11 +inline +lastweek_weekday::lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT + : wd_(wd) + {} + +CONSTCD11 inline weekday lastweek_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +bool +lastweek_weekday::ok() const NOEXCEPT +{ + return wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return static_cast(x.weekday()) < static_cast(y.weekday()); +} + +CONSTCD11 +inline +bool +operator>(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const lastweek_weekday& md) +{ + return os << "W last-" << md.weekday(); +} + +// year_lastweek_weekday + +CONSTCD11 +inline +year_lastweek_weekday::year_lastweek_weekday(const iso_week::year& y, + const iso_week::weekday& wd) NOEXCEPT + : y_(y) + , wd_(wd) + {} + +inline +year_lastweek_weekday& +year_lastweek_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +year_lastweek_weekday& +year_lastweek_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_lastweek_weekday::year() const NOEXCEPT {return y_;} + +CONSTCD14 +inline +weeknum +year_lastweek_weekday::weeknum() const NOEXCEPT +{ + return (y_ / last).weeknum(); +} + +CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +year_lastweek_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days(date::year{static_cast(y_)}/date::dec/date::thu[date::last]) + + (sun - thu) - (sun - wd_); +} + +CONSTCD14 +inline +year_lastweek_weekday::operator local_days() const NOEXCEPT +{ + return local_days(date::year{static_cast(y_)}/date::dec/date::thu[date::last]) + + (sun - thu) - (sun - wd_); +} + +CONSTCD11 +inline +bool +year_lastweek_weekday::ok() const NOEXCEPT +{ + return y_.ok() && wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (static_cast(x.weekday()) < static_cast(y.weekday()))); +} + +CONSTCD11 +inline +bool +operator>(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT +{ + return (ywnwd.year() + y) / last / ywnwd.weekday(); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT +{ + return ywnwd + y; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT +{ + return ywnwd + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd) +{ + return os << ywnwd.year() << "-W last-" << ywnwd.weekday(); +} + +// year_weeknum_weekday + +CONSTCD11 +inline +year_weeknum_weekday::year_weeknum_weekday(const iso_week::year& y, + const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT + : y_(y) + , wn_(wn) + , wd_(wd) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT + : y_(ylwwd.year()) + , wn_(ylwwd.weeknum()) + , wd_(ylwwd.weekday()) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT + : year_weeknum_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const local_days& dp) NOEXCEPT + : year_weeknum_weekday(from_days(dp.time_since_epoch())) + {} + +inline +year_weeknum_weekday& +year_weeknum_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +year_weeknum_weekday& +year_weeknum_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_weeknum_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline weeknum year_weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +year_weeknum_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days(date::year{static_cast(y_)-1}/date::dec/date::thu[date::last]) + + (date::mon - date::thu) + weeks{static_cast(wn_)-1} + (wd_ - mon); +} + +CONSTCD14 +inline +year_weeknum_weekday::operator local_days() const NOEXCEPT +{ + return local_days(date::year{static_cast(y_)-1}/date::dec/date::thu[date::last]) + + (date::mon - date::thu) + weeks{static_cast(wn_)-1} + (wd_ - mon); +} + +CONSTCD14 +inline +bool +year_weeknum_weekday::ok() const NOEXCEPT +{ + return y_.ok() && wd_.ok() && iso_week::weeknum{1u} <= wn_ && wn_ <= year_lastweek{y_}.weeknum(); +} + +CONSTCD14 +inline +year_weeknum_weekday +year_weeknum_weekday::from_days(days d) NOEXCEPT +{ + const auto dp = sys_days{d}; + const auto wd = iso_week::weekday{dp}; + auto y = date::year_month_day{dp + days{3}}.year(); + auto start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); + if (dp < start) + { + --y; + start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); + } + const auto wn = iso_week::weeknum( + static_cast(date::trunc(dp - start).count() + 1)); + return {iso_week::year(static_cast(y)), wn, wd}; +} + +CONSTCD11 +inline +bool +operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.weeknum() < y.weeknum() ? true + : (x.weeknum() > y.weeknum() ? false + : (static_cast(x.weekday()) < static_cast(y.weekday()))))); +} + +CONSTCD11 +inline +bool +operator>(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT +{ + return (ywnwd.year() + y) / ywnwd.weeknum() / ywnwd.weekday(); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT +{ + return ywnwd + y; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT +{ + return ywnwd + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd) +{ + return os << ywnwd.year() << '-' << ywnwd.weeknum() << '-' << ywnwd.weekday(); +} + +// date composition operators + +CONSTCD11 +inline +year_weeknum +operator/(const year& y, const weeknum& wn) NOEXCEPT +{ + return {y, wn}; +} + +CONSTCD11 +inline +year_weeknum +operator/(const year& y, int wn) NOEXCEPT +{ + return y/weeknum(static_cast(wn)); +} + +CONSTCD11 +inline +year_lastweek +operator/(const year& y, last_week) NOEXCEPT +{ + return year_lastweek{y}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weeknum& wn, const weekday& wd) NOEXCEPT +{ + return {wn, wd}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weeknum& wn, int wd) NOEXCEPT +{ + return wn/weekday{static_cast(wd)}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weekday& wd, const weeknum& wn) NOEXCEPT +{ + return wn/wd; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weekday& wd, int wn) NOEXCEPT +{ + return weeknum{static_cast(wn)}/wd; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const last_week&, const weekday& wd) NOEXCEPT +{ + return lastweek_weekday{wd}; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const last_week& wn, int wd) NOEXCEPT +{ + return wn / weekday{static_cast(wd)}; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const weekday& wd, const last_week& wn) NOEXCEPT +{ + return wn / wd; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT +{ + return {ywn.year(), ywn.weeknum(), wd}; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const year_weeknum& ywn, int wd) NOEXCEPT +{ + return ywn / weekday(static_cast(wd)); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT +{ + return {y, wnwd.weeknum(), wnwd.weekday()}; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT +{ + return wnwd / year{y}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT +{ + return {ylw.year(), wd}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const year_lastweek& ylw, int wd) NOEXCEPT +{ + return ylw / weekday(static_cast(wd)); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT +{ + return {y, lwwd.weekday()}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT +{ + return lwwd / year{y}; +} + +} // namespace iso_week + +#endif // ISO_WEEK_H diff --git a/third_party/date/include/date/julian.h b/third_party/date/include/date/julian.h new file mode 100644 index 000000000..d909d69b1 --- /dev/null +++ b/third_party/date/include/date/julian.h @@ -0,0 +1,3052 @@ +#ifndef JULIAN_H +#define JULIAN_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace julian +{ + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + CONSTCD11 weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + julian::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const julian::weekday& wd) NOEXCEPT; + + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + julian::year y_; + julian::month m_; + +public: + CONSTCD11 year_month(const julian::year& y, const julian::month& m) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + julian::month m_; + julian::day d_; + +public: + CONSTCD11 month_day(const julian::month& m, const julian::day& d) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + julian::month m_; + +public: + CONSTCD11 explicit month_day_last(const julian::month& m) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + julian::month m_; + julian::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + julian::month m_; + julian::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const julian::month& m, + const julian::weekday_last& wd) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + julian::year y_; + julian::month m_; + julian::day d_; + +public: + CONSTCD11 year_month_day(const julian::year& y, const julian::month& m, + const julian::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + julian::year y_; + julian::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const julian::year& y, + const julian::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 julian::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + julian::year y_; + julian::month m_; + julian::weekday_indexed wdi_; + +public: + CONSTCD11 year_month_weekday(const julian::year& y, const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + julian::year y_; + julian::month m_; + julian::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const julian::year& y, const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT; + +// CONSTDATA julian::month jan{1}; +// CONSTDATA julian::month feb{2}; +// CONSTDATA julian::month mar{3}; +// CONSTDATA julian::month apr{4}; +// CONSTDATA julian::month may{5}; +// CONSTDATA julian::month jun{6}; +// CONSTDATA julian::month jul{7}; +// CONSTDATA julian::month aug{8}; +// CONSTDATA julian::month sep{9}; +// CONSTDATA julian::month oct{10}; +// CONSTDATA julian::month nov{11}; +// CONSTDATA julian::month dec{12}; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + switch (static_cast(m)) + { + case 1: + os << "Jan"; + break; + case 2: + os << "Feb"; + break; + case 3: + os << "Mar"; + break; + case 4: + os << "Apr"; + break; + case 5: + os << "May"; + break; + case 6: + os << "Jun"; + break; + case 7: + os << "Jul"; + break; + case 8: + os << "Aug"; + break; + case 9: + os << "Sep"; + break; + case 10: + os << "Oct"; + break; + case 11: + os << "Nov"; + break; + case 12: + os << "Dec"; + break; + default: + os << static_cast(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0; +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast(static_cast( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast(x) - static_cast(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + switch (static_cast(wd)) + { + case 0: + os << "Sun"; + break; + case 1: + os << "Mon"; + break; + case 2: + os << "Tue"; + break; + case 3: + os << "Wed"; + break; + case 4: + os << "Thu"; + break; + case 5: + os << "Fri"; + break; + case 6: + os << "Sat"; + break; + default: + os << static_cast(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +julian::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return julian::day{static_cast(d)}; +} + +CONSTCD11 +inline +julian::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return julian::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA julian::last_spec last{}; + +CONSTDATA julian::month jan{1}; +CONSTDATA julian::month feb{2}; +CONSTDATA julian::month mar{3}; +CONSTDATA julian::month apr{4}; +CONSTDATA julian::month may{5}; +CONSTDATA julian::month jun{6}; +CONSTDATA julian::month jul{7}; +CONSTDATA julian::month aug{8}; +CONSTDATA julian::month sep{9}; +CONSTDATA julian::month oct{10}; +CONSTDATA julian::month nov{11}; +CONSTDATA julian::month dec{12}; + +CONSTDATA julian::weekday sun{0u}; +CONSTDATA julian::weekday mon{1u}; +CONSTDATA julian::weekday tue{2u}; +CONSTDATA julian::weekday wed{3u}; +CONSTDATA julian::weekday thu{4u}; +CONSTDATA julian::weekday fri{5u}; +CONSTDATA julian::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return julian::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd))) + , index_(static_cast(index)) + {} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline julian::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const julian::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const julian::year& y, const julian::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const julian::month& m, const julian::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline julian::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline julian::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA julian::day d[] = { + julian::day(31), julian::day(29), julian::day(31), julian::day(30), + julian::day(31), julian::day(30), julian::day(31), julian::day(31), + julian::day(30), julian::day(31), julian::day(30), julian::day(31) + }; + return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const julian::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const julian::year& y, + const julian::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA julian::day d[] = { + julian::day(31), julian::day(28), julian::day(31), julian::day(30), + julian::day(31), julian::day(30), julian::day(31), julian::day(31), + julian::day(30), julian::day(31), julian::day(30), julian::day(31) + }; + return month() != feb || !y_.is_leap() ? d[static_cast(month())-1] : julian::day(29); +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const julian::year& y, const julian::month& m, + const julian::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast(y_) - (m_ <= feb); + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era = (y >= 0 ? y : y-3) / 4; + auto const yoe = static_cast(y - era * 4); // [0, 3] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + doy; // [0, 1460] + return days{era * 1461 + static_cast(doe) - 719470}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return julian::day(1) <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719470; + auto const era = (z >= 0 ? z : z - 1460) / 1461; + auto const doe = static_cast(z - era * 1461); // [0, 1460] + auto const yoe = (doe - doe/1460) / 365; // [0, 3] + auto const y = static_cast(yoe) + era * 4; + auto const doy = doe - 365*yoe; // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{julian::year{y + (m <= 2)}, julian::month(m), julian::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const julian::year& y, const julian::month& m, + const julian::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - julian::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = julian::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - julian::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const julian::year& y, + const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (julian::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace julian + +#endif // JULIAN_H diff --git a/third_party/date/include/date/ptz.h b/third_party/date/include/date/ptz.h new file mode 100644 index 000000000..ebd6e04e9 --- /dev/null +++ b/third_party/date/include/date/ptz.h @@ -0,0 +1,861 @@ +#ifndef PTZ_H +#define PTZ_H + +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This header allows Posix-style time zones as specified for TZ here: +// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 +// +// Posix::time_zone can be constructed with a posix-style string and then used in +// a zoned_time like so: +// +// zoned_time zt{"EST5EDT,M3.2.0,M11.1.0", +// system_clock::now()}; +// or: +// +// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"}; +// zoned_time zt{tz, system_clock::now()}; +// +// If the rule set is missing (everything starting with ','), then the rule is that the +// alternate offset is never enabled. +// +// Note, Posix-style time zones are not recommended for all of the reasons described here: +// https://stackoverflow.com/tags/timezone/info +// +// They are provided here as a non-trivial custom time zone example, and if you really +// have to have Posix time zones, you're welcome to use this one. + +#include "date/tz.h" +#include +#include +#include + +namespace Posix +{ + +namespace detail +{ + +#if HAS_STRING_VIEW + +using string_t = std::string_view; + +#else // !HAS_STRING_VIEW + +using string_t = std::string; + +#endif // !HAS_STRING_VIEW + +class rule; + +void throw_invalid(const string_t& s, unsigned i, const string_t& message); +unsigned read_date(const string_t& s, unsigned i, rule& r); +unsigned read_name(const string_t& s, unsigned i, std::string& name); +unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t); +unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t); +unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, + const string_t& message = string_t{}); + +class rule +{ + enum {off, J, M, N}; + + date::month m_; + date::weekday wd_; + unsigned short n_ : 14; + unsigned short mode_ : 2; + std::chrono::duration time_ = std::chrono::hours{2}; + +public: + rule() : mode_(off) {} + + bool ok() const {return mode_ != off;} + date::local_seconds operator()(date::year y) const; + std::string to_string() const; + + friend std::ostream& operator<<(std::ostream& os, const rule& r); + friend unsigned read_date(const string_t& s, unsigned i, rule& r); + friend bool operator==(const rule& x, const rule& y); +}; + +inline +bool +operator==(const rule& x, const rule& y) +{ + if (x.mode_ != y.mode_) + return false; + switch (x.mode_) + { + case rule::J: + case rule::N: + return x.n_ == y.n_; + case rule::M: + return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_; + default: + return true; + } +} + +inline +bool +operator!=(const rule& x, const rule& y) +{ + return !(x == y); +} + +inline +date::local_seconds +rule::operator()(date::year y) const +{ + using date::local_days; + using date::January; + using date::days; + using date::last; + using sec = std::chrono::seconds; + date::local_seconds t; + switch (mode_) + { + case J: + t = local_days{y/January/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_}; + break; + case M: + t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_}; + break; + case N: + t = local_days{y/January/1} + days{n_} + sec{time_}; + break; + default: + assert(!"rule called with bad mode"); + } + return t; +} + +inline +std::string +rule::to_string() const +{ + using namespace std::chrono; + auto print_offset = [](seconds off) + { + std::string nm; + if (off != hours{2}) + { + date::hh_mm_ss offset{off}; + nm = '/'; + nm += std::to_string(offset.hours().count()); + if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.minutes() < minutes{10}) + nm += '0'; + nm += std::to_string(offset.minutes().count()); + if (offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.seconds() < seconds{10}) + nm += '0'; + nm += std::to_string(offset.seconds().count()); + } + } + } + return nm; + }; + + std::string nm; + switch (mode_) + { + case rule::J: + nm = 'J'; + nm += std::to_string(n_); + break; + case rule::M: + nm = 'M'; + nm += std::to_string(static_cast(m_)); + nm += '.'; + nm += std::to_string(n_); + nm += '.'; + nm += std::to_string(wd_.c_encoding()); + break; + case rule::N: + nm = std::to_string(n_); + break; + default: + break; + } + nm += print_offset(time_); + return nm; +} + +inline +std::ostream& +operator<<(std::ostream& os, const rule& r) +{ + switch (r.mode_) + { + case rule::J: + os << 'J' << r.n_ << date::format(" %T", r.time_); + break; + case rule::M: + if (r.n_ == 5) + os << r.m_/r.wd_[date::last]; + else + os << r.m_/r.wd_[r.n_]; + os << date::format(" %T", r.time_); + break; + case rule::N: + os << r.n_ << date::format(" %T", r.time_); + break; + default: + break; + } + return os; +} + +} // namespace detail + +class time_zone +{ + std::string std_abbrev_; + std::string dst_abbrev_ = {}; + std::chrono::seconds offset_; + std::chrono::seconds save_ = std::chrono::hours{1}; + detail::rule start_rule_; + detail::rule end_rule_; + +public: + explicit time_zone(const detail::string_t& name); + + template + date::sys_info get_info(date::sys_time st) const; + template + date::local_info get_info(date::local_time tp) const; + + template + date::sys_time::type> + to_sys(date::local_time tp) const; + + template + date::sys_time::type> + to_sys(date::local_time tp, date::choose z) const; + + template + date::local_time::type> + to_local(date::sys_time tp) const; + + friend std::ostream& operator<<(std::ostream& os, const time_zone& z); + + const time_zone* operator->() const {return this;} + + std::string name() const; + + friend bool operator==(const time_zone& x, const time_zone& y); + +private: + date::sys_seconds get_start(date::year y) const; + date::sys_seconds get_prev_start(date::year y) const; + date::sys_seconds get_next_start(date::year y) const; + date::sys_seconds get_end(date::year y) const; + date::sys_seconds get_prev_end(date::year y) const; + date::sys_seconds get_next_end(date::year y) const; + date::sys_info contant_offset() const; +}; + +inline +date::sys_seconds +time_zone::get_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_prev_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_next_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_prev_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_next_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()}; +} + +date::sys_info +time_zone::contant_offset() const +{ + using date::year; + using date::sys_info; + using date::sys_days; + using date::January; + using date::December; + using date::last; + sys_info r; + r.begin = sys_days{year::min()/January/1}; + r.end = sys_days{year::max()/December/last}; + r.abbrev = std_abbrev_; + r.offset = offset_; + return r; +} + +inline +time_zone::time_zone(const detail::string_t& s) +{ + using detail::read_name; + using detail::read_signed_time; + using detail::throw_invalid; + auto i = read_name(s, 0, std_abbrev_); + i = read_signed_time(s, i, offset_); + offset_ = -offset_; + if (i != s.size()) + { + i = read_name(s, i, dst_abbrev_); + if (i != s.size()) + { + if (s[i] != ',') + { + i = read_signed_time(s, i, save_); + save_ = -save_ - offset_; + } + if (i != s.size()) + { + if (s[i] != ',') + throw_invalid(s, i, "Expecting end of string or ',' to start rule"); + ++i; + i = read_date(s, i, start_rule_); + if (i == s.size() || s[i] != ',') + throw_invalid(s, i, "Expecting ',' and then the ending rule"); + ++i; + i = read_date(s, i, end_rule_); + if (i != s.size()) + throw_invalid(s, i, "Found unexpected trailing characters"); + } + } + } +} + +template +date::sys_info +time_zone::get_info(date::sys_time st) const +{ + using date::sys_info; + using date::year_month_day; + using date::sys_days; + using date::floor; + using date::ceil; + using date::days; + using date::year; + using date::January; + using date::December; + using date::last; + using std::chrono::minutes; + sys_info r{}; + r.offset = offset_; + if (start_rule_.ok()) + { + auto y = year_month_day{floor(st)}.year(); + auto start = get_start(y); + auto end = get_end(y); + if (start <= end) // (northern hemisphere) + { + if (start <= st && st < end) + { + r.begin = start; + r.end = end; + r.offset += save_; + r.save = ceil(save_); + r.abbrev = dst_abbrev_; + } + else if (st < start) + { + r.begin = get_prev_end(y); + r.end = start; + r.abbrev = std_abbrev_; + } + else // st >= end + { + r.begin = end; + r.end = get_next_start(y); + r.abbrev = std_abbrev_; + } + } + else // end < start (southern hemisphere) + { + if (end <= st && st < start) + { + r.begin = end; + r.end = start; + r.abbrev = std_abbrev_; + } + else if (st < end) + { + r.begin = get_prev_start(y); + r.end = end; + r.offset += save_; + r.save = ceil(save_); + r.abbrev = dst_abbrev_; + } + else // st >= start + { + r.begin = start; + r.end = get_next_end(y); + r.offset += save_; + r.save = ceil(save_); + r.abbrev = dst_abbrev_; + } + } + } + else + r = contant_offset(); + return r; +} + +template +date::local_info +time_zone::get_info(date::local_time tp) const +{ + using date::local_info; + using date::year_month_day; + using date::days; + using date::sys_days; + using date::sys_seconds; + using date::year; + using date::ceil; + using date::January; + using date::December; + using date::last; + using std::chrono::seconds; + using std::chrono::minutes; + local_info r{}; + using date::floor; + if (start_rule_.ok()) + { + auto y = year_month_day{floor(tp)}.year(); + auto start = get_start(y); + auto end = get_end(y); + auto utcs = sys_seconds{floor(tp - offset_).time_since_epoch()}; + auto utcd = sys_seconds{floor(tp - (offset_ + save_)).time_since_epoch()}; + auto northern = start <= end; + if ((utcs < start) != (utcd < start)) + { + if (northern) + r.first.begin = get_prev_end(y); + else + r.first.begin = end; + r.first.end = start; + r.first.offset = offset_; + r.first.abbrev = std_abbrev_; + r.second.begin = start; + if (northern) + r.second.end = end; + else + r.second.end = get_next_end(y); + r.second.abbrev = dst_abbrev_; + r.second.offset = offset_ + save_; + r.second.save = ceil(save_); + r.result = save_ > seconds{0} ? local_info::nonexistent + : local_info::ambiguous; + } + else if ((utcs < end) != (utcd < end)) + { + if (northern) + r.first.begin = start; + else + r.first.begin = get_prev_start(y); + r.first.end = end; + r.first.offset = offset_ + save_; + r.first.save = ceil(save_); + r.first.abbrev = dst_abbrev_; + r.second.begin = end; + if (northern) + r.second.end = get_next_start(y); + else + r.second.end = start; + r.second.abbrev = std_abbrev_; + r.second.offset = offset_; + r.result = save_ > seconds{0} ? local_info::ambiguous + : local_info::nonexistent; + } + else + r.first = get_info(utcs); + } + else + r.first = contant_offset(); + return r; +} + +template +date::sys_time::type> +time_zone::to_sys(date::local_time tp) const +{ + using date::local_info; + using date::sys_time; + using date::ambiguous_local_time; + using date::nonexistent_local_time; + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + throw nonexistent_local_time(tp, i); + else if (i.result == local_info::ambiguous) + throw ambiguous_local_time(tp, i); + return sys_time{tp.time_since_epoch()} - i.first.offset; +} + +template +date::sys_time::type> +time_zone::to_sys(date::local_time tp, date::choose z) const +{ + using date::local_info; + using date::sys_time; + using date::choose; + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + { + return i.first.end; + } + else if (i.result == local_info::ambiguous) + { + if (z == choose::latest) + return sys_time{tp.time_since_epoch()} - i.second.offset; + } + return sys_time{tp.time_since_epoch()} - i.first.offset; +} + +template +date::local_time::type> +time_zone::to_local(date::sys_time tp) const +{ + using date::local_time; + using std::chrono::seconds; + using LT = local_time::type>; + auto i = get_info(tp); + return LT{(tp + i.offset).time_since_epoch()}; +} + +inline +std::ostream& +operator<<(std::ostream& os, const time_zone& z) +{ + using date::operator<<; + os << '{'; + os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_) + << date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}"; + return os; +} + +inline +std::string +time_zone::name() const +{ + using namespace date; + using namespace std::chrono; + auto nm = std_abbrev_; + auto print_offset = [](seconds off) + { + std::string nm; + hh_mm_ss offset{-off}; + if (offset.is_negative()) + nm += '-'; + nm += std::to_string(offset.hours().count()); + if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.minutes() < minutes{10}) + nm += '0'; + nm += std::to_string(offset.minutes().count()); + if (offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.seconds() < seconds{10}) + nm += '0'; + nm += std::to_string(offset.seconds().count()); + } + } + return nm; + }; + nm += print_offset(offset_); + if (!dst_abbrev_.empty()) + { + nm += dst_abbrev_; + if (save_ != hours{1}) + nm += print_offset(offset_+save_); + if (start_rule_.ok()) + { + nm += ','; + nm += start_rule_.to_string(); + nm += ','; + nm += end_rule_.to_string(); + } + } + return nm; +} + +inline +bool +operator==(const time_zone& x, const time_zone& y) +{ + return x.std_abbrev_ == y.std_abbrev_ && + x.dst_abbrev_ == y. dst_abbrev_ && + x.offset_ == y.offset_ && + x.save_ == y.save_ && + x.start_rule_ == y.start_rule_ && + x.end_rule_ == y.end_rule_; +} + +inline +bool +operator!=(const time_zone& x, const time_zone& y) +{ + return !(x == y); +} + +namespace detail +{ + +inline +void +throw_invalid(const string_t& s, unsigned i, const string_t& message) +{ + throw std::runtime_error(std::string("Invalid time_zone initializer.\n") + + std::string(message) + ":\n" + + std::string(s) + '\n' + + "\x1b[1;32m" + + std::string(i, '~') + '^' + + std::string(i < s.size() ? s.size()-i-1 : 0, '~') + + "\x1b[0m"); +} + +inline +unsigned +read_date(const string_t& s, unsigned i, rule& r) +{ + using date::month; + using date::weekday; + if (i == s.size()) + throw_invalid(s, i, "Expected rule but found end of string"); + if (s[i] == 'J') + { + ++i; + unsigned n; + i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]"); + r.mode_ = rule::J; + r.n_ = n; + } + else if (s[i] == 'M') + { + ++i; + unsigned m; + i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]"); + if (i == s.size() || s[i] != '.') + throw_invalid(s, i, "Expected '.' after month"); + ++i; + unsigned n; + i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]"); + if (i == s.size() || s[i] != '.') + throw_invalid(s, i, "Expected '.' after weekday index"); + ++i; + unsigned wd; + i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]"); + r.mode_ = rule::M; + r.m_ = month{m}; + r.wd_ = weekday{wd}; + r.n_ = n; + } + else if (std::isdigit(s[i])) + { + unsigned n; + i = read_unsigned(s, i, 3, n); + r.mode_ = rule::N; + r.n_ = n; + } + else + throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule"); + if (i != s.size() && s[i] == '/') + { + ++i; + std::chrono::seconds t; + i = read_unsigned_time(s, i, t); + r.time_ = t; + } + return i; +} + +inline +unsigned +read_name(const string_t& s, unsigned i, std::string& name) +{ + if (i == s.size()) + throw_invalid(s, i, "Expected a name but found end of string"); + if (s[i] == '<') + { + ++i; + while (true) + { + if (i == s.size()) + throw_invalid(s, i, + "Expected to find closing '>', but found end of string"); + if (s[i] == '>') + break; + name.push_back(s[i]); + ++i; + } + ++i; + } + else + { + while (i != s.size() && std::isalpha(s[i])) + { + name.push_back(s[i]); + ++i; + } + } + if (name.size() < 3) + throw_invalid(s, i, "Found name to be shorter than 3 characters"); + return i; +} + +inline +unsigned +read_signed_time(const string_t& s, unsigned i, + std::chrono::seconds& t) +{ + if (i == s.size()) + throw_invalid(s, i, "Expected to read signed time, but found end of string"); + bool negative = false; + if (s[i] == '-') + { + negative = true; + ++i; + } + else if (s[i] == '+') + ++i; + i = read_unsigned_time(s, i, t); + if (negative) + t = -t; + return i; +} + +inline +unsigned +read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t) +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + if (i == s.size()) + throw_invalid(s, i, "Expected to read unsigned time, but found end of string"); + unsigned x; + i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]"); + t = hours{x}; + if (i != s.size() && s[i] == ':') + { + ++i; + i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]"); + t += minutes{x}; + if (i != s.size() && s[i] == ':') + { + ++i; + i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]"); + t += seconds{x}; + } + } + return i; +} + +inline +unsigned +read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, + const string_t& message) +{ + if (i == s.size() || !std::isdigit(s[i])) + throw_invalid(s, i, message); + u = static_cast(s[i] - '0'); + unsigned count = 1; + for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count) + u = u * 10 + static_cast(s[i] - '0'); + return i; +} + +} // namespace detail + +} // namespace Posix + +namespace date +{ + +template <> +struct zoned_traits +{ + +#if HAS_STRING_VIEW + + static + Posix::time_zone + locate_zone(std::string_view name) + { + return Posix::time_zone{name}; + } + +#else // !HAS_STRING_VIEW + + static + Posix::time_zone + locate_zone(const std::string& name) + { + return Posix::time_zone{name}; + } + + static + Posix::time_zone + locate_zone(const char* name) + { + return Posix::time_zone{name}; + } + +#endif // !HAS_STRING_VIEW + +}; + +} // namespace date + +#endif // PTZ_H diff --git a/third_party/date/include/date/solar_hijri.h b/third_party/date/include/date/solar_hijri.h new file mode 100644 index 000000000..6f6c331be --- /dev/null +++ b/third_party/date/include/date/solar_hijri.h @@ -0,0 +1,3151 @@ +#ifndef SOLAR_HIJRI_H +#define SOLAR_HIJRI_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// Copyright (c) 2019 Asad. Gharighi +// +// Calculations are based on: +// https://www.timeanddate.com/calendar/persian-calendar.html +// and follow style +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace solar_hijri +{ + +namespace internal +{ +static const auto epoch = static_cast(2121446); +static const auto days_in_era = static_cast(1029983); +static const auto years_in_era = static_cast(2820); +static const auto unix_time_shift = static_cast(2440588); +auto const years_in_first_cycle = static_cast(29); +auto const years_in_other_cycles = static_cast(33); +auto const years_in_period = static_cast(128); // 29 + 3*33 +auto const days_in_first_cycle = static_cast(10592); // 28/4 + 29*365 +auto const days_in_other_cycles = static_cast(12053); // 32/4 + 33*365 +auto const days_in_period = static_cast(46751); // days_in_first_cycle + 3*days_in_other_cycles; +} + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + short y_; + +public: + year() = default; + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD14 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + weekday_indexed() = default; + CONSTCD11 weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + solar_hijri::weekday wd_; + +public: + weekday_last() = default; + explicit CONSTCD11 weekday_last(const solar_hijri::weekday& wd) NOEXCEPT; + + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + solar_hijri::year y_; + solar_hijri::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + solar_hijri::month m_; + solar_hijri::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + solar_hijri::month m_; + +public: + month_day_last() = default; + CONSTCD11 explicit month_day_last(const solar_hijri::month& m) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + solar_hijri::month m_; + solar_hijri::weekday_indexed wdi_; +public: + month_weekday() = default; + CONSTCD11 month_weekday(const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + solar_hijri::month m_; + solar_hijri::weekday_last wdl_; + +public: + month_weekday_last() = default; + CONSTCD11 month_weekday_last(const solar_hijri::month& m, + const solar_hijri::weekday_last& wd) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + solar_hijri::year y_; + solar_hijri::month_day_last mdl_; + +public: + year_month_day_last() = default; + CONSTCD11 year_month_day_last(const solar_hijri::year& y, + const solar_hijri::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::weekday_last wdl_; + +public: + year_month_weekday_last() = default; + CONSTCD11 year_month_weekday_last(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 solar_hijri::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 solar_hijri::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + switch (static_cast(m)) + { + case 1: + os << "Farvardin"; + break; + case 2: + os << "Ordibehesht"; + break; + case 3: + os << "Khordad"; + break; + case 4: + os << "Tir"; + break; + case 5: + os << "Mordad"; + break; + case 6: + os << "Shahrivar"; + break; + case 7: + os << "Mehr"; + break; + case 8: + os << "Aban"; + break; + case 9: + os << "Azar"; + break; + case 10: + os << "Dey"; + break; + case 11: + os << "Bahman"; + break; + case 12: + os << "Esfand"; + break; + default: + os << static_cast(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD14 +inline +bool +year::is_leap() const NOEXCEPT +{ + using namespace internal; + auto const y = static_cast(y_)-475; + auto const era_d = static_cast(y >= 0 ? y : y-years_in_era+1) / static_cast(years_in_era); + auto const era = static_cast(era_d); + auto const yoe = static_cast(y - era * years_in_era); + + // Reference: https://www.timeanddate.com/date/iran-leap-year.html + // 29 + 33 + 33 + 33 = 128 + // 22 * 128 + 4 + auto const yoc = (yoe < (22 * 128)) ? ((yoe%128) < 29 ? yoe%128 : (yoe%128 - 29)%33) : yoe - (22 * 128) + 33; + return (yoc != 0 && (yoc%4)==0); +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + auto u = static_cast(z); + return static_cast(z >= -4 ? (u+4) % 7 : u % 7); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd != 7 ? wd : 0)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast(x) - static_cast(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + switch (static_cast(wd)) + { + case 0: + os << "Yekshanbe"; + break; + case 1: + os << "Doshanbe"; + break; + case 2: + os << "Seshanbe"; + break; + case 3: + os << "Chaharshanbe"; + break; + case 4: + os << "Panjshanbe"; + break; + case 5: + os << "Adine"; + break; + case 6: + os << "Shanbe"; + break; + default: + os << static_cast(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +solar_hijri::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return solar_hijri::day{static_cast(d)}; +} + +CONSTCD11 +inline +solar_hijri::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return solar_hijri::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA solar_hijri::last_spec last{}; + +CONSTDATA solar_hijri::month far {1}; +CONSTDATA solar_hijri::month ord {2}; +CONSTDATA solar_hijri::month kho {3}; +CONSTDATA solar_hijri::month tir {4}; +CONSTDATA solar_hijri::month mor {5}; +CONSTDATA solar_hijri::month sha {6}; +CONSTDATA solar_hijri::month meh {7}; +CONSTDATA solar_hijri::month aba {8}; +CONSTDATA solar_hijri::month aza {9}; +CONSTDATA solar_hijri::month dey {10}; +CONSTDATA solar_hijri::month bah {11}; +CONSTDATA solar_hijri::month esf {12}; + +CONSTDATA solar_hijri::month Farvardin {1}; +CONSTDATA solar_hijri::month Ordibehesht {2}; +CONSTDATA solar_hijri::month Khordad {3}; +CONSTDATA solar_hijri::month Tir {4}; +CONSTDATA solar_hijri::month Mordad {5}; +CONSTDATA solar_hijri::month Shahrivar {6}; +CONSTDATA solar_hijri::month Mehr {7}; +CONSTDATA solar_hijri::month Aban {8}; +CONSTDATA solar_hijri::month Azar {9}; +CONSTDATA solar_hijri::month Dey {10}; +CONSTDATA solar_hijri::month Bahman {11}; +CONSTDATA solar_hijri::month Esfand {12}; + +CONSTDATA solar_hijri::weekday yek {0u}; +CONSTDATA solar_hijri::weekday dos {1u}; +CONSTDATA solar_hijri::weekday ses {2u}; +CONSTDATA solar_hijri::weekday cha {3u}; +CONSTDATA solar_hijri::weekday pan {4u}; +CONSTDATA solar_hijri::weekday adi {5u}; +CONSTDATA solar_hijri::weekday shn {6u}; + +CONSTDATA solar_hijri::weekday Yekshanbe {0u}; +CONSTDATA solar_hijri::weekday Doshanbe {1u}; +CONSTDATA solar_hijri::weekday Seshanbe {2u}; +CONSTDATA solar_hijri::weekday Chaharshanbe {3u}; +CONSTDATA solar_hijri::weekday Panjshanbe {4u}; +CONSTDATA solar_hijri::weekday Adine {5u}; +CONSTDATA solar_hijri::weekday Shanbe {6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return solar_hijri::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd))) + , index_(static_cast(index)) + {} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline solar_hijri::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const solar_hijri::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline solar_hijri::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline solar_hijri::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA solar_hijri::day d[] = { + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30) + }; + return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const solar_hijri::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const solar_hijri::year& y, + const solar_hijri::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA solar_hijri::day d[] = { + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29) + }; + return month() != esf || !y_.is_leap() ? + d[static_cast(month()) - 1] : solar_hijri::day(30); +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + + using namespace internal; + auto const y = static_cast(y_) - 475; + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era_d = static_cast(y >= 0 ? y : y-years_in_era+1) / static_cast(years_in_era); + auto const era = static_cast(era_d); + auto const fdoe = static_cast(epoch + era * days_in_era); + auto const yoe = static_cast(y - era * years_in_era); + + auto const period_d = static_cast(yoe/years_in_period); + auto const period = static_cast(period_d); + auto const yop = yoe%years_in_period; + auto const fdop = period*days_in_period; + auto const cycle = yop < 29 ? 0 : static_cast((yop-29)/years_in_other_cycles + 1); + auto const yoc = yop < 29 ? yop : (yop-29)%years_in_other_cycles; + auto const fdoc = cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0; + auto const group = yoc < 1 ? 0 : static_cast((yoc-1) / 4); + auto const yog = static_cast(yoc < 1 ? -1 : (yoc-1) % 4); + auto const fdoyog = group*1461 + (yog+1)*365; + auto const fdoyoe = fdop + fdoc + fdoyog; + + auto const doy = 30*(m-1) + ((m > 6) ? 6 : m-1) + d-1; // [0, 365] + auto const doe = fdoe + fdoyoe + doy; + return days{doe - unix_time_shift}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return solar_hijri::day(1) <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + + using namespace internal; + auto const z = dp.count() + unix_time_shift; + auto const delta = static_cast(z - epoch); + auto const era = static_cast(delta >= 0 ? delta : delta-days_in_era+1) / static_cast(days_in_era); + auto const era_i = static_cast(era); + auto const fdoe = static_cast(epoch + static_cast(era_i * days_in_era)); + + auto const doe_fdoe = z - fdoe; + auto const period = static_cast(doe_fdoe < 22*days_in_period ? doe_fdoe / days_in_period : 22); + auto const dop = doe_fdoe % days_in_period; + auto const cycle = dop < days_in_first_cycle ? 0 : (dop-days_in_first_cycle) / days_in_other_cycles + 1; + auto const doc = dop < days_in_first_cycle ? dop : (dop-days_in_first_cycle) % days_in_other_cycles; + auto const group = doc < 365 && period != 22 ? -1 : static_cast(((doc < 365 ? 365 : doc)-365)/1461); + auto const yog = doc < 365 && period != 22 ? -1 : static_cast( (period != 22 ? ((doc-365 )%1461) : doc)/365); + auto const yoc = group == -1 ? 0 : (period != 22 ? 1 : 0) + group*4 + (yog == 4 ? 3 : yog); + auto const doy = group == -1 ? doc : (period != 22 ? ((yoc-1)%4 == 0 ? (group >= 0 ? (doe_fdoe - + (period*days_in_period) - + (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - + (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) + : 365) + : doe_fdoe - + (period*days_in_period) - + (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - + (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) + : (yog == 4 ? 365 : doe_fdoe - (period*days_in_period) - yog*365)); + auto const yoe = period != 22 ? period*years_in_period + + (cycle > 0 ? years_in_first_cycle + + (cycle-1)*years_in_other_cycles + : 0) + + yoc + : 22*years_in_period + ((yog == 4) ? 3 : yog); + auto const y = static_cast(static_cast(yoe) + 475 + era_i * years_in_era); + auto const m = doy < 186 ? doy/31 + 1 : (doy-186)/30 + 7; // [1, 12] + auto const d = doy - (30*(m-1) + ((m > 6) ? 6 : m-1) - 1); // [1, 31] + + return year_month_day{solar_hijri::year(y), solar_hijri::month(m), solar_hijri::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - solar_hijri::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = solar_hijri::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - solar_hijri::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const solar_hijri::year& y, + const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (solar_hijri::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace solar_hijri + +#endif // SOLAR_HIJRI_H diff --git a/third_party/date/include/date/tz.h b/third_party/date/include/date/tz.h new file mode 100644 index 000000000..492106839 --- /dev/null +++ b/third_party/date/include/date/tz.h @@ -0,0 +1,2792 @@ +#ifndef TZ_H +#define TZ_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2017 Jiangang Zhuang +// Copyright (c) 2017 Aaron Bishop +// Copyright (c) 2017 Tomasz Kamiński +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +// Get more recent database at http://www.iana.org/time-zones + +// The notion of "current timezone" is something the operating system is expected to "just +// know". How it knows this is system specific. It's often a value set by the user at OS +// installation time and recorded by the OS somewhere. On Linux and Mac systems the current +// timezone name is obtained by looking at the name or contents of a particular file on +// disk. On Windows the current timezone name comes from the registry. In either method, +// there is no guarantee that the "native" current timezone name obtained will match any +// of the "Standard" names in this library's "database". On Linux, the names usually do +// seem to match so mapping functions to map from native to "Standard" are typically not +// required. On Windows, the names are never "Standard" so mapping is always required. +// Technically any OS may use the mapping process but currently only Windows does use it. + +#ifndef USE_OS_TZDB +# define USE_OS_TZDB 0 +#endif + +#ifndef HAS_REMOTE_API +# if USE_OS_TZDB == 0 +# ifdef _WIN32 +# define HAS_REMOTE_API 0 +# else +# define HAS_REMOTE_API 1 +# endif +# else // HAS_REMOTE_API makes no since when using the OS timezone database +# define HAS_REMOTE_API 0 +# endif +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wconstant-logical-operand" +#endif + +static_assert(!(USE_OS_TZDB && HAS_REMOTE_API), + "USE_OS_TZDB and HAS_REMOTE_API can not be used together"); + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifndef AUTO_DOWNLOAD +# define AUTO_DOWNLOAD HAS_REMOTE_API +#endif + +static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, + "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); + +#ifndef USE_SHELL_API +# define USE_SHELL_API 1 +#endif + +#if USE_OS_TZDB +# ifdef _WIN32 +# error "USE_OS_TZDB can not be used on Windows" +# endif +#endif + +#ifndef HAS_DEDUCTION_GUIDES +# if __cplusplus >= 201703 +# define HAS_DEDUCTION_GUIDES 1 +# else +# define HAS_DEDUCTION_GUIDES 0 +# endif +#endif // HAS_DEDUCTION_GUIDES + +#include "date.h" + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#include "tz_private.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef DATE_BUILD_DLL +# define DATE_API __declspec(dllexport) +# elif defined(DATE_USE_DLL) +# define DATE_API __declspec(dllimport) +# else +# define DATE_API +# endif +#else +# ifdef DATE_BUILD_DLL +# define DATE_API __attribute__ ((visibility ("default"))) +# else +# define DATE_API +# endif +#endif + +namespace date +{ + +enum class choose {earliest, latest}; + +namespace detail +{ + struct undocumented; + + template + struct nodeduct + { + using type = T; + }; + + template + using nodeduct_t = typename nodeduct::type; +} + +struct sys_info +{ + sys_seconds begin; + sys_seconds end; + std::chrono::seconds offset; + std::chrono::minutes save; + std::string abbrev; +}; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const sys_info& r) +{ + os << r.begin << '\n'; + os << r.end << '\n'; + os << make_time(r.offset) << "\n"; + os << make_time(r.save) << "\n"; + os << r.abbrev << '\n'; + return os; +} + +struct local_info +{ + enum {unique, nonexistent, ambiguous} result; + sys_info first; + sys_info second; +}; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_info& r) +{ + if (r.result == local_info::nonexistent) + os << "nonexistent between\n"; + else if (r.result == local_info::ambiguous) + os << "ambiguous between\n"; + os << r.first; + if (r.result != local_info::unique) + { + os << "and\n"; + os << r.second; + } + return os; +} + +class nonexistent_local_time + : public std::runtime_error +{ +public: + template + nonexistent_local_time(local_time tp, const local_info& i); + +private: + template + static + std::string + make_msg(local_time tp, const local_info& i); +}; + +template +inline +nonexistent_local_time::nonexistent_local_time(local_time tp, + const local_info& i) + : std::runtime_error(make_msg(tp, i)) +{ +} + +template +std::string +nonexistent_local_time::make_msg(local_time tp, const local_info& i) +{ + assert(i.result == local_info::nonexistent); + std::ostringstream os; + os << tp << " is in a gap between\n" + << local_seconds{i.first.end.time_since_epoch()} + i.first.offset << ' ' + << i.first.abbrev << " and\n" + << local_seconds{i.second.begin.time_since_epoch()} + i.second.offset << ' ' + << i.second.abbrev + << " which are both equivalent to\n" + << i.first.end << " UTC"; + return os.str(); +} + +class ambiguous_local_time + : public std::runtime_error +{ +public: + template + ambiguous_local_time(local_time tp, const local_info& i); + +private: + template + static + std::string + make_msg(local_time tp, const local_info& i); +}; + +template +inline +ambiguous_local_time::ambiguous_local_time(local_time tp, const local_info& i) + : std::runtime_error(make_msg(tp, i)) +{ +} + +template +std::string +ambiguous_local_time::make_msg(local_time tp, const local_info& i) +{ + assert(i.result == local_info::ambiguous); + std::ostringstream os; + os << tp << " is ambiguous. It could be\n" + << tp << ' ' << i.first.abbrev << " == " + << tp - i.first.offset << " UTC or\n" + << tp << ' ' << i.second.abbrev << " == " + << tp - i.second.offset << " UTC"; + return os.str(); +} + +class time_zone; + +#if HAS_STRING_VIEW +DATE_API const time_zone* locate_zone(std::string_view tz_name); +#else +DATE_API const time_zone* locate_zone(const std::string& tz_name); +#endif + +DATE_API const time_zone* current_zone(); + +template +struct zoned_traits +{ +}; + +template <> +struct zoned_traits +{ + static + const time_zone* + default_zone() + { + return date::locate_zone("Etc/UTC"); + } + +#if HAS_STRING_VIEW + + static + const time_zone* + locate_zone(std::string_view name) + { + return date::locate_zone(name); + } + +#else // !HAS_STRING_VIEW + + static + const time_zone* + locate_zone(const std::string& name) + { + return date::locate_zone(name); + } + + static + const time_zone* + locate_zone(const char* name) + { + return date::locate_zone(name); + } + +#endif // !HAS_STRING_VIEW +}; + +template +class zoned_time; + +template +bool +operator==(const zoned_time& x, + const zoned_time& y); + +template +class zoned_time +{ +public: + using duration = typename std::common_type::type; + +private: + TimeZonePtr zone_; + sys_time tp_; + +public: +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::default_zone())> +#endif + zoned_time(); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::default_zone())> +#endif + zoned_time(const sys_time& st); + explicit zoned_time(TimeZonePtr z); + +#if HAS_STRING_VIEW + template ::locate_zone(std::string_view())) + >::value + >::type> + explicit zoned_time(std::string_view name); +#else +# if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())) + >::value + >::type> +# endif + explicit zoned_time(const std::string& name); +#endif + + template , + sys_time>::value + >::type> + zoned_time(const zoned_time& zt) NOEXCEPT; + + zoned_time(TimeZonePtr z, const sys_time& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ()->to_sys(local_time{})), + sys_time + >::value + >::type> +#endif + zoned_time(TimeZonePtr z, const local_time& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ()->to_sys(local_time{}, + choose::earliest)), + sys_time + >::value + >::type> +#endif + zoned_time(TimeZonePtr z, const local_time& tp, choose c); + + template , + sys_time>::value + >::type> + zoned_time(TimeZonePtr z, const zoned_time& zt); + + template , + sys_time>::value + >::type> + zoned_time(TimeZonePtr z, const zoned_time& zt, choose); + +#if HAS_STRING_VIEW + + template ::locate_zone(std::string_view())), + sys_time + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t&> st); + + template ::locate_zone(std::string_view())), + local_time + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t&> tp); + + template ::locate_zone(std::string_view())), + local_time, + choose + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t&> tp, choose c); + + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string_view())), + zoned_time + >::value + >::type> + zoned_time(std::string_view name, const zoned_time& zt); + + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string_view())), + zoned_time, + choose + >::value + >::type> + zoned_time(std::string_view name, const zoned_time& zt, choose); + +#else // !HAS_STRING_VIEW + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + sys_time + >::value + >::type> +#endif + zoned_time(const std::string& name, const sys_time& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + sys_time + >::value + >::type> +#endif + zoned_time(const char* name, const sys_time& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + local_time + >::value + >::type> +#endif + zoned_time(const std::string& name, const local_time& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + local_time + >::value + >::type> +#endif + zoned_time(const char* name, const local_time& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + local_time, + choose + >::value + >::type> +#endif + zoned_time(const std::string& name, const local_time& tp, choose c); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template ::locate_zone(std::string())), + local_time, + choose + >::value + >::type> +#endif + zoned_time(const char* name, const local_time& tp, choose c); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string())), + zoned_time + >::value + >::type> +#else + template +#endif + zoned_time(const std::string& name, const zoned_time& zt); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string())), + zoned_time + >::value + >::type> +#else + template +#endif + zoned_time(const char* name, const zoned_time& zt); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string())), + zoned_time, + choose + >::value + >::type> +#else + template +#endif + zoned_time(const std::string& name, const zoned_time& zt, + choose); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template , + sys_time>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits::locate_zone(std::string())), + zoned_time, + choose + >::value + >::type> +#else + template +#endif + zoned_time(const char* name, const zoned_time& zt, + choose); + +#endif // !HAS_STRING_VIEW + + zoned_time& operator=(const sys_time& st); + zoned_time& operator=(const local_time& ut); + + explicit operator sys_time() const; + explicit operator local_time() const; + + TimeZonePtr get_time_zone() const; + local_time get_local_time() const; + sys_time get_sys_time() const; + sys_info get_info() const; + + template + friend + bool + operator==(const zoned_time& x, + const zoned_time& y); + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, + const zoned_time& t); + +private: + template friend class zoned_time; + + template + static + TimeZonePtr2&& + check(TimeZonePtr2&& p); +}; + +using zoned_seconds = zoned_time; + +#if HAS_DEDUCTION_GUIDES + +namespace detail +{ + template + using time_zone_representation = + std::conditional_t + < + std::is_convertible::value, + time_zone const*, + std::remove_cv_t> + >; +} + +zoned_time() + -> zoned_time; + +template +zoned_time(sys_time) + -> zoned_time>; + +template +zoned_time(TimeZonePtrOrName&&) + -> zoned_time>; + +template +zoned_time(TimeZonePtrOrName&&, sys_time) + -> zoned_time, detail::time_zone_representation>; + +template +zoned_time(TimeZonePtrOrName&&, local_time, choose = choose::earliest) + -> zoned_time, detail::time_zone_representation>; + +template +zoned_time(TimeZonePtrOrName&&, zoned_time, choose = choose::earliest) + -> zoned_time, detail::time_zone_representation>; + +#endif // HAS_DEDUCTION_GUIDES + +template +inline +bool +operator==(const zoned_time& x, + const zoned_time& y) +{ + return x.zone_ == y.zone_ && x.tp_ == y.tp_; +} + +template +inline +bool +operator!=(const zoned_time& x, + const zoned_time& y) +{ + return !(x == y); +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + +namespace detail +{ +# if USE_OS_TZDB + struct transition; + struct expanded_ttinfo; +# else // !USE_OS_TZDB + struct zonelet; + class Rule; +# endif // !USE_OS_TZDB +} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +class time_zone +{ +private: + std::string name_; +#if USE_OS_TZDB + std::vector transitions_; + std::vector ttinfos_; +#else // !USE_OS_TZDB + std::vector zonelets_; +#endif // !USE_OS_TZDB + std::unique_ptr adjusted_; + +public: +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + time_zone(time_zone&&) = default; + time_zone& operator=(time_zone&&) = default; +#else // defined(_MSC_VER) && (_MSC_VER < 1900) + time_zone(time_zone&& src); + time_zone& operator=(time_zone&& src); +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + + DATE_API explicit time_zone(const std::string& s, detail::undocumented); + + const std::string& name() const NOEXCEPT; + + template sys_info get_info(sys_time st) const; + template local_info get_info(local_time tp) const; + + template + sys_time::type> + to_sys(local_time tp) const; + + template + sys_time::type> + to_sys(local_time tp, choose z) const; + + template + local_time::type> + to_local(sys_time tp) const; + + friend bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT; + friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; + friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z); + +#if !USE_OS_TZDB + DATE_API void add(const std::string& s); +#endif // !USE_OS_TZDB + +private: + DATE_API sys_info get_info_impl(sys_seconds tp) const; + DATE_API local_info get_info_impl(local_seconds tp) const; + + template + sys_time::type> + to_sys_impl(local_time tp, choose z, std::false_type) const; + template + sys_time::type> + to_sys_impl(local_time tp, choose, std::true_type) const; + +#if USE_OS_TZDB + DATE_API void init() const; + DATE_API void init_impl(); + DATE_API sys_info + load_sys_info(std::vector::const_iterator i) const; + + template + DATE_API void + load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, + std::int32_t tzh_typecnt, std::int32_t tzh_charcnt); +#else // !USE_OS_TZDB + DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const; + DATE_API void adjust_infos(const std::vector& rules); + DATE_API void parse_info(std::istream& in); +#endif // !USE_OS_TZDB +}; + +#if defined(_MSC_VER) && (_MSC_VER < 1900) + +inline +time_zone::time_zone(time_zone&& src) + : name_(std::move(src.name_)) + , zonelets_(std::move(src.zonelets_)) + , adjusted_(std::move(src.adjusted_)) + {} + +inline +time_zone& +time_zone::operator=(time_zone&& src) +{ + name_ = std::move(src.name_); + zonelets_ = std::move(src.zonelets_); + adjusted_ = std::move(src.adjusted_); + return *this; +} + +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + +inline +const std::string& +time_zone::name() const NOEXCEPT +{ + return name_; +} + +template +inline +sys_info +time_zone::get_info(sys_time st) const +{ + return get_info_impl(date::floor(st)); +} + +template +inline +local_info +time_zone::get_info(local_time tp) const +{ + return get_info_impl(date::floor(tp)); +} + +template +inline +sys_time::type> +time_zone::to_sys(local_time tp) const +{ + return to_sys_impl(tp, choose{}, std::true_type{}); +} + +template +inline +sys_time::type> +time_zone::to_sys(local_time tp, choose z) const +{ + return to_sys_impl(tp, z, std::false_type{}); +} + +template +inline +local_time::type> +time_zone::to_local(sys_time tp) const +{ + using LT = local_time::type>; + auto i = get_info(tp); + return LT{(tp + i.offset).time_since_epoch()}; +} + +inline bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ == y.name_;} +inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} + +inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} +inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} +inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} +inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x < y);} + +template +sys_time::type> +time_zone::to_sys_impl(local_time tp, choose z, std::false_type) const +{ + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + { + return i.first.end; + } + else if (i.result == local_info::ambiguous) + { + if (z == choose::latest) + return sys_time{tp.time_since_epoch()} - i.second.offset; + } + return sys_time{tp.time_since_epoch()} - i.first.offset; +} + +template +sys_time::type> +time_zone::to_sys_impl(local_time tp, choose, std::true_type) const +{ + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + throw nonexistent_local_time(tp, i); + else if (i.result == local_info::ambiguous) + throw ambiguous_local_time(tp, i); + return sys_time{tp.time_since_epoch()} - i.first.offset; +} + +#if !USE_OS_TZDB + +class time_zone_link +{ +private: + std::string name_; + std::string target_; +public: + DATE_API explicit time_zone_link(const std::string& s); + + const std::string& name() const {return name_;} + const std::string& target() const {return target_;} + + friend bool operator==(const time_zone_link& x, const time_zone_link& y) {return x.name_ == y.name_;} + friend bool operator< (const time_zone_link& x, const time_zone_link& y) {return x.name_ < y.name_;} + + friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone_link& x); +}; + +using link = time_zone_link; + +inline bool operator!=(const time_zone_link& x, const time_zone_link& y) {return !(x == y);} +inline bool operator> (const time_zone_link& x, const time_zone_link& y) {return y < x;} +inline bool operator<=(const time_zone_link& x, const time_zone_link& y) {return !(y < x);} +inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return !(x < y);} + +#endif // !USE_OS_TZDB + +class leap_second +{ +private: + sys_seconds date_; + +public: +#if USE_OS_TZDB + DATE_API explicit leap_second(const sys_seconds& s, detail::undocumented); +#else + DATE_API explicit leap_second(const std::string& s, detail::undocumented); +#endif + + sys_seconds date() const {return date_;} + + friend bool operator==(const leap_second& x, const leap_second& y) {return x.date_ == y.date_;} + friend bool operator< (const leap_second& x, const leap_second& y) {return x.date_ < y.date_;} + + template + friend + bool + operator==(const leap_second& x, const sys_time& y) + { + return x.date_ == y; + } + + template + friend + bool + operator< (const leap_second& x, const sys_time& y) + { + return x.date_ < y; + } + + template + friend + bool + operator< (const sys_time& x, const leap_second& y) + { + return x < y.date_; + } + + friend DATE_API std::ostream& operator<<(std::ostream& os, const leap_second& x); +}; + +inline bool operator!=(const leap_second& x, const leap_second& y) {return !(x == y);} +inline bool operator> (const leap_second& x, const leap_second& y) {return y < x;} +inline bool operator<=(const leap_second& x, const leap_second& y) {return !(y < x);} +inline bool operator>=(const leap_second& x, const leap_second& y) {return !(x < y);} + +template +inline +bool +operator==(const sys_time& x, const leap_second& y) +{ + return y == x; +} + +template +inline +bool +operator!=(const leap_second& x, const sys_time& y) +{ + return !(x == y); +} + +template +inline +bool +operator!=(const sys_time& x, const leap_second& y) +{ + return !(x == y); +} + +template +inline +bool +operator> (const leap_second& x, const sys_time& y) +{ + return y < x; +} + +template +inline +bool +operator> (const sys_time& x, const leap_second& y) +{ + return y < x; +} + +template +inline +bool +operator<=(const leap_second& x, const sys_time& y) +{ + return !(y < x); +} + +template +inline +bool +operator<=(const sys_time& x, const leap_second& y) +{ + return !(y < x); +} + +template +inline +bool +operator>=(const leap_second& x, const sys_time& y) +{ + return !(x < y); +} + +template +inline +bool +operator>=(const sys_time& x, const leap_second& y) +{ + return !(x < y); +} + +using leap = leap_second; + +#ifdef _WIN32 + +namespace detail +{ + +// The time zone mapping is modelled after this data file: +// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml +// and the field names match the element names from the mapZone element +// of windowsZones.xml. +// The website displays this file here: +// http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html +// The html view is sorted before being displayed but is otherwise the same +// There is a mapping between the os centric view (in this case windows) +// the html displays uses and the generic view the xml file. +// That mapping is this: +// display column "windows" -> xml field "other". +// display column "region" -> xml field "territory". +// display column "tzid" -> xml field "type". +// This structure uses the generic terminology because it could be +// used to to support other os/native name conversions, not just windows, +// and using the same generic names helps retain the connection to the +// origin of the data that we are using. +struct timezone_mapping +{ + timezone_mapping(const char* other, const char* territory, const char* type) + : other(other), territory(territory), type(type) + { + } + timezone_mapping() = default; + std::string other; + std::string territory; + std::string type; +}; + +} // detail + +#endif // _WIN32 + +struct tzdb +{ + std::string version = "unknown"; + std::vector zones; +#if !USE_OS_TZDB + std::vector links; +#endif + std::vector leap_seconds; +#if !USE_OS_TZDB + std::vector rules; +#endif +#ifdef _WIN32 + std::vector mappings; +#endif + tzdb* next = nullptr; + + tzdb() = default; +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + tzdb(tzdb&&) = default; + tzdb& operator=(tzdb&&) = default; +#else // defined(_MSC_VER) && (_MSC_VER < 1900) + tzdb(tzdb&& src) + : version(std::move(src.version)) + , zones(std::move(src.zones)) + , links(std::move(src.links)) + , leap_seconds(std::move(src.leap_seconds)) + , rules(std::move(src.rules)) + , mappings(std::move(src.mappings)) + {} + + tzdb& operator=(tzdb&& src) + { + version = std::move(src.version); + zones = std::move(src.zones); + links = std::move(src.links); + leap_seconds = std::move(src.leap_seconds); + rules = std::move(src.rules); + mappings = std::move(src.mappings); + return *this; + } +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + +#if HAS_STRING_VIEW + const time_zone* locate_zone(std::string_view tz_name) const; +#else + const time_zone* locate_zone(const std::string& tz_name) const; +#endif + const time_zone* current_zone() const; +}; + +using TZ_DB = tzdb; + +DATE_API std::ostream& +operator<<(std::ostream& os, const tzdb& db); + +DATE_API const tzdb& get_tzdb(); + +class tzdb_list +{ + std::atomic head_{nullptr}; + +public: + ~tzdb_list(); + tzdb_list() = default; + tzdb_list(tzdb_list&& x) NOEXCEPT; + + const tzdb& front() const NOEXCEPT {return *head_;} + tzdb& front() NOEXCEPT {return *head_;} + + class const_iterator; + + const_iterator begin() const NOEXCEPT; + const_iterator end() const NOEXCEPT; + + const_iterator cbegin() const NOEXCEPT; + const_iterator cend() const NOEXCEPT; + + const_iterator erase_after(const_iterator p) NOEXCEPT; + + struct undocumented_helper; +private: + void push_front(tzdb* tzdb) NOEXCEPT; +}; + +class tzdb_list::const_iterator +{ + tzdb* p_ = nullptr; + + explicit const_iterator(tzdb* p) NOEXCEPT : p_{p} {} +public: + const_iterator() = default; + + using iterator_category = std::forward_iterator_tag; + using value_type = tzdb; + using reference = const value_type&; + using pointer = const value_type*; + using difference_type = std::ptrdiff_t; + + reference operator*() const NOEXCEPT {return *p_;} + pointer operator->() const NOEXCEPT {return p_;} + + const_iterator& operator++() NOEXCEPT {p_ = p_->next; return *this;} + const_iterator operator++(int) NOEXCEPT {auto t = *this; ++(*this); return t;} + + friend + bool + operator==(const const_iterator& x, const const_iterator& y) NOEXCEPT + {return x.p_ == y.p_;} + + friend + bool + operator!=(const const_iterator& x, const const_iterator& y) NOEXCEPT + {return !(x == y);} + + friend class tzdb_list; +}; + +inline +tzdb_list::const_iterator +tzdb_list::begin() const NOEXCEPT +{ + return const_iterator{head_}; +} + +inline +tzdb_list::const_iterator +tzdb_list::end() const NOEXCEPT +{ + return const_iterator{nullptr}; +} + +inline +tzdb_list::const_iterator +tzdb_list::cbegin() const NOEXCEPT +{ + return begin(); +} + +inline +tzdb_list::const_iterator +tzdb_list::cend() const NOEXCEPT +{ + return end(); +} + +DATE_API tzdb_list& get_tzdb_list(); + +#if !USE_OS_TZDB + +DATE_API const tzdb& reload_tzdb(); +DATE_API void set_install(const std::string& install); + +#endif // !USE_OS_TZDB + +#if HAS_REMOTE_API + +DATE_API std::string remote_version(); +// if provided error_buffer size should be at least CURL_ERROR_SIZE +DATE_API bool remote_download(const std::string& version, char* error_buffer = nullptr); +DATE_API bool remote_install(const std::string& version); + +#endif + +// zoned_time + +namespace detail +{ + +template +inline +T* +to_raw_pointer(T* p) NOEXCEPT +{ + return p; +} + +template +inline +auto +to_raw_pointer(Pointer p) NOEXCEPT + -> decltype(detail::to_raw_pointer(p.operator->())) +{ + return detail::to_raw_pointer(p.operator->()); +} + +} // namespace detail + +template +template +inline +TimeZonePtr2&& +zoned_time::check(TimeZonePtr2&& p) +{ + if (detail::to_raw_pointer(p) == nullptr) + throw std::runtime_error( + "zoned_time constructed with a time zone pointer == nullptr"); + return std::forward(p); +} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time() + : zone_(check(zoned_traits::default_zone())) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const sys_time& st) + : zone_(check(zoned_traits::default_zone())) + , tp_(st) + {} + +template +inline +zoned_time::zoned_time(TimeZonePtr z) + : zone_(check(std::move(z))) + {} + +#if HAS_STRING_VIEW + +template +template +inline +zoned_time::zoned_time(std::string_view name) + : zoned_time(zoned_traits::locate_zone(name)) + {} + +#else // !HAS_STRING_VIEW + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const std::string& name) + : zoned_time(zoned_traits::locate_zone(name)) + {} + +#endif // !HAS_STRING_VIEW + +template +template +inline +zoned_time::zoned_time(const zoned_time& zt) NOEXCEPT + : zone_(zt.zone_) + , tp_(zt.tp_) + {} + +template +inline +zoned_time::zoned_time(TimeZonePtr z, const sys_time& st) + : zone_(check(std::move(z))) + , tp_(st) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(TimeZonePtr z, const local_time& t) + : zone_(check(std::move(z))) + , tp_(zone_->to_sys(t)) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(TimeZonePtr z, const local_time& t, + choose c) + : zone_(check(std::move(z))) + , tp_(zone_->to_sys(t, c)) + {} + +template +template +inline +zoned_time::zoned_time(TimeZonePtr z, + const zoned_time& zt) + : zone_(check(std::move(z))) + , tp_(zt.tp_) + {} + +template +template +inline +zoned_time::zoned_time(TimeZonePtr z, + const zoned_time& zt, choose) + : zoned_time(std::move(z), zt) + {} + +#if HAS_STRING_VIEW + +template +template +inline +zoned_time::zoned_time(std::string_view name, + detail::nodeduct_t&> st) + : zoned_time(zoned_traits::locate_zone(name), st) + {} + +template +template +inline +zoned_time::zoned_time(std::string_view name, + detail::nodeduct_t&> t) + : zoned_time(zoned_traits::locate_zone(name), t) + {} + +template +template +inline +zoned_time::zoned_time(std::string_view name, + detail::nodeduct_t&> t, choose c) + : zoned_time(zoned_traits::locate_zone(name), t, c) + {} + +template +template +inline +zoned_time::zoned_time(std::string_view name, + const zoned_time& zt) + : zoned_time(zoned_traits::locate_zone(name), zt) + {} + +template +template +inline +zoned_time::zoned_time(std::string_view name, + const zoned_time& zt, + choose c) + : zoned_time(zoned_traits::locate_zone(name), zt, c) + {} + +#else // !HAS_STRING_VIEW + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const std::string& name, + const sys_time& st) + : zoned_time(zoned_traits::locate_zone(name), st) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const char* name, + const sys_time& st) + : zoned_time(zoned_traits::locate_zone(name), st) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const std::string& name, + const local_time& t) + : zoned_time(zoned_traits::locate_zone(name), t) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const char* name, + const local_time& t) + : zoned_time(zoned_traits::locate_zone(name), t) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const std::string& name, + const local_time& t, choose c) + : zoned_time(zoned_traits::locate_zone(name), t, c) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#endif +inline +zoned_time::zoned_time(const char* name, + const local_time& t, choose c) + : zoned_time(zoned_traits::locate_zone(name), t, c) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#else +template +#endif +inline +zoned_time::zoned_time(const std::string& name, + const zoned_time& zt) + : zoned_time(zoned_traits::locate_zone(name), zt) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#else +template +#endif +inline +zoned_time::zoned_time(const char* name, + const zoned_time& zt) + : zoned_time(zoned_traits::locate_zone(name), zt) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#else +template +#endif +inline +zoned_time::zoned_time(const std::string& name, + const zoned_time& zt, + choose c) + : zoned_time(zoned_traits::locate_zone(name), zt, c) + {} + +template +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template +#else +template +#endif +inline +zoned_time::zoned_time(const char* name, + const zoned_time& zt, + choose c) + : zoned_time(zoned_traits::locate_zone(name), zt, c) + {} + +#endif // HAS_STRING_VIEW + +template +inline +zoned_time& +zoned_time::operator=(const sys_time& st) +{ + tp_ = st; + return *this; +} + +template +inline +zoned_time& +zoned_time::operator=(const local_time& ut) +{ + tp_ = zone_->to_sys(ut); + return *this; +} + +template +inline +zoned_time::operator local_time::duration>() const +{ + return get_local_time(); +} + +template +inline +zoned_time::operator sys_time::duration>() const +{ + return get_sys_time(); +} + +template +inline +TimeZonePtr +zoned_time::get_time_zone() const +{ + return zone_; +} + +template +inline +local_time::duration> +zoned_time::get_local_time() const +{ + return zone_->to_local(tp_); +} + +template +inline +sys_time::duration> +zoned_time::get_sys_time() const +{ + return tp_; +} + +template +inline +sys_info +zoned_time::get_info() const +{ + return zone_->get_info(tp_); +} + +// make_zoned_time + +inline +zoned_time +make_zoned() +{ + return zoned_time(); +} + +template +inline +zoned_time::type> +make_zoned(const sys_time& tp) +{ + return zoned_time::type>(tp); +} + +template 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class + < + typename std::decay + < + decltype(*detail::to_raw_pointer(std::declval())) + >::type + >{} + >::type +#endif +#endif + > +inline +zoned_time +make_zoned(TimeZonePtr z) +{ + return zoned_time(std::move(z)); +} + +inline +zoned_seconds +make_zoned(const std::string& name) +{ + return zoned_seconds(name); +} + +template 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const local_time& tp) +{ + return zoned_time::type, + TimeZonePtr>(std::move(zone), tp); +} + +template 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const local_time& tp, choose c) +{ + return zoned_time::type, + TimeZonePtr>(std::move(zone), tp, c); +} + +template +inline +zoned_time::type> +make_zoned(const std::string& name, const local_time& tp) +{ + return zoned_time::type>(name, tp); +} + +template +inline +zoned_time::type> +make_zoned(const std::string& name, const local_time& tp, choose c) +{ + return zoned_time::type>(name, tp, c); +} + +template +inline +zoned_time +make_zoned(TimeZonePtr zone, const zoned_time& zt) +{ + return zoned_time(std::move(zone), zt); +} + +template +inline +zoned_time +make_zoned(const std::string& name, const zoned_time& zt) +{ + return zoned_time(name, zt); +} + +template +inline +zoned_time +make_zoned(TimeZonePtr zone, const zoned_time& zt, choose c) +{ + return zoned_time(std::move(zone), zt, c); +} + +template +inline +zoned_time +make_zoned(const std::string& name, const zoned_time& zt, choose c) +{ + return zoned_time(name, zt, c); +} + +template 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const sys_time& st) +{ + return zoned_time::type, + TimeZonePtr>(std::move(zone), st); +} + +template +inline +zoned_time::type> +make_zoned(const std::string& name, const sys_time& st) +{ + return zoned_time::type>(name, st); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const zoned_time& tp) +{ + using duration = typename zoned_time::duration; + using LT = local_time; + auto const st = tp.get_sys_time(); + auto const info = tp.get_time_zone()->get_info(st); + return to_stream(os, fmt, LT{(st+info.offset).time_since_epoch()}, + &info.abbrev, &info.offset); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const zoned_time& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', ' ', '%', 'Z', CharT{}}; + return to_stream(os, fmt, t); +} + +class utc_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + static CONSTDATA bool is_steady = false; + + static time_point now(); + + template + static + std::chrono::time_point::type> + to_sys(const std::chrono::time_point&); + + template + static + std::chrono::time_point::type> + from_sys(const std::chrono::time_point&); + + template + static + std::chrono::time_point::type> + to_local(const std::chrono::time_point&); + + template + static + std::chrono::time_point::type> + from_local(const std::chrono::time_point&); +}; + +template + using utc_time = std::chrono::time_point; + +using utc_seconds = utc_time; + +template +utc_time::type> +utc_clock::from_sys(const sys_time& st) +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + auto const& leaps = get_tzdb().leap_seconds; + auto const lt = std::upper_bound(leaps.begin(), leaps.end(), st); + return utc_time{st.time_since_epoch() + seconds{lt-leaps.begin()}}; +} + +// Return pair +// first is true if ut is during a leap second insertion, otherwise false. +// If ut is during a leap second insertion, that leap second is included in the count +template +std::pair +is_leap_second(date::utc_time const& ut) +{ + using std::chrono::seconds; + using duration = typename std::common_type::type; + auto const& leaps = get_tzdb().leap_seconds; + auto tp = sys_time{ut.time_since_epoch()}; + auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); + auto ds = seconds{lt-leaps.begin()}; + tp -= ds; + auto ls = false; + if (lt > leaps.begin()) + { + if (tp < lt[-1]) + { + if (tp >= lt[-1].date() - seconds{1}) + ls = true; + else + --ds; + } + } + return {ls, ds}; +} + +struct leap_second_info +{ + bool is_leap_second; + std::chrono::seconds elapsed; +}; + +template +leap_second_info +get_leap_second_info(date::utc_time const& ut) +{ + auto p = is_leap_second(ut); + return {p.first, p.second}; +} + +template +sys_time::type> +utc_clock::to_sys(const utc_time& ut) +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + auto ls = is_leap_second(ut); + auto tp = sys_time{ut.time_since_epoch() - ls.second}; + if (ls.first) + tp = floor(tp) + seconds{1} - CD{1}; + return tp; +} + +inline +utc_clock::time_point +utc_clock::now() +{ + return from_sys(std::chrono::system_clock::now()); +} + +template +utc_time::type> +utc_clock::from_local(const local_time& st) +{ + return from_sys(sys_time{st.time_since_epoch()}); +} + +template +local_time::type> +utc_clock::to_local(const utc_time& ut) +{ + using CD = typename std::common_type::type; + return local_time{to_sys(ut).time_since_epoch()}; +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const utc_time& t) +{ + using std::chrono::seconds; + using CT = typename std::common_type::type; + const std::string abbrev("UTC"); + CONSTDATA seconds offset{0}; + auto ls = is_leap_second(t); + auto tp = sys_time{t.time_since_epoch() - ls.second}; + auto const sd = floor(tp); + year_month_day ymd = sd; + auto time = make_time(tp - sys_seconds{sd}); + time.seconds(detail::undocumented{}) += seconds{ls.first}; + fields fds{ymd, time}; + return to_stream(os, fmt, fds, &abbrev, &offset); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const utc_time& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + utc_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using std::chrono::seconds; + using std::chrono::minutes; + using CT = typename std::common_type::type; + minutes offset_local{}; + auto offptr = offset ? offset : &offset_local; + fields fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offptr); + if (!fds.ymd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + { + bool is_60_sec = fds.tod.seconds() == seconds{60}; + if (is_60_sec) + fds.tod.seconds(detail::undocumented{}) -= seconds{1}; + auto tmp = utc_clock::from_sys(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); + if (is_60_sec) + tmp += seconds{1}; + if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) + { + is.setstate(std::ios::failbit); + return is; + } + tp = std::chrono::time_point_cast(tmp); + } + return is; +} + +// tai_clock + +class tai_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + static const bool is_steady = false; + + static time_point now(); + + template + static + std::chrono::time_point::type> + to_utc(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + from_utc(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + to_local(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + from_local(const std::chrono::time_point&) NOEXCEPT; +}; + +template + using tai_time = std::chrono::time_point; + +using tai_seconds = tai_time; + +template +inline +utc_time::type> +tai_clock::to_utc(const tai_time& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + return utc_time{t.time_since_epoch()} - + (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); +} + +template +inline +tai_time::type> +tai_clock::from_utc(const utc_time& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + return tai_time{t.time_since_epoch()} + + (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); +} + +inline +tai_clock::time_point +tai_clock::now() +{ + return from_utc(utc_clock::now()); +} + +template +inline +local_time::type> +tai_clock::to_local(const tai_time& t) NOEXCEPT +{ + using CD = typename std::common_type::type; + return local_time{t.time_since_epoch()} - + (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); +} + +template +inline +tai_time::type> +tai_clock::from_local(const local_time& t) NOEXCEPT +{ + using CD = typename std::common_type::type; + return tai_time{t.time_since_epoch()} + + (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const tai_time& t) +{ + const std::string abbrev("TAI"); + CONSTDATA std::chrono::seconds offset{0}; + return to_stream(os, fmt, tai_clock::to_local(t), &abbrev, &offset); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const tai_time& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + tai_time& tp, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + local_time lp; + from_stream(is, fmt, lp, abbrev, offset); + if (!is.fail()) + tp = tai_clock::from_local(lp); + return is; +} + +// gps_clock + +class gps_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + static const bool is_steady = false; + + static time_point now(); + + template + static + std::chrono::time_point::type> + to_utc(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + from_utc(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + to_local(const std::chrono::time_point&) NOEXCEPT; + + template + static + std::chrono::time_point::type> + from_local(const std::chrono::time_point&) NOEXCEPT; +}; + +template + using gps_time = std::chrono::time_point; + +using gps_seconds = gps_time; + +template +inline +utc_time::type> +gps_clock::to_utc(const gps_time& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + return utc_time{t.time_since_epoch()} + + (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + + seconds{9}); +} + +template +inline +gps_time::type> +gps_clock::from_utc(const utc_time& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type::type; + return gps_time{t.time_since_epoch()} - + (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + + seconds{9}); +} + +inline +gps_clock::time_point +gps_clock::now() +{ + return from_utc(utc_clock::now()); +} + +template +inline +local_time::type> +gps_clock::to_local(const gps_time& t) NOEXCEPT +{ + using CD = typename std::common_type::type; + return local_time{t.time_since_epoch()} + + (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); +} + +template +inline +gps_time::type> +gps_clock::from_local(const local_time& t) NOEXCEPT +{ + using CD = typename std::common_type::type; + return gps_time{t.time_since_epoch()} - + (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); +} + + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const gps_time& t) +{ + const std::string abbrev("GPS"); + CONSTDATA std::chrono::seconds offset{0}; + return to_stream(os, fmt, gps_clock::to_local(t), &abbrev, &offset); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const gps_time& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + gps_time& tp, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + local_time lp; + from_stream(is, fmt, lp, abbrev, offset); + if (!is.fail()) + tp = gps_clock::from_local(lp); + return is; +} + +// clock_time_conversion + +template +struct clock_time_conversion +{}; + +template <> +struct clock_time_conversion +{ + template + CONSTCD14 + sys_time + operator()(const sys_time& st) const + { + return st; + } +}; + +template <> +struct clock_time_conversion +{ + template + CONSTCD14 + utc_time + operator()(const utc_time& ut) const + { + return ut; + } +}; + +template<> +struct clock_time_conversion +{ + template + CONSTCD14 + local_time + operator()(const local_time& lt) const + { + return lt; + } +}; + +template <> +struct clock_time_conversion +{ + template + utc_time::type> + operator()(const sys_time& st) const + { + return utc_clock::from_sys(st); + } +}; + +template <> +struct clock_time_conversion +{ + template + sys_time::type> + operator()(const utc_time& ut) const + { + return utc_clock::to_sys(ut); + } +}; + +template<> +struct clock_time_conversion +{ + template + CONSTCD14 + local_time + operator()(const sys_time& st) const + { + return local_time{st.time_since_epoch()}; + } +}; + +template<> +struct clock_time_conversion +{ + template + CONSTCD14 + sys_time + operator()(const local_time& lt) const + { + return sys_time{lt.time_since_epoch()}; + } +}; + +template<> +struct clock_time_conversion +{ + template + utc_time::type> + operator()(const local_time& lt) const + { + return utc_clock::from_local(lt); + } +}; + +template<> +struct clock_time_conversion +{ + template + local_time::type> + operator()(const utc_time& ut) const + { + return utc_clock::to_local(ut); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + std::chrono::time_point + operator()(const std::chrono::time_point& tp) const + { + return tp; + } +}; + +namespace ctc_detail +{ + +template + using time_point = std::chrono::time_point; + +using std::declval; +using std::chrono::system_clock; + +//Check if TimePoint is time for given clock, +//if not emits hard error +template +struct return_clock_time +{ + using clock_time_point = time_point; + using type = TimePoint; + + static_assert(std::is_same::value, + "time point with appropariate clock shall be returned"); +}; + +// Check if Clock has to_sys method accepting TimePoint with given duration const& and +// returning sys_time. If so has nested type member equal to return type to_sys. +template +struct return_to_sys +{}; + +template +struct return_to_sys + < + Clock, Duration, + decltype(Clock::to_sys(declval const&>()), void()) + > + : return_clock_time + < + system_clock, + decltype(Clock::to_sys(declval const&>())) + > +{}; + +// Similiar to above +template +struct return_from_sys +{}; + +template +struct return_from_sys + < + Clock, Duration, + decltype(Clock::from_sys(declval const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_sys(declval const&>())) + > +{}; + +// Similiar to above +template +struct return_to_utc +{}; + +template +struct return_to_utc + < + Clock, Duration, + decltype(Clock::to_utc(declval const&>()), void()) + > + : return_clock_time + < + utc_clock, + decltype(Clock::to_utc(declval const&>()))> +{}; + +// Similiar to above +template +struct return_from_utc +{}; + +template +struct return_from_utc + < + Clock, Duration, + decltype(Clock::from_utc(declval const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_utc(declval const&>())) + > +{}; + +// Similiar to above +template +struct return_to_local +{}; + +template +struct return_to_local + < + Clock, Duration, + decltype(Clock::to_local(declval const&>()), + void()) + > + : return_clock_time + < + local_t, + decltype(Clock::to_local(declval const&>())) + > +{}; + +// Similiar to above +template +struct return_from_local +{}; + +template +struct return_from_local + < + Clock, Duration, + decltype(Clock::from_local(declval const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_local(declval const&>())) + > +{}; + +} // namespace ctc_detail + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_to_sys::type + operator()(const std::chrono::time_point& tp) const + { + return SrcClock::to_sys(tp); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_from_sys::type + operator()(const sys_time& st) const + { + return DstClock::from_sys(st); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_to_utc::type + operator()(const std::chrono::time_point& tp) const + { + return SrcClock::to_utc(tp); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_from_utc::type + operator()(const utc_time& ut) const + { + return DstClock::from_utc(ut); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_to_local::type + operator()(const std::chrono::time_point& tp) const + { + return SrcClock::to_local(tp); + } +}; + +template +struct clock_time_conversion +{ + template + CONSTCD14 + typename ctc_detail::return_from_local::type + operator()(const local_time& lt) const + { + return DstClock::from_local(lt); + } +}; + +namespace clock_cast_detail +{ + +template + using time_point = std::chrono::time_point; +using std::chrono::system_clock; + +template +CONSTCD14 +auto +conv_clock(const time_point& t) + -> decltype(std::declval>()(t)) +{ + return clock_time_conversion{}(t); +} + +//direct trait conversion, 1st candidate +template +CONSTCD14 +auto +cc_impl(const time_point& t, const time_point*) + -> decltype(conv_clock(t)) +{ + return conv_clock(t); +} + +//conversion through sys, 2nd candidate +template +CONSTCD14 +auto +cc_impl(const time_point& t, const void*) + -> decltype(conv_clock(conv_clock(t))) +{ + return conv_clock(conv_clock(t)); +} + +//conversion through utc, 2nd candidate +template +CONSTCD14 +auto +cc_impl(const time_point& t, const void*) + -> decltype(0, // MSVC_WORKAROUND + conv_clock(conv_clock(t))) +{ + return conv_clock(conv_clock(t)); +} + +//conversion through sys and utc, 3rd candidate +template +CONSTCD14 +auto +cc_impl(const time_point& t, ...) + -> decltype(conv_clock(conv_clock(conv_clock(t)))) +{ + return conv_clock(conv_clock(conv_clock(t))); +} + +//conversion through utc and sys, 3rd candidate +template +CONSTCD14 +auto +cc_impl(const time_point& t, ...) + -> decltype(0, // MSVC_WORKAROUND + conv_clock(conv_clock(conv_clock(t)))) +{ + return conv_clock(conv_clock(conv_clock(t))); +} + +} // namespace clock_cast_detail + +template +CONSTCD14 +auto +clock_cast(const std::chrono::time_point& tp) + -> decltype(clock_cast_detail::cc_impl(tp, &tp)) +{ + return clock_cast_detail::cc_impl(tp, &tp); +} + +// Deprecated API + +template +inline +sys_time::type> +to_sys_time(const utc_time& t) +{ + return utc_clock::to_sys(t); +} + +template +inline +sys_time::type> +to_sys_time(const tai_time& t) +{ + return utc_clock::to_sys(tai_clock::to_utc(t)); +} + +template +inline +sys_time::type> +to_sys_time(const gps_time& t) +{ + return utc_clock::to_sys(gps_clock::to_utc(t)); +} + + +template +inline +utc_time::type> +to_utc_time(const sys_time& t) +{ + return utc_clock::from_sys(t); +} + +template +inline +utc_time::type> +to_utc_time(const tai_time& t) +{ + return tai_clock::to_utc(t); +} + +template +inline +utc_time::type> +to_utc_time(const gps_time& t) +{ + return gps_clock::to_utc(t); +} + + +template +inline +tai_time::type> +to_tai_time(const sys_time& t) +{ + return tai_clock::from_utc(utc_clock::from_sys(t)); +} + +template +inline +tai_time::type> +to_tai_time(const utc_time& t) +{ + return tai_clock::from_utc(t); +} + +template +inline +tai_time::type> +to_tai_time(const gps_time& t) +{ + return tai_clock::from_utc(gps_clock::to_utc(t)); +} + + +template +inline +gps_time::type> +to_gps_time(const sys_time& t) +{ + return gps_clock::from_utc(utc_clock::from_sys(t)); +} + +template +inline +gps_time::type> +to_gps_time(const utc_time& t) +{ + return gps_clock::from_utc(t); +} + +template +inline +gps_time::type> +to_gps_time(const tai_time& t) +{ + return gps_clock::from_utc(tai_clock::to_utc(t)); +} + +} // namespace date + +#endif // TZ_H diff --git a/third_party/date/include/date/tz_private.h b/third_party/date/include/date/tz_private.h new file mode 100644 index 000000000..aec01d045 --- /dev/null +++ b/third_party/date/include/date/tz_private.h @@ -0,0 +1,316 @@ +#ifndef TZ_PRIVATE_H +#define TZ_PRIVATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +#include "tz.h" +#else +#include "date.h" +#include +#endif + +namespace date +{ + +namespace detail +{ + +#if !USE_OS_TZDB + +enum class tz {utc, local, standard}; + +//forward declare to avoid warnings in gcc 6.2 +class MonthDayTime; +std::istream& operator>>(std::istream& is, MonthDayTime& x); +std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); + + +class MonthDayTime +{ +private: + struct pair + { +#if defined(_MSC_VER) && (_MSC_VER < 1900) + pair() : month_day_(date::jan / 1), weekday_(0U) {} + + pair(const date::month_day& month_day, const date::weekday& weekday) + : month_day_(month_day), weekday_(weekday) {} +#endif + + date::month_day month_day_; + date::weekday weekday_; + }; + + enum Type {month_day, month_last_dow, lteq, gteq}; + + Type type_{month_day}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + union U +#else + struct U +#endif + { + date::month_day month_day_; + date::month_weekday_last month_weekday_last_; + pair month_day_weekday_; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + U() : month_day_{date::jan/1} {} +#else + U() : + month_day_(date::jan/1), + month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) + {} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + + U& operator=(const date::month_day& x); + U& operator=(const date::month_weekday_last& x); + U& operator=(const pair& x); + } u; + + std::chrono::hours h_{0}; + std::chrono::minutes m_{0}; + std::chrono::seconds s_{0}; + tz zone_{tz::local}; + +public: + MonthDayTime() = default; + MonthDayTime(local_seconds tp, tz timezone); + MonthDayTime(const date::month_day& md, tz timezone); + + date::day day() const; + date::month month() const; + tz zone() const {return zone_;} + + void canonicalize(date::year y); + + sys_seconds + to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; + sys_days to_sys_days(date::year y) const; + + sys_seconds to_time_point(date::year y) const; + int compare(date::year y, const MonthDayTime& x, date::year yx, + std::chrono::seconds offset, std::chrono::minutes prev_save) const; + + friend std::istream& operator>>(std::istream& is, MonthDayTime& x); + friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); +}; + +// A Rule specifies one or more set of datetimes without using an offset. +// Multiple dates are specified with multiple years. The years in effect +// go from starting_year_ to ending_year_, inclusive. starting_year_ <= +// ending_year_. save_ is in effect for times from the specified time +// onward, including the specified time. When the specified time is +// local, it uses the save_ from the chronologically previous Rule, or if +// there is none, 0. + +//forward declare to avoid warnings in gcc 6.2 +class Rule; +bool operator==(const Rule& x, const Rule& y); +bool operator<(const Rule& x, const Rule& y); +bool operator==(const Rule& x, const date::year& y); +bool operator<(const Rule& x, const date::year& y); +bool operator==(const date::year& x, const Rule& y); +bool operator<(const date::year& x, const Rule& y); +bool operator==(const Rule& x, const std::string& y); +bool operator<(const Rule& x, const std::string& y); +bool operator==(const std::string& x, const Rule& y); +bool operator<(const std::string& x, const Rule& y); +std::ostream& operator<<(std::ostream& os, const Rule& r); + +class Rule +{ +private: + std::string name_; + date::year starting_year_{0}; + date::year ending_year_{0}; + MonthDayTime starting_at_; + std::chrono::minutes save_{0}; + std::string abbrev_; + +public: + Rule() = default; + explicit Rule(const std::string& s); + Rule(const Rule& r, date::year starting_year, date::year ending_year); + + const std::string& name() const {return name_;} + const std::string& abbrev() const {return abbrev_;} + + const MonthDayTime& mdt() const {return starting_at_;} + const date::year& starting_year() const {return starting_year_;} + const date::year& ending_year() const {return ending_year_;} + const std::chrono::minutes& save() const {return save_;} + + static void split_overlaps(std::vector& rules); + + friend bool operator==(const Rule& x, const Rule& y); + friend bool operator<(const Rule& x, const Rule& y); + friend bool operator==(const Rule& x, const date::year& y); + friend bool operator<(const Rule& x, const date::year& y); + friend bool operator==(const date::year& x, const Rule& y); + friend bool operator<(const date::year& x, const Rule& y); + friend bool operator==(const Rule& x, const std::string& y); + friend bool operator<(const Rule& x, const std::string& y); + friend bool operator==(const std::string& x, const Rule& y); + friend bool operator<(const std::string& x, const Rule& y); + + friend std::ostream& operator<<(std::ostream& os, const Rule& r); + +private: + date::day day() const; + date::month month() const; + static void split_overlaps(std::vector& rules, std::size_t i, std::size_t& e); + static bool overlaps(const Rule& x, const Rule& y); + static void split(std::vector& rules, std::size_t i, std::size_t k, + std::size_t& e); +}; + +inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);} +inline bool operator> (const Rule& x, const Rule& y) {return y < x;} +inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);} + +inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);} +inline bool operator> (const Rule& x, const date::year& y) {return y < x;} +inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);} + +inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);} +inline bool operator> (const date::year& x, const Rule& y) {return y < x;} +inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);} + +inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);} +inline bool operator> (const Rule& x, const std::string& y) {return y < x;} +inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);} + +inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);} +inline bool operator> (const std::string& x, const Rule& y) {return y < x;} +inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} + +struct zonelet +{ + enum tag {has_rule, has_save, is_empty}; + + std::chrono::seconds gmtoff_; + tag tag_ = has_rule; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + union U +#else + struct U +#endif + { + std::string rule_; + std::chrono::minutes save_; + + ~U() {} + U() {} + U(const U&) {} + U& operator=(const U&) = delete; + } u; + + std::string format_; + date::year until_year_{0}; + MonthDayTime until_date_; + sys_seconds until_utc_; + local_seconds until_std_; + local_seconds until_loc_; + std::chrono::minutes initial_save_{0}; + std::string initial_abbrev_; + std::pair first_rule_{nullptr, date::year::min()}; + std::pair last_rule_{nullptr, date::year::max()}; + + ~zonelet(); + zonelet(); + zonelet(const zonelet& i); + zonelet& operator=(const zonelet&) = delete; +}; + +#else // USE_OS_TZDB + +struct ttinfo +{ + std::int32_t tt_gmtoff; + unsigned char tt_isdst; + unsigned char tt_abbrind; + unsigned char pad[2]; +}; + +static_assert(sizeof(ttinfo) == 8, ""); + +struct expanded_ttinfo +{ + std::chrono::seconds offset; + std::string abbrev; + bool is_dst; +}; + +struct transition +{ + sys_seconds timepoint; + const expanded_ttinfo* info; + + transition(sys_seconds tp, const expanded_ttinfo* i = nullptr) + : timepoint(tp) + , info(i) + {} + + friend + std::ostream& + operator<<(std::ostream& os, const transition& t) + { + using date::operator<<; + os << t.timepoint << "Z "; + if (t.info->offset >= std::chrono::seconds{0}) + os << '+'; + os << make_time(t.info->offset); + if (t.info->is_dst > 0) + os << " daylight "; + else + os << " standard "; + os << t.info->abbrev; + return os; + } +}; + +#endif // USE_OS_TZDB + +} // namespace detail + +} // namespace date + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#include "tz.h" +#endif + +#endif // TZ_PRIVATE_H diff --git a/third_party/filesystem.BUILD b/third_party/filesystem.BUILD new file mode 100644 index 000000000..2556cada6 --- /dev/null +++ b/third_party/filesystem.BUILD @@ -0,0 +1,9 @@ +cc_library( + name = "filesystem", + hdrs = glob([ + "include/**/*.hpp", + ]), + includes = ["include"], + visibility = ["//visibility:public"], + deps = [], +) \ No newline at end of file diff --git a/third_party/fmtlib.BUILD b/third_party/fmtlib.BUILD new file mode 100644 index 000000000..14b965fb5 --- /dev/null +++ b/third_party/fmtlib.BUILD @@ -0,0 +1,12 @@ +cc_library( + name = "fmtlib", + srcs = glob([ + "fmt/*.cc", + ]), + hdrs = glob([ + "include/fmt/*.h", + ]), + defines = ["FMT_HEADER_ONLY"], + includes = ["include"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/third_party/libuv.BUILD b/third_party/libuv.BUILD new file mode 100644 index 000000000..61f3eb3b1 --- /dev/null +++ b/third_party/libuv.BUILD @@ -0,0 +1,92 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +UV_COMMON_SOURCES = [ + "include/uv/unix.h", + "include/uv/linux.h", + "include/uv/sunos.h", + "include/uv/darwin.h", + "include/uv/bsd.h", + "include/uv/aix.h", + "src/idna.h", + "src/idna.c", + "src/fs-poll.c", + "src/heap-inl.h", + "src/inet.c", + "src/queue.h", + "src/threadpool.c", + "src/timer.c", + "src/strscpy.h", + "src/strscpy.c", + "src/uv-common.c", + "src/uv-common.h", + "src/uv-data-getter-setters.c", + "src/version.c" +] + +UV_UNIX_SOURCES = [ + "src/unix/async.c", + "src/unix/atomic-ops.h", + "src/unix/core.c", + "src/unix/dl.c", + "src/unix/fs.c", + "src/unix/getaddrinfo.c", + "src/unix/getnameinfo.c", + "src/unix/internal.h", + "src/unix/loop.c", + "src/unix/loop-watcher.c", + "src/unix/pipe.c", + "src/unix/poll.c", + "src/unix/process.c", + "src/unix/signal.c", + "src/unix/spinlock.h", + "src/unix/stream.c", + "src/unix/tcp.c", + "src/unix/thread.c", + "src/unix/tty.c", + "src/unix/udp.c", +] + +UV_DARWIN_SOURCES = [ + "src/unix/bsd-ifaddrs.c", + "src/unix/darwin.c", + "src/unix/darwin-proctitle.c", + "src/unix/fsevents.c", + "src/unix/kqueue.c", + "src/unix/proctitle.c", +] + +UV_LINUX_SOURCES = [ + "src/unix/linux-core.c", + "src/unix/linux-inotify.c", + "src/unix/linux-syscalls.c", + "src/unix/linux-syscalls.h", + "src/unix/procfs-exepath.c", + "src/unix/proctitle.c", + "src/unix/sysinfo-loadavg.c", +] + +cc_library( + name = "libuv", + srcs = UV_COMMON_SOURCES + select({ + "@bazel_tools//src/conditions:darwin": UV_UNIX_SOURCES + UV_DARWIN_SOURCES, + # FIXME(ofrobots): Figure out the right way to detect linux. + "//conditions:default": UV_UNIX_SOURCES + UV_LINUX_SOURCES, + }), + hdrs = [ + "src/unix/internal.h", + "include/uv.h", + "include/uv/darwin.h", + "include/uv/errno.h", + "include/uv/threadpool.h", + "include/uv/tree.h", + "include/uv/unix.h", + "include/uv/version.h", + ], + includes = ["include", "src"], + defines = [ + "_GNU_SOURCE", + ], + linkopts = [ + "-ldl", + ], + visibility = ["//visibility:public"], +) diff --git a/third_party/spdlog.BUILD b/third_party/spdlog.BUILD new file mode 100644 index 000000000..debd1ad44 --- /dev/null +++ b/third_party/spdlog.BUILD @@ -0,0 +1,21 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "spdlog", + hdrs = glob([ + "include/**/*.cc", + "include/**/*.h", + ]), + defines = [ + "SPDLOG_FMT_EXTERNAL" + ], + includes = [ + "include" + ], + visibility = [ + "//visibility:public" + ], + deps = [ + "@com_github_fmtlib_fmt//:fmtlib" + ], +) \ No newline at end of file diff --git a/third_party/thread_pool/CMakeLists.txt b/third_party/thread_pool/CMakeLists.txt new file mode 100644 index 000000000..d60e2e21d --- /dev/null +++ b/third_party/thread_pool/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(thread_pool INTERFACE) +target_include_directories(thread_pool INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/third_party/thread_pool/include/ThreadPool.h b/third_party/thread_pool/include/ThreadPool.h new file mode 100644 index 000000000..0c23c04af --- /dev/null +++ b/third_party/thread_pool/include/ThreadPool.h @@ -0,0 +1,92 @@ +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool { +public: + ThreadPool(size_t); + template + auto enqueue(F&& f, Args&&... args) + -> std::future::type>; + ~ThreadPool(); +private: + // need to keep track of threads so we can join them + std::vector< std::thread > workers; + // the task queue + std::queue< std::function > tasks; + + // synchronization + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +// the constructor just launches some amount of workers +inline ThreadPool::ThreadPool(size_t threads) : stop(false) { + for (size_t i = 0; i < threads; ++i) + workers.emplace_back([this] { + for (;;) { + std::function task; + + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); + if (this->stop && this->tasks.empty()) { + return; + } + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + + task(); + } + }); +} + +// add new work item to the pool +template +auto ThreadPool::enqueue(F&& f, Args&&... args) + -> std::future::type> +{ + using return_type = typename std::result_of::type; + + auto task = std::make_shared< std::packaged_task >( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + // don't allow enqueueing after stopping the pool + if(stop) + throw std::runtime_error("enqueue on stopped ThreadPool"); + + tasks.emplace([task](){ (*task)(); }); + } + condition.notify_one(); + return res; +} + +// the destructor joins all threads +inline ThreadPool::~ThreadPool() +{ + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for(std::thread &worker: workers) + worker.join(); +} + +#endif diff --git a/third_party/zlib.BUILD b/third_party/zlib.BUILD new file mode 100644 index 000000000..95ae7c5a8 --- /dev/null +++ b/third_party/zlib.BUILD @@ -0,0 +1,36 @@ +cc_library( + name = "zlib", + srcs = [ + "adler32.c", + "compress.c", + "crc32.c", + "deflate.c", + "infback.c", + "inffast.c", + "inflate.c", + "inftrees.c", + "trees.c", + "uncompr.c", + "zutil.c", + ], + hdrs = [ + "crc32.h", + "deflate.h", + "gzguts.h", + "inffast.h", + "inffixed.h", + "inflate.h", + "inftrees.h", + "trees.h", + "zconf.h", + "zlib.h", + "zutil.h", + ], + includes = [ + ".", + ], + linkstatic = 1, + visibility = [ + "//visibility:public", + ], +) diff --git a/tools/env.sh b/tools/env.sh new file mode 100755 index 000000000..3093dcb84 --- /dev/null +++ b/tools/env.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +export no_grpc_proxy=mq.docker.dev,localhost +export GRPC_TRACE=all +export GRPC_VERBOSITY=INFO +export http_proxy=http://30.57.177.111:3128 +export https_proxy=http://30.57.177.111:3128 \ No newline at end of file From e2551fa17acaaca6af2c1692967c3e2cb4b48d95 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 8 Jul 2021 20:49:03 +0800 Subject: [PATCH 002/133] Fix RetrySendCallback member naming --- src/main/cpp/rocketmq/SendCallbacks.cpp | 13 +++++++++---- src/main/cpp/rocketmq/include/SendCallbacks.h | 10 +++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index 837e6b2a8..02f0f37f8 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -48,8 +48,8 @@ void RetrySendCallback::onSuccess(const SendResult& send_result) { } void RetrySendCallback::onException(const MQException& e) { - if (++retry_ >= max_retry_) { - SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", retry_, max_retry_); + if (++attempt_times_ >= max_attempt_times_) { + SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, max_attempt_times_); callback_->onException(e); delete this; return; @@ -70,8 +70,13 @@ void RetrySendCallback::onException(const MQException& e) { return; } - const std::string& host = candidates_[retry_ % candidates_.size()].serviceAddress(); - SPDLOG_DEBUG("Retry-send message to {} for {} times", host, retry_); + const std::string& host = candidates_[attempt_times_ % candidates_.size()].serviceAddress(); + SPDLOG_DEBUG("Retry-send message to {} for {} times", host, attempt_times_); + + // TODO: Sync target broker-name and partition-id with current partition. + request_.mutable_message()->mutable_system_attribute()->set_partition_id( + candidates_[attempt_times_ % candidates_.size()].getQueueId()); + client->send(host, metadata_, request_, this); } diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index 5905bad94..c08af2b28 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -46,10 +46,10 @@ class AwaitSendCallback : public SendCallback { class RetrySendCallback : public SendCallback { public: RetrySendCallback(std::weak_ptr client_instance, - absl::flat_hash_map metadata, SendMessageRequest request, int max_retry, - SendCallback* callback, std::vector candidates) + absl::flat_hash_map metadata, SendMessageRequest request, + int max_attempt_times, SendCallback* callback, std::vector candidates) : client_instance_(std::move(client_instance)), metadata_(std::move(metadata)), request_(std::move(request)), - max_retry_(max_retry), callback_(callback), candidates_(std::move(candidates)) {} + max_attempt_times_(max_attempt_times), callback_(callback), candidates_(std::move(candidates)) {} void onSuccess(const SendResult& send_result) override; @@ -59,8 +59,8 @@ class RetrySendCallback : public SendCallback { std::weak_ptr client_instance_; absl::flat_hash_map metadata_; SendMessageRequest request_; - int retry_{0}; - int max_retry_; + int attempt_times_{0}; + int max_attempt_times_; SendCallback* callback_; std::vector candidates_; }; From f0cbcf770cbcf2733cd6ad1e10b86c9a8504d694 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 8 Jul 2021 21:00:12 +0800 Subject: [PATCH 003/133] Create main.yml --- .github/workflows/main.yml | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..8d821bc10 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,39 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Setup Bazel + uses: abhinavsingh/setup-bazel@v3 + - name: Use Bazel (Windows only) + if: matrix.os == 'windows' + run: ./bazel.exe -h + - name: Use Bazel + if: matrix.os != 'windows' + run: bazel -h + - name: Compile Targets + run: bazel build //... + - name: Run Unit Test Cases + run: bazel test //src/test/cpp/ut/... From d73199cf9f0c7d5300c4467ed737791562b2f234 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 8 Jul 2021 21:05:33 +0800 Subject: [PATCH 004/133] Set bazel version for github action --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8d821bc10..c849d0545 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,8 @@ jobs: - name: Setup Bazel uses: abhinavsingh/setup-bazel@v3 + with: + version: 4.1.0 - name: Use Bazel (Windows only) if: matrix.os == 'windows' run: ./bazel.exe -h From 4e0048268edbf46045c478350fcf5841b37a0cab Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 8 Jul 2021 21:30:39 +0800 Subject: [PATCH 005/133] Fix deps issue for //src/test/cpp/it:it_topic_publish_info --- src/test/cpp/it/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index 8ed6c5cf1..4cd8bf76f 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -34,6 +34,7 @@ cc_test( ], deps = [ "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], linkstatic = True, From 4cb2d48c1a45fae1ccfa9e998f0dc783a5f79b2e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 10:14:34 +0800 Subject: [PATCH 006/133] Add sha256 for absl and protobuf --- bazel/rocketmq_deps.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index d0ebd634a..749b12584 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -64,6 +64,7 @@ def rocketmq_deps(): if "com_google_protobuf" not in native.existing_rules(): http_archive( name = "com_google_protobuf", + sha256 = "36f81e03a0702f8f935fffd5a486dac1c0fc6d4bae1cd02c7a32448ad6e63bcb", strip_prefix = "protobuf-3.17.2", urls = [ "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.17.2.tar.gz", @@ -102,6 +103,7 @@ def rocketmq_deps(): if "com_google_absl" not in native.existing_rules(): http_archive( name = "com_google_absl", + sha256 = "59b862f50e710277f8ede96f083a5bb8d7c9595376146838b9580be90374ee1f", strip_prefix = "abseil-cpp-20210324.2", urls = [ "https://github.com/abseil/abseil-cpp/archive/refs/tags/20210324.2.tar.gz", From 2b4fbdd4487235927c505ec162297c5459fb3ca5 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 11:21:32 +0800 Subject: [PATCH 007/133] Use bazel-remote --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c849d0545..6082b5c71 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,6 +36,6 @@ jobs: if: matrix.os != 'windows' run: bazel -h - name: Compile Targets - run: bazel build //... + run: bazel build --remote_cache=http://47.253.54.230:9090 //... - name: Run Unit Test Cases - run: bazel test //src/test/cpp/ut/... + run: bazel test --remote_cache=http://47.253.54.230:9090 //src/test/cpp/ut/... \ No newline at end of file From 373c45a2ac6cd70fbe9da39339e5e2725a63d1cc Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 13:41:31 +0800 Subject: [PATCH 008/133] Remove ConfigFileCredentialsProvider unit test for now --- .bazelrc | 10 +++- .github/workflows/main.yml | 4 +- .../ut/rocketmq/CredentialsProviderTest.cpp | 55 ------------------- 3 files changed, 11 insertions(+), 58 deletions(-) diff --git a/.bazelrc b/.bazelrc index cfea7c592..605011e23 100644 --- a/.bazelrc +++ b/.bazelrc @@ -52,4 +52,12 @@ build:ubsan --copt -fsanitize=undefined build:ubsan --copt -O1 build:ubsan --copt -fno-omit-frame-pointer build:ubsan --linkopt -fsanitize=undefined -build:ubsan --linkopt -lubsan \ No newline at end of file +build:ubsan --linkopt -lubsan + +# Use remote cache, --config=remote_cache See https://docs.bazel.build/versions/main/guide.html#--config +build:remote_cache --remote_cache=http://11.122.50.85:9090 +test:remote_cache --remote_cache=http://11.122.50.85:9090 + +# Remote cache for CI. --config=ci_remote_cache +build:ci_remote_cache --remote_cache=http://47.253.54.230:9090 +test:ci_remote_cache --remote_cache=http://47.253.54.230:9090 \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6082b5c71..3cf82310f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,6 +36,6 @@ jobs: if: matrix.os != 'windows' run: bazel -h - name: Compile Targets - run: bazel build --remote_cache=http://47.253.54.230:9090 //... + run: bazel build --config=ci_remote_cache //... - name: Run Unit Test Cases - run: bazel test --remote_cache=http://47.253.54.230:9090 //src/test/cpp/ut/... \ No newline at end of file + run: bazel test --config=ci_remote_cache //src/test/cpp/ut/... \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 80c6aab5b..56017e4bf 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -10,63 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN class CredentialsProviderTest : public testing::Test { -public: - void SetUp() override { - MixAll::homeDirectory(config_file); - config_file.push_back(ghc::filesystem::path::preferred_separator); - config_file.append(ConfigFileCredentialsProvider::credentialFile()); - ghc::filesystem::path config_file_path(config_file); - if (!ghc::filesystem::exists(config_file_path)) { - delete_config_file_ = true; - writeConfigFile(); - } - } - - void TearDown() override { - ghc::filesystem::path config_file_path(config_file); - if (delete_config_file_ && ghc::filesystem::exists(config_file_path)) { - ghc::filesystem::remove(config_file_path); - } - } - -private: - std::string config_file; - bool delete_config_file_{false}; - - void writeConfigFile() { - ghc::filesystem::path config_file_path(config_file); - if (!ghc::filesystem::exists(config_file_path.parent_path())) { - ghc::filesystem::create_directories(config_file_path.parent_path()); - } - - if (!ghc::filesystem::exists(config_file_path)) { - std::ofstream stream; - stream.open(config_file.c_str(), std::ios::binary | std::ios::out | std::ios::app | std::ios::ate); - google::protobuf::Struct root; - auto fields = root.mutable_fields(); - - google::protobuf::Value access_key_value; - access_key_value.set_string_value("abc"); - fields->insert({"AccessKey", access_key_value}); - - google::protobuf::Value access_secret_value; - access_secret_value.set_string_value("def"); - fields->insert({"AccessSecret", access_secret_value}); - - std::string json; - google::protobuf::util::MessageToJsonString(root, &json); - stream.write(json.c_str(), json.length()); - } - } }; -TEST_F(CredentialsProviderTest, testSetUp) { - ConfigFileCredentialsProvider provider; - Credentials credentials = provider.getCredentials(); - ASSERT_FALSE(credentials.accessKey().empty()); - ASSERT_FALSE(credentials.accessSecret().empty()); -} - TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { std::string access_key("abc"); std::string access_secret("def"); From c25b33a6a26c6294544b02d3af83f3de7454b24b Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 14:13:38 +0800 Subject: [PATCH 009/133] Add build badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index be6132828..af3ecf672 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![CI](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml/badge.svg)](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml) ### Introduction MQ products in the market supports two styles of API to acknowledge success of message consumption. From fb4ea7defb0fd3ccc2ba8eff6e6ce6f22476e655 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 16:12:35 +0800 Subject: [PATCH 010/133] Generate coverage --- .bazelrc | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/.bazelrc b/.bazelrc index 605011e23..d7afe86ac 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,28 @@ +# Bazel doesn't need more than 200MB of memory for local build based on memory profiling: +# https://docs.bazel.build/versions/master/skylark/performance.html#memory-profiling +# The default JVM max heapsize is 1/4 of physical memory up to 32GB which could be large +# enough to consume all memory constrained by cgroup in large host. +# Limiting JVM heapsize here to let it do GC more when approaching the limit to +# leave room for compiler/linker. +# The number 2G is chosen heuristically to both support large VM and small VM with RBE. +# Startup options cannot be selected via config. +startup --host_jvm_args=-Xmx4g + +build --host_force_python=PY3 +build --host_javabase=@bazel_tools//tools/jdk:remote_jdk11 +build --javabase=@bazel_tools//tools/jdk:remote_jdk11 + +# https://docs.bazel.build/versions/main/command-line-reference.html#flag--enable_platform_specific_config +# If true, Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is Linux and +# you run bazel build, Bazel picks up lines starting with build:linux. Supported OS identifiers are linux, macos, +# windows, freebsd, and openbsd. Enabling this flag is equivalent to using --config=linux on Linux, +# --config=windows on Windows, etc. +build --enable_platform_specific_config + +build:macos --cxxopt=-std=c++11 + +# Enable position independent code (this is the default on macOS and Windows) +# (Workaround for https://github.com/bazelbuild/rules_foreign_cc/issues/421) build:linux --copt=-fPIC build:linux --cxxopt=-std=c++11 @@ -13,19 +38,48 @@ build --action_env=PATH build --copt=-maes +# Common flags for sanitizers +build:sanitizer --define tcmalloc=disabled +build:sanitizer --linkopt -ldl +build:sanitizer --build_tag_filters=-no_san +build:sanitizer --test_tag_filters=-no_san + +# Common flags for Clang +build:clang --action_env=BAZEL_COMPILER=clang +build:clang --linkopt=-fuse-ld=lld + + # Address sanitizer -# To use it: bazel build --config asan +# To use it: bazel build --config=asan +build:asan --config=sanitizer build:asan --compiler clang build:asan --strip=never build:asan --copt -fsanitize=address -build:asan --copt -DADDRESS_SANITIZER +# ASAN needs -O1 to get reasonable performance. build:asan --copt -O1 +build:asan --copt -fno-optimize-sibling-calls build:asan --copt -g build:asan --copt -fno-omit-frame-pointer build:asan --linkopt -fsanitize=address +build:asan --copt -DADDRESS_SANITIZER=1 +build:asan --copt -D__SANITIZE_ADDRESS__ + +# Clang ASAN/UBSAN +build:clang-asan --config=asan +build:clang-asan --linkopt -fuse-ld=lld + +build:macos-asan --config=asan +# Workaround, see https://github.com/bazelbuild/bazel/issues/6932 +build:macos-asan --copt -Wno-macro-redefined +build:macos-asan --copt -D_FORTIFY_SOURCE=0 +# Workaround, see https://github.com/bazelbuild/bazel/issues/4341 +build:macos-asan --copt -DGRPC_BAZEL_BUILD +# Dynamic link cause issues like: `dyld: malformed mach-o: load commands size (59272) > 32768` +build:macos-asan --dynamic_mode=off # Thread sanitizer -# bazel build --config tsan +# bazel build --config=tsan +build:tsan --config=sanitizer build:tsan --compiler clang build:tsan --strip=never build:tsan --copt -fsanitize=thread @@ -60,4 +114,15 @@ test:remote_cache --remote_cache=http://11.122.50.85:9090 # Remote cache for CI. --config=ci_remote_cache build:ci_remote_cache --remote_cache=http://47.253.54.230:9090 -test:ci_remote_cache --remote_cache=http://47.253.54.230:9090 \ No newline at end of file +test:ci_remote_cache --remote_cache=http://47.253.54.230:9090 + +# Coverage options +coverage --config=coverage +# 1.5x original timeout + 300s for trace merger in all categories +build:coverage --test_timeout=390,750,1500,5700 +build:coverage --define=dynamic_link_tests=true +build:coverage --combined_report=lcov +build:coverage --strategy=TestRunner=sandboxed,local +build:coverage --strategy=CoverageReport=sandboxed,local +build:coverage --collect_code_coverage +build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:]" \ No newline at end of file From 35547320cc71d369396e8379f81e08a3e3f02ce4 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 16:38:31 +0800 Subject: [PATCH 011/133] Add script to calculate coverage --- tools/coverage.sh | 6 ++++++ tools/env.sh | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100755 tools/coverage.sh delete mode 100755 tools/env.sh diff --git a/tools/coverage.sh b/tools/coverage.sh new file mode 100755 index 000000000..e28402db4 --- /dev/null +++ b/tools/coverage.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +TOOLS_DIR=$(dirname "$0") +WORKSPACE_DIR=$(dirname "$TOOLS_DIR") +cd $WORKSPACE_DIR +bazel coverage --config=remote_cache //src/test/cpp/ut/... +genhtml bazel-out/_coverage/_coverage_report.dat \ No newline at end of file diff --git a/tools/env.sh b/tools/env.sh deleted file mode 100755 index 3093dcb84..000000000 --- a/tools/env.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -export no_grpc_proxy=mq.docker.dev,localhost -export GRPC_TRACE=all -export GRPC_VERBOSITY=INFO -export http_proxy=http://30.57.177.111:3128 -export https_proxy=http://30.57.177.111:3128 \ No newline at end of file From e23360002ad889d9abe5a87ebf52a98858322e50 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 16:43:15 +0800 Subject: [PATCH 012/133] Run coverage in CI --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cf82310f..62a6164f8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,4 +38,6 @@ jobs: - name: Compile Targets run: bazel build --config=ci_remote_cache //... - name: Run Unit Test Cases - run: bazel test --config=ci_remote_cache //src/test/cpp/ut/... \ No newline at end of file + run: bazel test --config=ci_remote_cache //src/test/cpp/ut/... + - name: Coverage + run: ./tools/coverage.sh \ No newline at end of file From 02aa5e7aff7fb98358e07692d52ec39824e38e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=A5=9A?= Date: Thu, 15 Jul 2021 19:00:23 +0800 Subject: [PATCH 013/133] Fix compile issue --- bazel-rocketmq-client-cpp | 1 + src/main/cpp/base/UtilAll.cpp | 4 ++-- src/main/cpp/client/mocks/RpcClientMock.cpp | 4 ++-- src/main/cpp/concurrent/CountdownLatch.cpp | 4 ++-- src/test/cpp/ut/rocketmq/Semaphore.cpp | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) create mode 120000 bazel-rocketmq-client-cpp diff --git a/bazel-rocketmq-client-cpp b/bazel-rocketmq-client-cpp new file mode 120000 index 000000000..476412eb2 --- /dev/null +++ b/bazel-rocketmq-client-cpp @@ -0,0 +1 @@ +/home/yangkun.ayk/.cache/bazel/_bazel_yangkun.ayk/dfc3abe54cc318d8d185535bc4ec65fa/execroot/org_apache_rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index d44a46137..35a724b20 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -1,7 +1,7 @@ #include "UtilAll.h" #include "absl/strings/str_split.h" -#include "include/LoggerImpl.h" +#include "LoggerImpl.h" #include "uv.h" #include "zlib.h" @@ -216,4 +216,4 @@ bool UtilAll::uncompress(const std::string& src, std::string& dst) { std::string UtilAll::LOOP_BACK_IP("127.0.0.1"); -} // namespace rocketmq \ No newline at end of file +} // namespace rocketmq diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp index e2d8a6c2c..ed7b07661 100644 --- a/src/main/cpp/client/mocks/RpcClientMock.cpp +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -1,4 +1,4 @@ -#include "include/RpcClientMock.h" +#include "RpcClientMock.h" using namespace testing; @@ -11,4 +11,4 @@ RpcClientMock::RpcClientMock() : completion_queue_(std::make_shared Date: Wed, 21 Jul 2021 16:30:54 +0800 Subject: [PATCH 014/133] Run ci for develop branch --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 62a6164f8..d82a5e2ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ name: CI on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master ] + branches: [ master, develop ] pull_request: - branches: [ master ] + branches: [ master, develop ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: From d10862f212df5221d7cae9e6e0c4da2801f4c309 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 22 Jul 2021 11:10:46 +0800 Subject: [PATCH 015/133] Use secrets to access bazel remote cache --- .github/workflows/main.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d82a5e2ce..bbdf1a5ab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,8 +36,6 @@ jobs: if: matrix.os != 'windows' run: bazel -h - name: Compile Targets - run: bazel build --config=ci_remote_cache //... + run: bazel build --remote_cache=http://${{ secrets.USER_NAME }}:${{ secrets.PASSWORD }}@47.253.54.230:9090 //... - name: Run Unit Test Cases - run: bazel test --config=ci_remote_cache //src/test/cpp/ut/... - - name: Coverage - run: ./tools/coverage.sh \ No newline at end of file + run: bazel test --remote_cache=http://${{ secrets.USER_NAME }}:${{ secrets.PASSWORD }}@47.253.54.230:9090 //src/test/cpp/ut/... \ No newline at end of file From a1bce2096e2fcc60206269726d23e42b88dfe799 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 9 Jul 2021 22:31:40 +0800 Subject: [PATCH 016/133] 1. Refactor to use async mechanism internally 2. Minimize third-party dependencies --- .bazelrc | 3 + api/rocketmq/DefaultMQProducer.h | 5 +- api/rocketmq/LocalTransactionStateChecker.h | 18 + api/rocketmq/MQMessage.h | 2 +- api/rocketmq/Transaction.h | 6 + bazel/rocketmq_deps.bzl | 29 - proto/apache/rocketmq/v1/definition.proto | 3 - proto/apache/rocketmq/v1/service.proto | 5 +- src/main/cpp/admin/AdminClient.cpp | 2 +- src/main/cpp/admin/include/AdminClient.h | 1 - src/main/cpp/base/BUILD.bazel | 24 +- src/main/cpp/base/CMakeLists.txt | 12 +- src/main/cpp/base/GHttpClient.cpp | 161 +++++ src/main/cpp/base/HttpClient.cpp | 67 -- src/main/cpp/base/MQMessage.cpp | 2 +- src/main/cpp/base/MessageSystemFlag.cpp | 13 - src/main/cpp/{client => base}/Metadata.cpp | 0 src/main/cpp/base/MixAll.cpp | 4 +- src/main/cpp/base/RequestIdGenerator.cpp | 44 -- src/main/cpp/base/TopAddressing.cpp | 43 +- src/main/cpp/base/UniqueIdGenerator.cpp | 63 ++ src/main/cpp/base/UtilAll.cpp | 197 +++--- src/main/cpp/base/include/Functional.h | 28 - src/main/cpp/base/include/GHttpClient.h | 72 +++ src/main/cpp/base/include/Histogram.h | 11 +- src/main/cpp/base/include/HttpClient.h | 29 - src/main/cpp/base/include/InvocationContext.h | 58 +- src/main/cpp/base/include/MessageSystemFlag.h | 31 - .../cpp/{client => base}/include/Metadata.h | 0 src/main/cpp/base/include/MixAll.h | 3 +- .../cpp/base/include/RequestIdGenerator.h | 20 - src/main/cpp/base/include/TopAddressing.h | 17 +- src/main/cpp/base/include/UniqueIdGenerator.h | 64 ++ src/main/cpp/base/include/UtilAll.h | 28 +- src/main/cpp/client/BUILD.bazel | 1 + src/main/cpp/client/CMakeLists.txt | 1 - src/main/cpp/client/ClientConfig.cpp | 9 +- src/main/cpp/client/ClientInstance.cpp | 578 ++++++++---------- src/main/cpp/client/RpcClientImpl.cpp | 122 ++-- src/main/cpp/client/Signature.cpp | 6 - src/main/cpp/client/TopicPublishInfo.cpp | 4 +- src/main/cpp/client/include/ClientCallback.h | 13 +- src/main/cpp/client/include/ClientConfig.h | 7 + src/main/cpp/client/include/ClientInstance.h | 115 ++-- .../cpp/client/include/ReceiveMessageResult.h | 29 - src/main/cpp/client/include/RpcClient.h | 2 + src/main/cpp/client/include/RpcClientImpl.h | 2 + .../cpp/client/include/TopicPublishInfo.h | 4 +- .../cpp/client/mocks/include/RpcClientMock.h | 7 +- src/main/cpp/concurrent/BUILD.bazel | 1 + src/main/cpp/concurrent/CountdownLatch.cpp | 4 +- .../cpp/concurrent/include/CountdownLatch.h | 7 +- src/main/cpp/log/BUILD.bazel | 4 + src/main/cpp/log/CMakeLists.txt | 13 +- src/main/cpp/log/Logger.cpp | 5 +- .../cpp/{base => log}/include/LoggerImpl.h | 0 src/main/cpp/rocketmq/BaseImpl.cpp | 281 +++++++-- src/main/cpp/rocketmq/CMakeLists.txt | 1 - .../ConsumeMessageConcurrentlyService.cpp | 14 +- .../rocketmq/ConsumeMessageOrderlyService.cpp | 5 +- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 4 + .../cpp/rocketmq/DefaultMQProducerImpl.cpp | 68 ++- .../rocketmq/DefaultMQPullConsumerImpl.cpp | 11 +- .../rocketmq/DefaultMQPushConsumerImpl.cpp | 111 ++-- .../cpp/rocketmq/MessageClientIDSetter.cpp | 59 -- src/main/cpp/rocketmq/ProcessQueue.cpp | 46 +- src/main/cpp/rocketmq/include/BaseImpl.h | 68 ++- .../rocketmq/include/ClientResourceBundle.h | 21 + .../rocketmq/include/ConsumeMessageService.h | 10 +- .../rocketmq/include/DefaultMQProducerImpl.h | 33 +- .../include/DefaultMQPullConsumerImpl.h | 7 +- .../include/DefaultMQPushConsumerImpl.h | 23 +- .../rocketmq/include/MessageClientIDSetter.h | 81 --- src/main/cpp/rocketmq/include/ProcessQueue.h | 18 - src/main/cpp/scheduler/BUILD.bazel | 9 +- src/main/cpp/scheduler/Scheduler.cpp | 315 +++------- src/main/cpp/scheduler/include/Scheduler.h | 136 +---- src/test/cpp/it/BUILD.bazel | 4 +- src/test/cpp/it/RpcClientIntegrationTest.cpp | 10 +- src/test/cpp/it/RpcClientTest.cpp | 79 ++- src/test/cpp/it/TopAddressingTest.cpp | 126 +++- src/test/cpp/it/TopicPublishInfoTest.cpp | 23 +- src/test/cpp/ut/BUILD | 33 +- src/test/cpp/ut/CMakeLists.txt | 6 +- src/test/cpp/ut/CurlTest.cpp | 29 - src/test/cpp/ut/MacTest.cpp | 84 +++ src/test/cpp/ut/ThreadPoolTest.cpp | 47 -- src/test/cpp/ut/base/BUILD.bazel | 21 +- src/test/cpp/ut/base/CMakeLists.txt | 8 +- src/test/cpp/ut/base/GHttpClientTest.cpp | 51 ++ src/test/cpp/ut/base/HistogramTest.cpp | 5 +- src/test/cpp/ut/base/MixAllTest.cpp | 2 +- .../cpp/ut/base/RequestIdGeneratorTest.cpp | 16 - .../cpp/ut/base/UniqueIdGeneratorTest.cpp | 25 + src/test/cpp/ut/base/UtilAllTest.cpp | 19 - src/test/cpp/ut/client/BUILD.bazel | 29 - src/test/cpp/ut/client/CMakeLists.txt | 17 - src/test/cpp/ut/client/RpcClientTest.cpp | 20 +- src/test/cpp/ut/client/SchedulerTest.cpp | 109 ---- src/test/cpp/ut/concurrent/BUILD.bazel | 12 + src/test/cpp/ut/concurrent/CMakeLists.txt | 7 + .../CountdownLatchTest.cpp | 0 src/test/cpp/ut/grpc/BUILD.bazel | 24 + src/test/cpp/ut/grpc/CMakeLists.txt | 7 + src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp | 34 ++ src/test/cpp/ut/grpc/GrpcTimer.cpp | 95 +++ src/test/cpp/ut/rocketmq/BUILD.bazel | 20 - src/test/cpp/ut/rocketmq/CMakeLists.txt | 7 - .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 53 +- .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 77 ++- .../ut/rocketmq/MessageClientIDSetterTest.cpp | 25 - src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 9 - .../cpp/ut/rocketmq/include/MQClientTest.h | 21 +- src/test/cpp/ut/scheduler/BUILD.bazel | 15 + src/test/cpp/ut/scheduler/CMakeLists.txt | 8 + src/test/cpp/ut/scheduler/SchedulerTest.cpp | 64 ++ tools/coverage.sh | 13 +- 117 files changed, 2346 insertions(+), 2228 deletions(-) create mode 100644 api/rocketmq/LocalTransactionStateChecker.h create mode 100644 src/main/cpp/base/GHttpClient.cpp delete mode 100644 src/main/cpp/base/HttpClient.cpp delete mode 100644 src/main/cpp/base/MessageSystemFlag.cpp rename src/main/cpp/{client => base}/Metadata.cpp (100%) delete mode 100644 src/main/cpp/base/RequestIdGenerator.cpp create mode 100644 src/main/cpp/base/UniqueIdGenerator.cpp delete mode 100644 src/main/cpp/base/include/Functional.h create mode 100644 src/main/cpp/base/include/GHttpClient.h delete mode 100644 src/main/cpp/base/include/HttpClient.h delete mode 100644 src/main/cpp/base/include/MessageSystemFlag.h rename src/main/cpp/{client => base}/include/Metadata.h (100%) delete mode 100644 src/main/cpp/base/include/RequestIdGenerator.h create mode 100644 src/main/cpp/base/include/UniqueIdGenerator.h rename src/main/cpp/{base => log}/include/LoggerImpl.h (100%) delete mode 100644 src/main/cpp/rocketmq/MessageClientIDSetter.cpp create mode 100644 src/main/cpp/rocketmq/include/ClientResourceBundle.h delete mode 100644 src/main/cpp/rocketmq/include/MessageClientIDSetter.h delete mode 100644 src/test/cpp/ut/CurlTest.cpp create mode 100644 src/test/cpp/ut/MacTest.cpp delete mode 100644 src/test/cpp/ut/ThreadPoolTest.cpp create mode 100644 src/test/cpp/ut/base/GHttpClientTest.cpp delete mode 100644 src/test/cpp/ut/base/RequestIdGeneratorTest.cpp create mode 100644 src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp delete mode 100644 src/test/cpp/ut/client/SchedulerTest.cpp create mode 100644 src/test/cpp/ut/concurrent/BUILD.bazel create mode 100644 src/test/cpp/ut/concurrent/CMakeLists.txt rename src/test/cpp/ut/{client => concurrent}/CountdownLatchTest.cpp (100%) create mode 100644 src/test/cpp/ut/grpc/BUILD.bazel create mode 100644 src/test/cpp/ut/grpc/CMakeLists.txt create mode 100644 src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp create mode 100644 src/test/cpp/ut/grpc/GrpcTimer.cpp delete mode 100644 src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp create mode 100644 src/test/cpp/ut/scheduler/BUILD.bazel create mode 100644 src/test/cpp/ut/scheduler/CMakeLists.txt create mode 100644 src/test/cpp/ut/scheduler/SchedulerTest.cpp diff --git a/.bazelrc b/.bazelrc index d7afe86ac..23c73b746 100644 --- a/.bazelrc +++ b/.bazelrc @@ -20,11 +20,14 @@ build --javabase=@bazel_tools//tools/jdk:remote_jdk11 build --enable_platform_specific_config build:macos --cxxopt=-std=c++11 +build:macos --strip=never +build:macos --copt -g # Enable position independent code (this is the default on macOS and Windows) # (Workaround for https://github.com/bazelbuild/rules_foreign_cc/issues/421) build:linux --copt=-fPIC build:linux --cxxopt=-std=c++11 +build:linux --strip=never # We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace. build --define absl=1 diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 8976c7bdf..5a17555b9 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -4,11 +4,12 @@ #include #include "AsyncCallback.h" +#include "CredentialsProvider.h" +#include "LocalTransactionStateChecker.h" #include "Logger.h" #include "MQMessage.h" #include "MQSelector.h" #include "SendResult.h" -#include "CredentialsProvider.h" ROCKETMQ_NAMESPACE_BEGIN @@ -71,6 +72,8 @@ class DefaultMQProducer { void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); + void setNamesrvAddr(const std::string& name_server_address_list); void setGroupName(const std::string& group_name); diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/api/rocketmq/LocalTransactionStateChecker.h new file mode 100644 index 000000000..6d0bef618 --- /dev/null +++ b/api/rocketmq/LocalTransactionStateChecker.h @@ -0,0 +1,18 @@ +#pragma once + +#include "MQMessageExt.h" +#include "Transaction.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class LocalTransactionStateChecker { +public: + virtual ~LocalTransactionStateChecker() = default; + + virtual TransactionState checkLocalTransactionState(const MQMessageExt& message) = 0; +}; + +using LocalTransactionStateCheckerPtr = std::unique_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 544e13798..fe3174ae1 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -47,7 +47,7 @@ class MQMessage { void setBody(const char* data, int len); void setBody(const std::string& body); - int32_t bodyLength() const; + uint32_t bodyLength() const; const std::map& getProperties() const; void setProperties(const std::map& properties); diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index 2f90b18d8..2265498ad 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -1,6 +1,7 @@ #pragma once #include "RocketMQ.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -15,4 +16,9 @@ class Transaction { virtual bool rollback() = 0; }; +enum class TransactionState : int8_t { + COMMIT = 0, + ROLLBACK = 1, +}; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index 749b12584..1b7e8beba 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -31,14 +31,6 @@ def rocketmq_deps(): build_file = "@org_apache_rocketmq//third_party:filesystem.BUILD", ) - if "com_github_progschj_ThreadPool" not in native.existing_rules(): - new_git_repository( - name = "com_github_progschj_ThreadPool", - remote = "https://github.com/progschj/ThreadPool.git", - branch = "master", - build_file = "@org_apache_rocketmq//third_party:ThreadPool.BUILD" - ) - if "com_github_gabime_spdlog" not in native.existing_rules(): http_archive( name = "com_github_gabime_spdlog", @@ -79,27 +71,6 @@ def rocketmq_deps(): urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/3.1.1.tar.gz"], ) - if "curl" not in native.existing_rules(): - http_archive( - name = "curl", - strip_prefix = "curl-7.73.0", - sha256 = "ba98332752257b47b9dea6d8c0ad25ec1745c20424f1dd3ff2c99ab59e97cf91", - urls = ["https://curl.haxx.se/download/curl-7.73.0.tar.gz"], - build_file = "@org_apache_rocketmq//third_party:curl.BUILD", - ) - - if "libuv" not in native.existing_rules(): - http_archive( - name = "libuv", - sha256 = "dfb4fe1ff0b47340978490a14bf253475159ecfcbad46ab2a350c78f9ce3360f", - strip_prefix = "libuv-15ae750151ac9341e5945eb38f8982d59fb99201", - urls = [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/libuv/libuv/archive/15ae750151ac9341e5945eb38f8982d59fb99201.tar.gz", - "https://github.com/libuv/libuv/archive/15ae750151ac9341e5945eb38f8982d59fb99201.tar.gz", - ], - build_file = "@org_apache_rocketmq//third_party:libuv.BUILD", - ) - if "com_google_absl" not in native.existing_rules(): http_archive( name = "com_google_absl", diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index b4879d01d..6a82ae38a 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -252,9 +252,6 @@ message SystemAttribute { // Trace context. string trace_context = 20; - - // Correlated transaction - string transaction_id = 21; } message Message { diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 6e079015d..8a1ef2468 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -183,7 +183,7 @@ message ClientResourceBundle { } message GenericPollingRequest { - repeated ClientResourceBundle bundles = 1; + ClientResourceBundle client_resource_bundle = 1; } message GenericPollingResponse { @@ -207,7 +207,8 @@ message VerifyMessageConsumptionResponse { } message ResolveOrphanedTransactionRequest { - repeated Message orphaned_transactional_messages = 1; + Message orphaned_transactional_message = 1; + string transaction_id = 2; } message MultiplexingRequest { diff --git a/src/main/cpp/admin/AdminClient.cpp b/src/main/cpp/admin/AdminClient.cpp index 9c4d4b712..2900d6904 100644 --- a/src/main/cpp/admin/AdminClient.cpp +++ b/src/main/cpp/admin/AdminClient.cpp @@ -6,7 +6,7 @@ ROCKETMQ_NAMESPACE_BEGIN namespace admin { Status AdminClient::changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response) { - ClientContext context; + grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); context.set_deadline(deadline); Status status = stub_->ChangeLogLevel(&context, request, &response); diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h index 7954c958d..046b611c0 100644 --- a/src/main/cpp/admin/include/AdminClient.h +++ b/src/main/cpp/admin/include/AdminClient.h @@ -9,7 +9,6 @@ namespace rmq = apache::rocketmq::v1; ROCKETMQ_NAMESPACE_BEGIN using grpc::Channel; -using grpc::ClientContext; using grpc::Status; namespace admin { diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index e72b38609..1c5cecfcd 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -1,21 +1,19 @@ load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( - name = "http_client", + name = "ghttp_client", hdrs = [ - "include/HttpClient.h", + "include/GHttpClient.h", ], srcs = [ - "HttpClient.cpp", + "GHttpClient.cpp", ], - strip_include_prefix = "//src/main/cpp/base/include", deps = [ - "//api:rocketmq_interface", - "@com_google_absl//absl/time", - "@com_google_absl//absl/synchronization", - "@curl//:curl", - "@com_github_gabime_spdlog//:spdlog", + "//src/main/cpp/log:log_library", + "@com_github_fmtlib_fmt//:fmtlib", + "@com_github_grpc_grpc//:grpc", ], + strip_include_prefix = "//src/main/cpp/base/include", visibility = [ "//visibility:public", ], @@ -33,7 +31,7 @@ cc_library( ], strip_include_prefix = "//src/main/cpp/base/include", deps = [ - ":http_client", + ":ghttp_client", ], visibility = [ "//visibility:public", @@ -46,18 +44,16 @@ cc_library( srcs = glob(["*.cpp"]), strip_include_prefix = "//src/main/cpp/base/include", deps = [ - "//api:rocketmq_interface", + "//src/main/cpp/log:log_library", "@com_google_absl//absl/random", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_github_gabime_spdlog//:spdlog", - "@com_github_grpc_grpc//:grpc_secure", - "//external:libuv", + "@com_github_grpc_grpc//:grpc", "@com_googlesource_code_re2//:re2", "@zlib//:zlib", "@boringssl//:crypto", "@boringssl//:ssl", - "@curl//:curl", ], visibility = [ "//visibility:public", diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt index 8f5b1fbdd..f4e44406f 100644 --- a/src/main/cpp/base/CMakeLists.txt +++ b/src/main/cpp/base/CMakeLists.txt @@ -1,16 +1,16 @@ add_library(base_obj OBJECT) target_sources(base_obj PRIVATE - HttpClient.cpp + GHttpClient.cpp HostInfo.cpp + Metadata.cpp MixAll.cpp MQMessage.cpp PermissionHelper.cpp Protocol.cpp RateLimiter.cpp - RequestIdGenerator.cpp TopAddressing.cpp - MessageSystemFlag.cpp + UniqueIdGenerator.cpp UtilAll.cpp) target_include_directories(base_obj PUBLIC @@ -24,13 +24,12 @@ target_link_libraries(base_obj absl::strings absl::time CURL::libcurl + crypto fmt grpc++ - crypto + logger_library ssl re2::re2 - rocketmq_interface - spdlog::spdlog_header_only uv_a zlib) @@ -50,6 +49,7 @@ target_link_libraries(base_library CURL::libcurl fmt grpc++ + logger_library re2::re2 rocketmq_interface spdlog::spdlog_header_only diff --git a/src/main/cpp/base/GHttpClient.cpp b/src/main/cpp/base/GHttpClient.cpp new file mode 100644 index 000000000..c8335348f --- /dev/null +++ b/src/main/cpp/base/GHttpClient.cpp @@ -0,0 +1,161 @@ +#include "GHttpClient.h" +#include "absl/strings/str_join.h" +#include "fmt/format.h" +#include +#include +#include +#include +#include +#include +#include + +#include "LoggerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +GHttpClient::GHttpClient() : shutdown_(false) { + grpc_core::ExecCtx exec_ctx; + grpc_httpcli_context_init(&http_context_); + grpc_pollset* pollset = static_cast(gpr_zalloc(grpc_pollset_size())); + grpc_pollset_init(pollset, &http_mtx_); + http_polling_entity_ = grpc_polling_entity_create_from_pollset(pollset); + GRPC_CLOSURE_INIT(&destroy_, &GHttpClient::destroyPollingEntity, &http_polling_entity_, grpc_schedule_on_exec_ctx); +} + +GHttpClient::~GHttpClient() { + SPDLOG_INFO("GHttpClient::~GHttpClient() starts"); + grpc_core::ExecCtx exec_ctx; + grpc_httpcli_context_destroy(&http_context_); + grpc_pollset_shutdown(grpc_polling_entity_pollset(&http_polling_entity_), &destroy_); + SPDLOG_INFO("GHttpClient::~GHttpClient() completed"); +} + +const int64_t GHttpClient::POLL_INTERVAL = 1000; + +const int GHttpClient::STATUS_OK = 200; + +void GHttpClient::poll() { + grpc_core::ExecCtx exec_ctx; + while (!shutdown_) { + gpr_mu_lock(http_mtx_); + grpc_error_handle error = grpc_pollset_work(grpc_polling_entity_pollset(&http_polling_entity_), &worker_, + grpc_core::ExecCtx::Get()->Now() + POLL_INTERVAL); + gpr_mu_unlock(http_mtx_); + if (error) { + SPDLOG_WARN("grpc_pollset_work failed"); + } else { + SPDLOG_TRACE("grpc_pollset_work returned. grpc_pollset_size: {}", grpc_pollset_size()); + } + submit0(); + } + SPDLOG_INFO("GHttpClient::poll completed"); +} + +void GHttpClient::start() { + SPDLOG_INFO("GHttpClient::start()"); + if (!shutdown_) { + loop_ = std::thread(std::bind(&GHttpClient::poll, this)); + SPDLOG_INFO("GHttpClient starts to poll"); + } +} + +void GHttpClient::shutdown() { + SPDLOG_INFO("GHttpClient::shutdown()"); + shutdown_.store(true, std::memory_order_relaxed); + if (loop_.joinable()) { + loop_.join(); + SPDLOG_INFO("GHttpClient#loop thread quit OK"); + } +} + +void GHttpClient::get( + HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + auto http_invocation_context = new HttpInvocationContext(); + std::string http_host = fmt::format("{}:{}", host, port); + http_invocation_context->host = http_host; + http_invocation_context->path = path; + http_invocation_context->request.host = const_cast(http_invocation_context->host.c_str()); + http_invocation_context->request.http.path = const_cast(http_invocation_context->path.c_str()); + http_invocation_context->request.handshaker = &grpc_httpcli_plaintext; + http_invocation_context->callback = cb; + + { + absl::MutexLock lk(&pending_requests_mtx_); + pending_requests_.emplace_back(http_invocation_context); + SPDLOG_TRACE("Add HTTP request to pending list"); + } + + { + grpc_core::ExecCtx exec_ctx; + SPDLOG_TRACE("Prepare to grpc_pollset_kick"); + { + gpr_mu_lock(http_mtx_); + grpc_error_handle error = grpc_pollset_kick(grpc_polling_entity_pollset(&http_polling_entity_), nullptr); + gpr_mu_unlock(http_mtx_); + if (GRPC_ERROR_NONE != error) { + SPDLOG_WARN("grpc_pollset_kick failed"); + } else { + SPDLOG_TRACE("grpc_pollset_kick completed"); + } + } + } +} + +void GHttpClient::submit0() { + absl::MutexLock lk(&pending_requests_mtx_); + if (pending_requests_.empty()) { + SPDLOG_TRACE("No pending HTTP requests"); + return; + } + + SPDLOG_DEBUG("Add {} pending HTTP requests to pollset", pending_requests_.size()); + grpc_core::ExecCtx exec_ctx; + { + for (auto it = pending_requests_.begin(); it != pending_requests_.end();) { + auto http_invocation_context = *it; + SPDLOG_TRACE("Prepare to create quota"); + grpc_resource_quota* resource_quota = grpc_resource_quota_create("get"); + SPDLOG_TRACE("Quota created"); + SPDLOG_TRACE("grpc_httpcli_get starts"); + grpc_httpcli_get( + &http_context_, &http_polling_entity_, resource_quota, &http_invocation_context->request, + grpc_core::ExecCtx::Get()->Now() + absl::ToInt64Milliseconds(absl::Seconds(3)), + GRPC_CLOSURE_CREATE(&GHttpClient::onCompletion, http_invocation_context, grpc_schedule_on_exec_ctx), + &http_invocation_context->response); + SPDLOG_TRACE("grpc_httpcli_get completed"); + grpc_resource_quota_unref_internal(resource_quota); + SPDLOG_TRACE("Resource quota unref completed"); + it = pending_requests_.erase(it); + } + } +} + +void GHttpClient::destroyPollingEntity(void* arg, grpc_error_handle error) { + auto polling_entity = reinterpret_cast(arg); + grpc_pollset_destroy(grpc_polling_entity_pollset(polling_entity)); +} + +void GHttpClient::onCompletion(void* arg, grpc_error_handle error) { + auto context = reinterpret_cast(arg); + absl::flat_hash_map metadata; + + if (error) { + context->callback(500, metadata, std::string()); + SPDLOG_WARN("HTTP request failed"); + delete context; + return; + } + + for (size_t i = 0; i < context->response.hdr_count; i++) { + grpc_http_header* hdr = context->response.hdrs + i; + metadata.insert({std::string(hdr->key), std::string(hdr->value)}); + } + std::string body(context->response.body, context->response.body_length); + SPDLOG_DEBUG("HTTP response received. Code: {}, response-headers: {}, response-body: {}", context->response.status, + absl::StrJoin(metadata, ",", absl::PairFormatter("=")), body); + context->callback(context->response.status, metadata, body); + delete context; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/HttpClient.cpp b/src/main/cpp/base/HttpClient.cpp deleted file mode 100644 index f66d95b03..000000000 --- a/src/main/cpp/base/HttpClient.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "HttpClient.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -HttpClient::HttpClient() { - curl_ = curl_easy_init(); - if (!curl_) { - return; - } -} - -HttpClient::~HttpClient() { - if (curl_) { - curl_easy_cleanup(curl_); - curl_ = nullptr; - } -} - -std::size_t HttpClient::writeCallback(void* data, std::size_t size, std::size_t nmemb, void* param) { - auto response = reinterpret_cast(param); - response->reserve(size * nmemb); - response->append(reinterpret_cast(data), size * nmemb); - return size * nmemb; -} - -bool HttpClient::get(const std::string& query, std::string& response, absl::Duration timeout) { - SPDLOG_DEBUG("GET {}", query); - if (!curl_) { - SPDLOG_WARN("CURL session is not properly"); - return false; - } - CURLcode res; - - // CURL easy-interface is not thread-safe. - absl::MutexLock lk(&mtx_); - - SPDLOG_DEBUG("Reset CURL session"); - curl_easy_reset(curl_); - - curl_easy_setopt(curl_, CURLOPT_URL, query.c_str()); - SPDLOG_DEBUG("Set CURL url: {}", query); - - curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, &HttpClient::writeCallback); - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void*)&response); - - SPDLOG_DEBUG("Set CURL IO timeout to {}ms", absl::ToInt64Milliseconds(timeout)); - curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS, absl::ToInt64Milliseconds(timeout)); - - SPDLOG_DEBUG("Set CURL connect timeout to {}ms", absl::ToInt64Milliseconds(timeout)); - curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT_MS, absl::ToInt64Milliseconds(timeout)); - - SPDLOG_DEBUG("Prepare to initiate HTTP/GET request"); - res = curl_easy_perform(curl_); - SPDLOG_DEBUG("CURL completes request/response"); - - if (CURLE_OK == res) { - SPDLOG_DEBUG("Response code: {}, text: {}", res, response); - return true; - } else { - SPDLOG_WARN("Response code: {}, text: {}", res, response); - return false; - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 3d062584f..6a25d05d8 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -90,7 +90,7 @@ void MQMessage::setBody(const char* body, int len) { void MQMessage::setBody(const std::string& body) { impl_->body_ = body; } -int32_t MQMessage::bodyLength() const { return static_cast(impl_->body_.length()); } +uint32_t MQMessage::bodyLength() const { return impl_->body_.length(); } const std::map& MQMessage::getProperties() const { return impl_->user_attribute_map_; } diff --git a/src/main/cpp/base/MessageSystemFlag.cpp b/src/main/cpp/base/MessageSystemFlag.cpp deleted file mode 100644 index a22e905e7..000000000 --- a/src/main/cpp/base/MessageSystemFlag.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "MessageSystemFlag.h" - -namespace rocketmq { - -const uint32_t MessageSystemFlag::BODY_COMPRESS_TYPE = 0x1; -const uint32_t MessageSystemFlag::MULTI_TAG_TYPE = 0x1 << 1; -const uint32_t MessageSystemFlag::PREPARED_TYPE = 0x1 << 2; -const uint32_t MessageSystemFlag::COMMIT_TYPE = 0x1 << 3; -const uint32_t MessageSystemFlag::ROLLBACK_TYPE = 0x3 << 2; - -bool MessageSystemFlag::bodyCompressed(uint32_t flag) { return BODY_COMPRESS_TYPE == (flag & BODY_COMPRESS_TYPE); } - -} // namespace rocketmq diff --git a/src/main/cpp/client/Metadata.cpp b/src/main/cpp/base/Metadata.cpp similarity index 100% rename from src/main/cpp/client/Metadata.cpp rename to src/main/cpp/base/Metadata.cpp diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 58e134fcd..aacf2c8d0 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -19,7 +19,7 @@ const int32_t MixAll::MASTER_BROKER_ID = 0; const int32_t MixAll::MAX_MESSAGE_NUMBER_PER_BATCH = 32; -const int32_t MixAll::MAX_MESSAGE_BODY_SIZE = 1024 * 1024 * 4; +const uint32_t MixAll::MAX_MESSAGE_BODY_SIZE = 1024 * 1024 * 4; const char* MixAll::RETRY_GROUP_TOPIC_PREFIX = "%RETRY%"; @@ -54,7 +54,7 @@ bool MixAll::validate(const MQMessage& message) { return false; } - int32_t body_length = message.bodyLength(); + uint32_t body_length = message.bodyLength(); if (!body_length || body_length > MAX_MESSAGE_BODY_SIZE) { return false; } diff --git a/src/main/cpp/base/RequestIdGenerator.cpp b/src/main/cpp/base/RequestIdGenerator.cpp deleted file mode 100644 index 05f7e5e80..000000000 --- a/src/main/cpp/base/RequestIdGenerator.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "RequestIdGenerator.h" - -#include "absl/base/internal/sysinfo.h" -#include "MixAll.h" -#include "UtilAll.h" -#include "spdlog/spdlog.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -std::atomic_long RequestIdGenerator::sequence_(0); - -std::string RequestIdGenerator::nextRequestId() { - int32_t numeric[4]; - std::string request_id; - const std::string& ip = UtilAll::getHostIPv4(); - struct in_addr addr = {}; - int ret = inet_aton(ip.data(), &addr); - - // ip is supposed to be legal and parsable. - assert(ret == 1); - numeric[0] = htonl(addr.s_addr); - numeric[1] = absl::base_internal::GetCachedTID(); - *(reinterpret_cast(numeric) + 1) = ++sequence_; - return MixAll::hex(numeric, sizeof(numeric)); -} - -int64_t RequestIdGenerator::sequenceOf(const std::string& request_id) { - if (request_id.empty()) { - SPDLOG_WARN("Invalid empty request_id"); - return 0; - } - std::vector bin; - if (!MixAll::hexToBinary(request_id, bin)) { - SPDLOG_WARN("Invalid request_id: {}", request_id); - return 0; - } - // IP(int32_t) + PID(int32_t) + term_id(int64_t) - assert(bin.size() == 4 + 4 + 8); - auto ptr = reinterpret_cast(bin.data()); - return *(ptr + 1); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index 344d358dd..9a1c840ac 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -1,27 +1,27 @@ #include "TopAddressing.h" +#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_split.h" #include #include -#include "HttpClient.h" - ROCKETMQ_NAMESPACE_BEGIN +TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") {} TopAddressing::TopAddressing(std::string host, int port, std::string path) - : host_(std::move(host)), port_(port), path_(std::move(path)) {} + : host_(std::move(host)), port_(port), path_(std::move(path)), http_client_(absl::make_unique()) { + http_client_->start(); +} + +TopAddressing::~TopAddressing() { http_client_->shutdown(); } -bool TopAddressing::fetchNameServerAddresses(std::vector& list) { +void TopAddressing::fetchNameServerAddresses(const std::function&)>& cb) { SPDLOG_DEBUG("Prepare to send HTTP request, timeout=3s"); std::string base(fmt::format("http://{}:{}", host_, port_)); // Append host info if necessary. std::string query_string(path_); - if (!unit_name_.empty()) { - query_string.append("-").append(unit_name_); - } - if (absl::StrContains(query_string, "?")) { query_string.append("&"); } else { @@ -34,25 +34,18 @@ bool TopAddressing::fetchNameServerAddresses(std::vector& list) { query_string.append("nofix=1"); } - std::string request(fmt::format("{}{}", base, query_string)); - std::string response; - - HttpClient http_client; - if (!http_client) { - SPDLOG_WARN("Failed to create CURL session. OS probably runs out of resources. Check out memory and FD usage"); - return false; - } - - if (http_client.get(request, response, absl::Seconds(3))) { - SPDLOG_INFO("Received name server list: {}", response); - std::vector splits = absl::StrSplit(response, ";"); - for (const auto& item : splits) { - list.push_back(item); + auto callback = [cb](int code, const absl::flat_hash_map& metadata, + const std::string& body) { + SPDLOG_DEBUG("Receive HTTP response. Code: {}, body: {}", code, body); + if (GHttpClient::STATUS_OK == code) { + cb(true, absl::StrSplit(body, ';')); + } else { + std::vector name_server_list; + cb(false, name_server_list); } - return true; - } + }; - return false; + http_client_->get(HttpProtocol::HTTP, host_, port_, query_string, callback); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp new file mode 100644 index 000000000..9bb0f04af --- /dev/null +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -0,0 +1,63 @@ +#include "UniqueIdGenerator.h" +#include "LoggerImpl.h" +#include "MixAll.h" +#include "UtilAll.h" +#include "absl/base/internal/endian.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +UniqueIdGenerator::UniqueIdGenerator() + : prefix_(), since_custom_epoch_(std::chrono::system_clock::now() - customEpoch()), + start_time_point_(std::chrono::steady_clock::now()), seconds_(deltaSeconds()), sequence_(0) { + std::vector mac_address; + if (UtilAll::macAddress(mac_address)) { + memcpy(prefix_.data(), mac_address.data(), mac_address.size()); + } else { + SPDLOG_WARN("Failed to get network interface MAC address"); + } + + pid_t pid = getpid(); + uint32_t big_endian_pid = absl::big_endian::FromHost32(pid); + // Copy the lower 2 bytes + memcpy(prefix_.data() + 6, reinterpret_cast(&big_endian_pid) + 2, sizeof(uint16_t)); +} + +UniqueIdGenerator& UniqueIdGenerator::instance() { + static UniqueIdGenerator generator; + return generator; +} + +std::string UniqueIdGenerator::next() { + Slot slot = {}; + { + absl::MutexLock lk(&mtx_); + uint32_t delta = deltaSeconds(); + if (seconds_ != delta) { + seconds_ = delta; + sequence_ = 0; + SPDLOG_DEBUG("Second: {} and sequence: {}", seconds_, sequence_); + } else { + sequence_++; + } + slot.seconds = seconds_; + slot.sequence = sequence_; + } + std::array raw{}; + memcpy(raw.data(), prefix_.data(), prefix_.size()); + memcpy(raw.data() + prefix_.size(), &slot, sizeof(slot)); + return MixAll::hex(raw.data(), raw.size()); +} + +std::chrono::system_clock::time_point UniqueIdGenerator::customEpoch() { + return absl::ToChronoTime(absl::FromDateTime(2021, 1, 1, 0, 0, 0, absl::UTCTimeZone())); +} + +uint32_t UniqueIdGenerator::deltaSeconds() { + return std::chrono::duration_cast((std::chrono::steady_clock::now() - start_time_point_) + + since_custom_epoch_) + .count(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index 35a724b20..f2cc38dab 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -1,146 +1,89 @@ #include "UtilAll.h" -#include "absl/strings/str_split.h" #include "LoggerImpl.h" -#include "uv.h" +#include "absl/strings/str_split.h" +#include "src/core/lib/iomgr/gethostname.h" #include "zlib.h" - -namespace rocketmq { - -std::string UtilAll::getHostIPv4() { - static std::string host_ip; - if (host_ip.empty()) { - std::vector addresses = getIPv4Addresses(); - if (!pickIPv4Address(addresses, host_ip)) { - return LOOP_BACK_IP; - } - } - return host_ip; -} - -std::vector UtilAll::getIPv4Addresses() { - std::vector addresses; - char buf[512]; - uv_interface_address_t* info; - int count, i; - - uv_interface_addresses(&info, &count); - i = count; - while (i--) { - uv_interface_address_t interface = info[i]; - if (interface.address.address4.sin_family == AF_INET) { - uv_ip4_name(&interface.address.address4, buf, sizeof(buf)); - if (interface.is_internal) { - continue; - } - addresses.emplace_back(std::string(buf)); - } else if (interface.address.address4.sin_family == AF_INET6) { - uv_ip6_name(&interface.address.address6, buf, sizeof(buf)); - } - } - uv_free_interface_addresses(info, count); - return addresses; +#include +#include +#include + +#if defined(__APPLE__) +#include +#define AF_FAMILY AF_LINK +#elif defined(__linux__) +#include +#include +#include +#define AF_FAMILY AF_PACKET +#elif defined(_WIN32) + +#endif + +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +#if defined(__APPLE__) +#define AF_FAMILY AF_LINK +#elif defined(__linux__) +#define AF_FAMILY AF_PACKET +#elif defined(_WIN32) +#endif + +std::string UtilAll::hostname() { + std::string host_name(grpc_gethostname()); + return host_name; } -/** - * Select IP from provided lists. The selecting criteria is first WAN then LAN. - * For case of LAN, the following order is followed: - * 1, 10.0.0.0/8 (255.0.0.0), 10.0.0.0 – 10.255.255.255 - * 2, 100.64.0.0/10(255.192.0.0), 100.64.0.0 - 100.127.255.255 - * 3, 172.16.0.0/12 (255.240.0.0) 172.16.0.0 – 172.31.255.255 - * 4, 192.168.0.0/16 (255.255.0.0), 192.168.0.0 – 192.168.255.255 - */ -std::vector> UtilAll::Ranges = {{"10.0.0.0", "10.255.255.255"}, - {"100.64.0.0", "100.127.255.255"}, - {"172.16.0.0", "172.31.255.255"}, - {"192.168.0.0", "192.168.255.255"}}; - -int UtilAll::findCategory(const std::string& ip, const std::vector>& categories) { - struct in_addr addr = {}; - int status = inet_aton(ip.data(), &addr); - if (!status) { - SPDLOG_WARN("inet_aton failed. IPv4: {}", ip); - return -1; - } - uint32_t current = ntohl(addr.s_addr); - - int index = 1; - for (const auto& item : categories) { - const std::string& left = item.first; - const std::string& right = item.second; - struct in_addr low = {}; - struct in_addr high = {}; - status = inet_aton(left.data(), &low); - if (!status) { - SPDLOG_WARN("inet_aton failed. IPv4: {}", left); - return -1; - } - - status = inet_aton(right.data(), &high); - if (!status) { - SPDLOG_WARN("inet_aton failed. IPv4: {}", right); - return -1; - } +bool UtilAll::macAddress(std::vector& mac) { + static std::vector cache; + static bool mac_cached = false; - uint32_t start = ntohl(low.s_addr); - uint32_t end = ntohl(high.s_addr); - if (current >= start && current <= end) { - return index; - } - index++; + if (mac_cached) { + mac = cache; + return true; } - return 0; -} -bool UtilAll::pickIPv4Address(const std::vector& addresses, std::string& result) { - if (addresses.empty()) { + struct ifaddrs *head = nullptr, *node; + if (getifaddrs(&head)) { return false; } - if (addresses.size() == 1) { - result = addresses[0]; - } - - std::vector> categories; - categories.reserve(5); - categories.resize(5); - for (const auto& ip : addresses) { - int index = findCategory(ip, Ranges); - if (index >= 0) { - std::vector>::size_type category_index = index; - if (category_index < categories.size()) { - categories[category_index].push_back(ip); + for (node = head; node; node = node->ifa_next) { + if (node->ifa_addr->sa_family == AF_FAMILY) { +#if defined(__APPLE__) + auto* ptr = reinterpret_cast(LLADDR(reinterpret_cast(node->ifa_addr))); +#elif defined(__linux__) + auto* ptr = reinterpret_cast(reinterpret_cast(node->ifa_addr)->sll_addr); +#endif + bool all_zero = true; + for (int i = 0; i < 6; i++) { + if (*(ptr + i) != 0) { + all_zero = false; + break; + } } + if (all_zero) { + SPDLOG_TRACE("Skip MAC address of network interface {}", node->ifa_name); + continue; + } + SPDLOG_DEBUG("Use MAC address of network interface {}", node->ifa_name); + // MAC address has 48 bits + cache.resize(6); + memcpy(cache.data(), ptr, 6); + mac_cached = true; + mac = cache; + break; } } - for (const auto& category : categories) { - if (!category.empty()) { - result = category[0]; - return true; - } - } - result = addresses[0]; + freeifaddrs(head); return true; } -void UtilAll::int16tobyte(char* out_array, int16_t data) { - if (!out_array) { - return; - } - out_array[0] = static_cast(data & 0xff); - out_array[1] = static_cast((data >> 8) & 0xff); -} - -void UtilAll::int32tobyte(char* out_array, int32_t data) { - if (!out_array) { - return; - } - out_array[0] = static_cast(data & 0xff); - out_array[1] = static_cast((data >> 8) & 0xff); - out_array[2] = static_cast((data >> 16) & 0xff); - out_array[3] = static_cast((data >> 24) & 0xff); -} - bool UtilAll::compress(const std::string& src, std::string& dst) { z_stream stream; stream.zalloc = Z_NULL; @@ -214,6 +157,4 @@ bool UtilAll::uncompress(const std::string& src, std::string& dst) { } } -std::string UtilAll::LOOP_BACK_IP("127.0.0.1"); - -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Functional.h b/src/main/cpp/base/include/Functional.h deleted file mode 100644 index d06e05a73..000000000 --- a/src/main/cpp/base/include/Functional.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "rocketmq/RocketMQ.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class Functional { -public: - Functional() = default; - - explicit Functional(std::function* function) : function_(function) {} - - ~Functional() = default; - - void setFunction(std::function* function) { function_ = function; } - - std::function* getFunction() { return function_; } - -private: - std::function* function_; -}; - -using FunctionalSharePtr = std::shared_ptr; -using FunctionalWeakPtr = std::weak_ptr; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/GHttpClient.h b/src/main/cpp/base/include/GHttpClient.h new file mode 100644 index 000000000..53dc7c2d7 --- /dev/null +++ b/src/main/cpp/base/include/GHttpClient.h @@ -0,0 +1,72 @@ +#include "rocketmq/RocketMQ.h" + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/iomgr/iomgr.h" +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class HttpProtocol : int8_t { + HTTP = 1, + HTTPS = 2, +}; + +struct HttpInvocationContext { + HttpInvocationContext() { memset(&request, 0, sizeof(request)); } + std::string host; + std::string path; + grpc_httpcli_request request; + grpc_http_response response; + std::function&, const std::string&)> callback; +}; + +class GHttpClient { +public: + GHttpClient(); + + ~GHttpClient(); + + void start(); + + void shutdown(); + + void + get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb); + + static const int STATUS_OK; + +private: + static void onCompletion(void* arg, grpc_error_handle error); + + static void destroyPollingEntity(void* arg, grpc_error_handle error); + + void poll(); + + void submit0() LOCKS_EXCLUDED(pending_requests_mtx_); + + grpc_httpcli_context http_context_; + grpc_polling_entity http_polling_entity_; + gpr_mu* http_mtx_{nullptr}; + + std::thread loop_; + grpc_pollset_worker* worker_; + grpc_closure destroy_; + std::atomic_bool shutdown_; + + std::vector pending_requests_ GUARDED_BY(pending_requests_mtx_); + absl::Mutex pending_requests_mtx_; + + static const int64_t POLL_INTERVAL; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h index 8a81bba7a..573bf7c88 100644 --- a/src/main/cpp/base/include/Histogram.h +++ b/src/main/cpp/base/include/Histogram.h @@ -5,8 +5,10 @@ #include #include #include +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN -namespace rocketmq { class Histogram { public: Histogram(std::string title, int32_t capacity) : title_(std::move(title)), capacity_(capacity) { @@ -36,7 +38,7 @@ class Histogram { std::vector& labels() { return labels_; } void reportAndReset(std::string& result) { - assert(labels_.size() == capacity_); + assert(labels_.size() == static_cast::size_type>(capacity_)); std::vector values; values.reserve(capacity_); for (auto& item : data_) { @@ -46,7 +48,7 @@ class Histogram { } result.clear(); result.append(title_).append(":"); - for (int i = 0; i < labels_.size(); ++i) { + for (std::vector::size_type i = 0; i < labels_.size(); ++i) { if (i) { result.append(", "); } @@ -60,4 +62,5 @@ class Histogram { int32_t capacity_; std::vector labels_; }; -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h deleted file mode 100644 index 096abfaee..000000000 --- a/src/main/cpp/base/include/HttpClient.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "absl/synchronization/mutex.h" -#include "absl/time/time.h" -#include "rocketmq/RocketMQ.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class HttpClient { -public: - HttpClient(); - - virtual ~HttpClient(); - - bool get(const std::string& query, std::string& response, absl::Duration timeout); - - explicit operator bool() const { return curl_ != nullptr; } - - static std::size_t writeCallback(void* data, std::size_t size, std::size_t nmemb, void* param); - -private: - CURL* curl_; - absl::Mutex mtx_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 7a08113e7..8fb2614f4 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -1,7 +1,10 @@ #pragma once #include "LoggerImpl.h" +#include "Metadata.h" +#include "UniqueIdGenerator.h" #include "absl/synchronization/mutex.h" +#include "absl/time/time.h" #include "rocketmq/RocketMQ.h" #include #include @@ -19,17 +22,21 @@ ROCKETMQ_NAMESPACE_BEGIN * async_stream.h */ struct BaseInvocationContext { + BaseInvocationContext() : request_id_(UniqueIdGenerator::instance().next()) { + context.AddMetadata(Metadata::REQUEST_ID_KEY, request_id_); + } + virtual ~BaseInvocationContext() = default; virtual void onCompletion(bool ok) = 0; - - grpc::ClientContext context_; - grpc::Status status_; - std::chrono::system_clock::time_point created_time_{std::chrono::system_clock::now()}; - std::chrono::steady_clock::time_point start_time_{std::chrono::steady_clock::now()}; + std::string request_id_; + std::string remote_address; + grpc::ClientContext context; + grpc::Status status; + absl::Time created_time{absl::Now()}; + std::chrono::steady_clock::time_point start_time{std::chrono::steady_clock::now()}; }; -template -struct InvocationContext : public BaseInvocationContext { +template struct InvocationContext : public BaseInvocationContext { void onCompletion(bool ok) override { /// Client-side Read, Server-side Read, Client-side @@ -42,38 +49,25 @@ struct InvocationContext : public BaseInvocationContext { /// has done a WritesDone already. if (!ok) { SPDLOG_WARN("One async call is already dead"); - - if (callback_) { - callback_(grpc::Status::CANCELLED, context_, response_); + if (callback) { + callback(this); } - delete this; return; } - if (!status_.ok() && grpc::StatusCode::DEADLINE_EXCEEDED == status_.error_code()) { - std::time_t creation_time_t = std::chrono::system_clock::to_time_t(created_time_); - auto fraction = - std::chrono::duration_cast(created_time_.time_since_epoch()).count() % 1000; - char fmt_date_time[128]; - - /** - * TODO: std::localtime is not thread-safe, output, as a result, may be less reliable in highly contending - * scenario - */ - std::strftime(fmt_date_time, sizeof(fmt_date_time), "%Y-%m-%d %H:%M:%S", std::localtime(&creation_time_t)); - + if (!status.ok() && grpc::StatusCode::DEADLINE_EXCEEDED == status.error_code()) { auto elapsed = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time_).count(); + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); auto diff = - std::chrono::duration_cast(std::chrono::system_clock::now() - context_.deadline()) + std::chrono::duration_cast(std::chrono::system_clock::now() - context.deadline()) .count(); - SPDLOG_WARN("Asynchronous RPC[{}.{}] timed out, elapsing {}ms, deadline-over-due: {}ms", fmt_date_time, fraction, - elapsed, diff); + SPDLOG_WARN("Asynchronous RPC[{}.{}] timed out, elapsing {}ms, deadline-over-due: {}ms", + absl::FormatTime(created_time, absl::UTCTimeZone()), elapsed, diff); } try { - if (callback_) { - callback_(status_, context_, response_); + if (callback) { + callback(this); } } catch (const std::exception& e) { SPDLOG_WARN("Unexpected error while invoking user-defined callback. Reason: {}", e.what()); @@ -83,8 +77,8 @@ struct InvocationContext : public BaseInvocationContext { delete this; } - T response_; - std::function callback_; - std::unique_ptr> response_reader_; + T response; + std::function*)> callback; + std::unique_ptr> response_reader; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageSystemFlag.h b/src/main/cpp/base/include/MessageSystemFlag.h deleted file mode 100644 index 2df7a4204..000000000 --- a/src/main/cpp/base/include/MessageSystemFlag.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace rocketmq { -class MessageSystemFlag { -public: - static const uint32_t BODY_COMPRESS_TYPE; - static const uint32_t MULTI_TAG_TYPE; - static const uint32_t PREPARED_TYPE; - static const uint32_t COMMIT_TYPE; - static const uint32_t ROLLBACK_TYPE; - - static bool bodyCompressed(uint32_t flag); - - template static void clearFlag(T& value, T flag) { value &= ~flag; } - - template static void tagFlag(T& value, T flag) { - -#ifdef DEBUG - // Ensure existing flags are orthogonal to the flag to tag - T check = value & flag; - if (0 != check && flag != check) { - abort(); - } -#endif - - value |= flag; - } -}; -} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/Metadata.h b/src/main/cpp/base/include/Metadata.h similarity index 100% rename from src/main/cpp/client/include/Metadata.h rename to src/main/cpp/base/include/Metadata.h diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 01f497adf..42d8203d9 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -15,7 +15,7 @@ class MixAll { public: static const int32_t MASTER_BROKER_ID; static const int32_t MAX_MESSAGE_NUMBER_PER_BATCH; - static const int32_t MAX_MESSAGE_BODY_SIZE; + static const uint32_t MAX_MESSAGE_BODY_SIZE; static const char* RETRY_GROUP_TOPIC_PREFIX; static const RE2 TOPIC_REGEX; @@ -65,6 +65,7 @@ class MixAll { static bool hexToBinary(const std::string& hex, std::vector& bin); static bool homeDirectory(std::string& home); + private: static bool hexCharValue(char c, uint8_t& value); }; diff --git a/src/main/cpp/base/include/RequestIdGenerator.h b/src/main/cpp/base/include/RequestIdGenerator.h deleted file mode 100644 index 048bbe23c..000000000 --- a/src/main/cpp/base/include/RequestIdGenerator.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "rocketmq/RocketMQ.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class RequestIdGenerator { -public: - static std::string nextRequestId(); - - static int64_t sequenceOf(const std::string& request_id); - -private: - static std::atomic_long sequence_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h index 71309417c..e556efe58 100644 --- a/src/main/cpp/base/include/TopAddressing.h +++ b/src/main/cpp/base/include/TopAddressing.h @@ -2,25 +2,25 @@ #include #include +#include #include #include #include +#include "GHttpClient.h" #include "HostInfo.h" -#include "HttpClient.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN class TopAddressing { public: - explicit TopAddressing() : host_("jmenv.tbsite.net"), path_("/rocketmq/nsaddr") {} + TopAddressing(); TopAddressing(std::string host, int port, std::string path); - bool fetchNameServerAddresses(std::vector& list); + virtual ~TopAddressing(); - void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } + void fetchNameServerAddresses(const std::function&)>& cb); private: std::string host_; @@ -28,12 +28,7 @@ class TopAddressing { std::string path_; HostInfo host_info_; - /** - * Allow use to override unit name. - */ - std::string unit_name_; + std::unique_ptr http_client_; }; -using TopAddressingPtr = std::unique_ptr; - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h new file mode 100644 index 000000000..e7b44fdfb --- /dev/null +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -0,0 +1,64 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class UniqueIdGenerator { +public: + static UniqueIdGenerator& instance(); + + std::string next() LOCKS_EXCLUDED(mtx_); + + UniqueIdGenerator(const UniqueIdGenerator&) = delete; + + UniqueIdGenerator(UniqueIdGenerator&&) = delete; + + UniqueIdGenerator& operator=(const UniqueIdGenerator&) = delete; + + UniqueIdGenerator& operator=(UniqueIdGenerator&&) = delete; + +private: + UniqueIdGenerator(); + + struct Slot { + uint32_t seconds; + uint32_t sequence; + }; + + static std::chrono::system_clock::time_point customEpoch(); + + /** + * Seconds since 2021-01-01 00:00:00.0 +0000 + * @return + */ + uint32_t deltaSeconds(); + + std::array prefix_; + absl::Mutex mtx_; + + /** + * Duration since 2021-01-01 00:00:00.0(UTC) + */ + std::chrono::system_clock::duration since_custom_epoch_; + + /** + * @brief Generator created time point using steady clock. Such that when system time are adjusted backward using NTP + * service, generated message identifiers remain unique. + * + * Note: when program restarts, duplicated message-id may still get generated. However, if program run in + * containerized environment, say Kubernetes, this will NOT be a problem since MAC address would change on restart. + */ + std::chrono::steady_clock::time_point start_time_point_; + + uint32_t seconds_; + uint32_t sequence_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/UtilAll.h b/src/main/cpp/base/include/UtilAll.h index 40a32070c..83c2b8baf 100644 --- a/src/main/cpp/base/include/UtilAll.h +++ b/src/main/cpp/base/include/UtilAll.h @@ -7,32 +7,11 @@ ROCKETMQ_NAMESPACE_BEGIN -const std::string DEFAULT_CONSUMER_GROUP = "DEFAULT_CONSUMER"; - class UtilAll { public: - static std::string getHostIPv4(); - - /** - * Walk through all network interfaces and return IPv4 addresses. - */ - static std::vector getIPv4Addresses(); - - /** - * Select IP from provided lists. The selecting criteria is first WAN then LAN. - * For case of LAN, the following order is followed: - * 1, 10.0.0.0/8 (255.0.0.0), 10.0.0.0 – 10.255.255.255 - * 2, 100.64.0.0/10(255.192.0.0), 100.64.0.0 - 100.127.255.255 - * 3, 172.16.0.0/12 (255.240.0.0) 172.16.0.0 – 172.31.255.255 - * 4, 192.168.0.0/16 (255.255.0.0), 192.168.0.0 – 192.168.255.255 - */ - static bool pickIPv4Address(const std::vector& addresses, std::string& result); - - static std::string LOOP_BACK_IP; + static std::string hostname(); - static void int16tobyte(char* out_array, int16_t data); - - static void int32tobyte(char* out_array, int32_t data); + static bool macAddress(std::vector& mac); /** * Compress @@ -45,9 +24,6 @@ class UtilAll { static bool uncompress(const std::string& src, std::string& dst); private: - static int findCategory(const std::string& ip, const std::vector>& ranges); - - static std::vector> Ranges; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index bef671ab2..6ec90b8de 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -7,6 +7,7 @@ cc_library( deps = [ "//api:rocketmq_interface", "//proto:rocketmq_grpc_library", + "//src/main/cpp/admin:admin_server_library", "//src/main/cpp/base:base_library", "//src/main/cpp/scheduler:scheduler_library", "//src/main/cpp/concurrent:countdown_latch_library", diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt index 73ce038f7..e39390c83 100644 --- a/src/main/cpp/client/CMakeLists.txt +++ b/src/main/cpp/client/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(client_obj ClientManager.cpp LogInterceptorFactory.cpp LogInterceptor.cpp - Metadata.cpp TopicAssignmentInfo.cpp RpcClientImpl.cpp Signature.cpp diff --git a/src/main/cpp/client/ClientConfig.cpp b/src/main/cpp/client/ClientConfig.cpp index 261894661..e754e0e27 100644 --- a/src/main/cpp/client/ClientConfig.cpp +++ b/src/main/cpp/client/ClientConfig.cpp @@ -21,13 +21,14 @@ const char* ClientConfig::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSI ClientConfig::ClientConfig() : ClientConfig(std::string()) {} -ClientConfig::ClientConfig(std::string group_name) : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)) { +ClientConfig::ClientConfig(std::string group_name) + : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)), long_polling_timeout_(absl::Seconds(30)) { instance_name_ = "DEFAULT"; } std::string ClientConfig::clientId() const { std::stringstream ss; - ss << UtilAll::getHostIPv4(); + ss << UtilAll::hostname(); ss << "@"; std::string processID = std::to_string(getpid()); ss << (instance_name_ == "DEFAULT" ? processID : instance_name_); @@ -48,9 +49,7 @@ void ClientConfig::setCredentialsProvider(CredentialsProviderPtr credentials_pro credentials_provider_ = std::move(credentials_provider); } -CredentialsProviderPtr ClientConfig::credentialsProvider() { - return credentials_provider_; -} +CredentialsProviderPtr ClientConfig::credentialsProvider() { return credentials_provider_; } absl::Duration ClientConfig::getIoTimeout() const { return io_timeout_; } diff --git a/src/main/cpp/client/ClientInstance.cpp b/src/main/cpp/client/ClientInstance.cpp index 00899de40..ef4293c4b 100644 --- a/src/main/cpp/client/ClientInstance.cpp +++ b/src/main/cpp/client/ClientInstance.cpp @@ -1,7 +1,9 @@ #include "ClientInstance.h" #include +#include #include +#include #include "CRC.h" #include "InvocationContext.h" @@ -9,7 +11,6 @@ #include "LogInterceptorFactory.h" #include "LoggerImpl.h" #include "MessageAccessor.h" -#include "MessageSystemFlag.h" #include "Metadata.h" #include "MixAll.h" #include "Partition.h" @@ -29,21 +30,9 @@ ROCKETMQ_NAMESPACE_BEGIN ClientInstance::ClientInstance(std::string arn) : arn_(std::move(arn)), state_(State::CREATED), completion_queue_(std::make_shared()), - callback_thread_pool_(std::make_shared(std::thread::hardware_concurrency())), - latency_histogram_("Message-Latency", 11) { + callback_thread_pool_(grpc::CreateDefaultThreadPool()), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); - - inactive_rpc_client_detector_ = std::bind(&ClientInstance::doHealthCheck, this); - inactive_rpc_client_detector_function_ = std::make_shared(&inactive_rpc_client_detector_); - - heartbeat_loop_ = std::bind(&ClientInstance::doHeartbeat, this); - heartbeat_loop_function_ = std::make_shared(&heartbeat_loop_); - assignLabels(latency_histogram_); - - stats_functor_ = std::bind(&ClientInstance::logStats, this); - stats_function_ = std::make_shared(&stats_functor_); - server_authorization_check_config_ = std::make_shared( std::make_shared()); @@ -75,32 +64,35 @@ void ClientInstance::start() { } state_.store(State::STARTING, std::memory_order_relaxed); - scheduler_.start(); - - bool scheduled = - scheduler_.schedule(inactive_rpc_client_detector_function_, std::chrono::seconds(5), std::chrono::seconds(15)); - if (scheduled) { - SPDLOG_INFO("inactive client detector scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule inactive client detector"); - } + std::weak_ptr client_instance_weak_ptr = shared_from_this(); - scheduled = scheduler_.schedule(heartbeat_loop_function_, std::chrono::seconds(1), std::chrono::seconds(10)); - if (scheduled) { - SPDLOG_INFO("Heartbeat loop scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule consumer heartbeat loop"); - } + auto health_check_functor = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->doHealthCheck(); + } + }; + health_check_handle_ = scheduler_.schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), + std::chrono::seconds(5)); + auto heartbeat_functor = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->doHeartbeat(); + } + }; + heartbeat_handle_ = + scheduler_.schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); completion_queue_thread_ = std::thread(std::bind(&ClientInstance::pollCompletionQueue, this)); - scheduled = scheduler_.schedule(stats_function_, std::chrono::seconds(0), std::chrono::seconds(10)); - if (scheduled) { - SPDLOG_INFO("logStats loop scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule logStats loop"); - } - + auto stats_functor_ = [client_instance_weak_ptr]() { + auto client_instance = client_instance_weak_ptr.lock(); + if (client_instance) { + client_instance->logStats(); + } + }; + stats_handle_ = + scheduler_.schedule(stats_functor_, STATS_TASK_NAME, std::chrono::seconds(0), std::chrono::seconds(10)); state_.store(State::STARTED, std::memory_order_relaxed); } @@ -112,10 +104,16 @@ void ClientInstance::shutdown() { } state_.store(STOPPING, std::memory_order_relaxed); - { - absl::MutexLock lk(&inactive_rpc_clients_mtx_); - inactive_rpc_clients_.clear(); - SPDLOG_DEBUG("CompletionQueue of inactive clients stopped"); + if (health_check_handle_) { + scheduler_.cancel(health_check_handle_); + } + + if (heartbeat_handle_) { + scheduler_.cancel(heartbeat_handle_); + } + + if (stats_handle_) { + scheduler_.cancel(stats_handle_); } { @@ -124,8 +122,6 @@ void ClientInstance::shutdown() { SPDLOG_DEBUG("CompletionQueue of active clients stopped"); } - scheduler_.shutdown(); - completion_queue_->Shutdown(); if (completion_queue_thread_.joinable()) { completion_queue_thread_.join(); @@ -150,60 +146,34 @@ void ClientInstance::assignLabels(Histogram& histogram) { histogram.labels().emplace_back("[200ms~inf): "); } -bool ClientInstance::addInactiveRpcClient(const std::string& target_host) { - std::shared_ptr client; - // get the inactive rpc client and remove from rpc_clients_(active client) +void ClientInstance::healthCheck( + const std::string& target_host, const absl::flat_hash_map& metadata, + const HealthCheckRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { { - absl::MutexLock lock(&rpc_clients_mtx_); + absl::MutexLock lk(&rpc_clients_mtx_); if (!rpc_clients_.contains(target_host)) { - SPDLOG_ERROR("RpcClient {} can not be found in rpc_clients_, remove error", target_host); - return false; - } else { - client = rpc_clients_[target_host]; - rpc_clients_.erase(target_host); + SPDLOG_WARN("Try to perform health check for {}, which is unknown to ClientInstance", target_host); + cb(target_host, nullptr); + return; } } - // add to inactive_rpc_clients_ - { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - inactive_rpc_clients_.insert(std::make_pair(target_host, client)); - } - return true; -} -bool ClientInstance::removeInactiveRpcClient(const std::string& target_host) { - std::shared_ptr client; - { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - if (!inactive_rpc_clients_.contains(target_host)) { - SPDLOG_ERROR("RpcClient {} can not be found in inactive_rpc_clients_, remove error", target_host); - return false; - } else { - client = inactive_rpc_clients_[target_host]; - inactive_rpc_clients_.erase(target_host); - } - } + auto client = getRpcClient(target_host); + assert(client); - { - absl::MutexLock lock(&rpc_clients_mtx_); - rpc_clients_.insert(std::make_pair(target_host, client)); - } - SPDLOG_DEBUG("Migrated {} from inactive to active list", target_host); - return true; -} + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); -bool ClientInstance::getInactiveRpcClient(absl::string_view target_host, RpcClientSharedPtr& client) { - std::string target(target_host.data(), target_host.length()); - { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - if (!inactive_rpc_clients_.contains(target)) { - SPDLOG_DEBUG("RpcClient {} can not be found in inactive_rpc_clients_. Host:{}", target); - return false; - } else { - client = inactive_rpc_clients_[target]; - } + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); } - return true; + + auto callback = [cb](const InvocationContext* ctx) { cb(ctx->remote_address, ctx); }; + + invocation_context->callback = callback; + client->asyncHealthCheck(request, invocation_context); } void ClientInstance::doHealthCheck() { @@ -213,8 +183,23 @@ void ClientInstance::doHealthCheck() { SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); return; } + cleanOfflineRpcClients(); - clientHealthCheck(); + + std::vector> clients; + { + absl::MutexLock lk(&clients_mtx_); + for (auto& item : clients_) { + auto client = item.lock(); + if (client && client->active()) { + clients.emplace_back(std::move(client)); + } + } + } + + for (auto& client : clients) { + client->healthCheck(); + } SPDLOG_DEBUG("Health check completed"); } @@ -300,7 +285,7 @@ void ClientInstance::cleanOfflineRpcClients() { if (!client) { continue; } - client->activeHosts(hosts); + client->endpointsInUse(hosts); } } @@ -316,76 +301,6 @@ void ClientInstance::cleanOfflineRpcClients() { } } } - - { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - for (auto it = inactive_rpc_clients_.begin(); it != inactive_rpc_clients_.end();) { - std::string host = it->first; - if (it->second->needHeartbeat() && !hosts.contains(host)) { - inactive_rpc_clients_.erase(it++); - SPDLOG_INFO("Removed RPC client whose peer is offline. RemoteHost={}", host); - } else { - it++; - } - } - } -} - -bool ClientInstance::isRpcClientActive(absl::string_view target_host) { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - return !inactive_rpc_clients_.contains(target_host); -} - -void ClientInstance::getAllInactiveRpcClientHost(absl::flat_hash_set& client_hosts) { - client_hosts.clear(); - { - absl::MutexLock lock(&inactive_rpc_clients_mtx_); - for (const auto& entry : inactive_rpc_clients_) { - client_hosts.insert(entry.first); - } - } -} - -void ClientInstance::clientHealthCheck() { - absl::flat_hash_set clientsHosts; - getAllInactiveRpcClientHost(clientsHosts); - for (const auto& clientHost : clientsHosts) { - RpcClientSharedPtr client; - if (!getInactiveRpcClient(clientHost, client)) { - SPDLOG_ERROR("get inactive client error, client Host :{}", clientHost); - } - SPDLOG_DEBUG("check client(host: {}) health status", clientHost); - // detect if the rpc client is healthy - HealthCheckRequest request; - HealthCheckResponse response; - request.set_client_host(clientHost); - auto invocation_context = new InvocationContext(); - - // TODO: Acquire metadata from ProducerImpl/ConsumerImpl. - absl::flat_hash_map metadata; - - // TODO: Sign metadata - - for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); - } - - auto callback = [this, clientHost](const grpc::Status& status, const grpc::ClientContext& context, - const HealthCheckResponse& response) { - if (status.ok() && response.common().status().code() == google::rpc::Code::OK) { - SPDLOG_INFO("Inactive client {} passes health check, thus, is considered active", clientHost); - if (!removeInactiveRpcClient(clientHost)) { - SPDLOG_ERROR("Failed to move the previous inactive client to active list", clientHost); - } - } else { - SPDLOG_INFO("client for host={} failed to pass health check again. Reason: {}", clientHost, - response.common().DebugString()); - } - }; - invocation_context->callback_ = callback; - - client->asyncHealthCheck(request, invocation_context); - } // end for } void ClientInstance::heartbeat(const std::string& target_host, @@ -398,31 +313,38 @@ void ClientInstance::heartbeat(const std::string& target_host, } auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, - const HeartbeatResponse& response) { - if (status.ok()) { - if (google::rpc::Code::OK == response.common().status().code()) { - SPDLOG_DEBUG("Send heartbeat to target_host={}, gRPC status OK", target_host); - cb(true, response); + auto callback = [cb](const InvocationContext* invocation_context) { + if (invocation_context->status.ok()) { + if (google::rpc::Code::OK == invocation_context->response.common().status().code()) { + SPDLOG_DEBUG("Send heartbeat to target_host={}, gRPC status OK", invocation_context->remote_address); + cb(true, invocation_context->response); } else { - SPDLOG_WARN("Server[{}] failed to process heartbeat. Reason: {}", target_host, response.common().DebugString()); - cb(false, response); + SPDLOG_WARN("Server[{}] failed to process heartbeat. Reason: {}", invocation_context->remote_address, + invocation_context->response.common().DebugString()); + cb(false, invocation_context->response); } } else { - SPDLOG_WARN("Failed to send heartbeat to target_host={}. GRPC code: {}, message : {}", target_host, - status.error_code(), status.error_message()); - cb(false, response); + SPDLOG_WARN("Failed to send heartbeat to target_host={}. GRPC code: {}, message : {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + cb(false, invocation_context->response); } }; - invocation_context->callback_ = callback; - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); client->asyncHeartbeat(request, invocation_context); } +bool ClientInstance::active() { + State state = state_.load(std::memory_order_relaxed); + return State::STARTED == state || State::STARTING == state; +} + void ClientInstance::doHeartbeat() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { @@ -430,15 +352,20 @@ void ClientInstance::doHeartbeat() { return; } + std::vector> clients; { absl::MutexLock lk(&clients_mtx_); for (const auto& item : clients_) { auto client = item.lock(); - if (client) { - client->heartbeat(); + if (client && client->active()) { + clients.emplace_back(std::move(client)); } } } + + for (auto& client : clients) { + client->heartbeat(); + } } void ClientInstance::pollCompletionQueue() { @@ -454,17 +381,16 @@ void ClientInstance::pollCompletionQueue() { SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); } auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; - callback_thread_pool_->enqueue(callback); + callback_thread_pool_->Add(callback); } SPDLOG_INFO("CompletionQueue is fully drained and shut down"); } SPDLOG_INFO("pollCompletionQueue completed and quit"); } -bool ClientInstance::send(const std::string& target, const absl::flat_hash_map& metadata, - SendMessageRequest& request, SendCallback* callback) { - assert(callback); - std::string target_host(target.data(), target.length()); +bool ClientInstance::send(const std::string& target_host, const absl::flat_hash_map& metadata, + SendMessageRequest& request, SendCallback* cb) { + assert(cb); SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); RpcClientSharedPtr client = getRpcClient(target_host); @@ -487,30 +413,33 @@ bool ClientInstance::send(const std::string& target, const absl::flat_hash_map(); + invocation_context->remote_address = target_host; for (const auto& entry : metadata) { - invocation_context->context_.AddMetadata(entry.first, entry.second); + invocation_context->context.AddMetadata(entry.first, entry.second); } const std::string& topic = request.message().topic().name(); - auto completion_callback = [topic, callback, target_host, span, this](const grpc::Status& status, - const grpc::ClientContext&, - const SendMessageResponse& response) { - if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { + auto completion_callback = [topic, cb, span, this](const InvocationContext* invocation_context) { + if (invocation_context->status.ok() && + google::rpc::Code::OK == invocation_context->response.common().status().code()) { + #ifdef ENABLE_TRACING if (span) { span->SetAttribute(TracingUtility::get().success_, true); span->End(); } +#else + (void)span; #endif SendResult send_result; - send_result.setMsgId(response.message_id()); + send_result.setMsgId(invocation_context->response.message_id()); send_result.setQueueOffset(-1); MQMessageQueue message_queue; message_queue.setQueueId(-1); message_queue.setTopic(topic); send_result.setMessageQueue(message_queue); if (State::STARTED == state_.load(std::memory_order_relaxed)) { - callback->onSuccess(send_result); + cb->onSuccess(send_result); } else { SPDLOG_INFO("Client instance has stopped, state={}. Ignore send result {}", state_.load(std::memory_order_relaxed), send_result.getMsgId()); @@ -524,22 +453,23 @@ bool ClientInstance::send(const std::string& target, const absl::flat_hash_mapstatus.ok()) { SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", - target_host, status.error_code(), status.error_message()); + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); } std::string msg; msg.append("gRPC code: ") - .append(std::to_string(status.error_code())) + .append(std::to_string(invocation_context->status.error_code())) .append(", gRPC message: ") - .append(status.error_message()) + .append(invocation_context->status.error_message()) .append(", code: ") - .append(std::to_string(response.common().status().code())) + .append(std::to_string(invocation_context->response.common().status().code())) .append(", remark: ") - .append(response.common().DebugString()); + .append(invocation_context->response.common().DebugString()); MQException e(msg, FAILED_TO_SEND_MESSAGE, __FILE__, __LINE__); if (State::STARTED == state_.load(std::memory_order_relaxed)) { - callback->onException(e); + cb->onException(e); } else { SPDLOG_WARN("Client instance has stopped, state={}. Ignore exception raised while sending message: {}", state_.load(std::memory_order_relaxed), e.what()); @@ -547,7 +477,7 @@ bool ClientInstance::send(const std::string& target, const absl::flat_hash_mapcallback_ = completion_callback; + invocation_context->callback = completion_callback; client->asyncSend(request, invocation_context); return true; } @@ -574,6 +504,10 @@ RpcClientSharedPtr ClientInstance::getRpcClient(const std::string& target_host, } } + if (need_heartbeat && !client->needHeartbeat()) { + client->needHeartbeat(need_heartbeat); + } + return client; } @@ -584,6 +518,11 @@ void ClientInstance::addRpcClient(const std::string& target_host, const RpcClien } } +void ClientInstance::cleanRpcClients() { + absl::MutexLock lk(&rpc_clients_mtx_); + rpc_clients_.clear(); +} + SendResult ClientInstance::processSendResponse(const MQMessageQueue& message_queue, const SendMessageResponse& response) { if (google::rpc::Code::OK != response.common().status().code()) { @@ -616,28 +555,28 @@ void ClientInstance::resolveRoute(const std::string& target_host, } auto invocation_context = new InvocationContext(); - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& client_context, - const QueryRouteResponse& response) { - if (!status.ok()) { - SPDLOG_WARN("Failed to send query route request to server[host={}]. Reason: {}", target_host, - status.error_message()); + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send query route request to server[host={}]. Reason: {}", + invocation_context->remote_address, invocation_context->status.error_message()); cb(false, nullptr); return; } - if (google::rpc::Code::OK != response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query route request. Reason: {}", target_host, - response.common().DebugString()); + if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query route request. Reason: {}", + invocation_context->remote_address, invocation_context->response.common().DebugString()); cb(false, nullptr); return; } - auto& partitions = response.partitions(); + auto& partitions = invocation_context->response.partitions(); std::vector topic_partitions; for (const auto& partition : partitions) { @@ -685,10 +624,11 @@ void ClientInstance::resolveRoute(const std::string& target_host, topic_partitions.emplace_back(std::move(topic_partition)); } - auto ptr = std::make_shared(std::move(topic_partitions), response.DebugString()); + auto ptr = + std::make_shared(std::move(topic_partitions), invocation_context->response.DebugString()); cb(true, ptr); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncQueryRoute(request, invocation_context); } @@ -699,105 +639,106 @@ void ClientInstance::queryAssignment(const std::string& target, SPDLOG_DEBUG("Prepare to send query assignment request to broker[address={}]", target); std::shared_ptr client = getRpcClient(target); - auto callback = [&, target, cb](const grpc::Status& status, const grpc::ClientContext& context, - const QueryAssignmentResponse& response) { - if (!status.ok()) { - SPDLOG_WARN("Failed to query assignment. Reason: {}", status.error_message()); - cb(false, response); + auto callback = [&, cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to query assignment. Reason: {}", invocation_context->status.error_message()); + cb(false, invocation_context->response); return; } - if (google::rpc::Code::OK != response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query assignment request. Reason: {}", target, - response.common().DebugString()); - cb(false, response); + if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query assignment request. Reason: {}", + invocation_context->remote_address, invocation_context->response.common().DebugString()); + cb(false, invocation_context->response); return; } - cb(true, response); + cb(true, invocation_context->response); }; auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target; for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); - invocation_context->callback_ = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->callback = callback; client->asyncQueryAssignment(request, invocation_context); } -void ClientInstance::receiveMessage(absl::string_view target, +void ClientInstance::receiveMessage(const std::string& target_host, const absl::flat_hash_map& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, std::shared_ptr& cb) { - std::string target_host(target.data(), target.length()); SPDLOG_DEBUG("Prepare to pop message from {} asynchronously. Request: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); - + invocation_context->remote_address = target_host; if (!metadata.empty()) { for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } } - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - auto callback = [this, cb, target_host](const grpc::Status& status, const grpc::ClientContext& client_context, - const ReceiveMessageResponse& response) { - if (status.ok()) { - SPDLOG_DEBUG("Received pop response through gRPC from brokerAddress={}", target_host); + auto callback = [this, cb](const InvocationContext* invocation_context) { + if (invocation_context->status.ok()) { + SPDLOG_DEBUG("Received pop response through gRPC from brokerAddress={}", invocation_context->remote_address); ReceiveMessageResult receive_result; - this->processPopResult(client_context, response, receive_result, target_host); + this->processPopResult(invocation_context->context, invocation_context->response, receive_result, + invocation_context->remote_address); cb->onSuccess(receive_result); } else { - SPDLOG_WARN("Failed to pop messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", target_host, - status.error_code(), status.error_message()); - MQException e(status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, __LINE__); + SPDLOG_WARN("Failed to pop messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + MQException e(invocation_context->status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, + __LINE__); cb->onException(e); } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncReceive(request, invocation_context); } -void ClientInstance::pullMessage(absl::string_view target, absl::flat_hash_map& metadata, - const PullMessageRequest& request, - std::shared_ptr& receive_callback) { - std::string target_host(target.data(), target.length()); +void ClientInstance::pullMessage(const std::string& target_host, + absl::flat_hash_map& metadata, + const PullMessageRequest& request, std::shared_ptr& cb) { SPDLOG_DEBUG("Prepare to pull message from {} asynchronously", target_host); RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; if (!metadata.empty()) { for (const auto& entry : metadata) { - invocation_context->context_.AddMetadata(entry.first, entry.second); + invocation_context->context.AddMetadata(entry.first, entry.second); } } - auto callback = [this, receive_callback, target_host](const grpc::Status& status, - const grpc::ClientContext& client_context, - const PullMessageResponse& response) { - if (status.ok()) { - SPDLOG_DEBUG("Received message response through gRPC from brokerAddress={}", target_host); + auto callback = [this, cb](const InvocationContext* invocation_context) { + if (invocation_context->status.ok()) { + SPDLOG_DEBUG("Received message response through gRPC from brokerAddress={}", invocation_context->remote_address); ReceiveMessageResult receive_result; - this->processPullResult(client_context, response, receive_result, target_host); - receive_callback->onSuccess(receive_result); + this->processPullResult(invocation_context->context, invocation_context->response, receive_result, + invocation_context->remote_address); + cb->onSuccess(receive_result); } else { - SPDLOG_WARN("Failed to pull messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", target_host, - status.error_code(), status.error_message()); - MQException e(status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, __LINE__); - receive_callback->onException(e); + SPDLOG_WARN("Failed to pull messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + MQException e(invocation_context->status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, + __LINE__); + cb->onException(e); } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncPull(request, invocation_context); } void ClientInstance::processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, - ReceiveMessageResult& result, absl::string_view target_host) { + ReceiveMessageResult& result, const std::string& target_host) { // process response to result ReceiveMessageStatus status; - std::string broker_address = std::string(target_host.data(), target_host.length()); switch (response.common().status().code()) { case google::rpc::Code::OK: status = ReceiveMessageStatus::OK; @@ -805,7 +746,7 @@ void ClientInstance::processPopResult(const grpc::ClientContext& client_context, case google::rpc::Code::RESOURCE_EXHAUSTED: status = ReceiveMessageStatus::RESOURCE_EXHAUSTED; SPDLOG_WARN("Too many pop requests in broker. Long polling is full in the broker side. BrokerAddress={}", - broker_address); + target_host); break; case google::rpc::Code::DEADLINE_EXCEEDED: status = ReceiveMessageStatus::DEADLINE_EXCEEDED; @@ -814,28 +755,20 @@ void ClientInstance::processPopResult(const grpc::ClientContext& client_context, status = ReceiveMessageStatus::NOT_FOUND; break; default: - SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", broker_address, + SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", target_host, response.common().DebugString()); status = ReceiveMessageStatus::INTERNAL; break; } - const auto& metadata = client_context.GetServerInitialMetadata(); - auto search = metadata.find(Metadata::REQUEST_ID_KEY); - if (metadata.end() == search) { - SPDLOG_WARN("Expected header {} is missing", Metadata::AUTHORIZATION); - } else { - result.requestId(std::string(search->second.data(), search->second.length())); - } - - result.sourceHost(broker_address); + result.sourceHost(target_host); std::vector msg_found_list; if (ReceiveMessageStatus::OK == status) { for (auto& item : response.messages()) { MQMessageExt message_ext; if (wrapMessage(item, message_ext)) { - MessageAccessor::setTargetEndpoint(message_ext, broker_address); + MessageAccessor::setTargetEndpoint(message_ext, target_host); msg_found_list.emplace_back(message_ext); } } @@ -856,12 +789,12 @@ void ClientInstance::processPopResult(const grpc::ClientContext& client_context, } void ClientInstance::processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, absl::string_view target_host) { + ReceiveMessageResult& result, const std::string& target_host) { if (google::rpc::Code::OK != response.common().status().code()) { result.status_ = ReceiveMessageStatus::INTERNAL; return; } - result.sourceHost(std::string(target_host.data(), target_host.length())); + result.sourceHost(target_host); switch (response.common().status().code()) { case google::rpc::Code::OK: { assert(!response.messages().empty()); @@ -1101,32 +1034,34 @@ bool ClientInstance::wrapMessage(const rmq::Message& item, MQMessageExt& message Scheduler& ClientInstance::getScheduler() { return scheduler_; } +TopAddressing& ClientInstance::topAddressing() { return top_addressing_; } + void ClientInstance::ack(const std::string& target, const absl::flat_hash_map& metadata, const AckMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& completion_callback) { + const std::function& cb) { std::string target_host(target.data(), target.length()); SPDLOG_DEBUG("Prepare to ack message against {} asynchronously. AckMessageRequest: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } // TODO: Use capture by move and pass-by-value paradigm when C++ 14 is available. - auto callback = [target_host, request, completion_callback](const grpc::Status& status, - const grpc::ClientContext& client_context, - const AckMessageResponse& response) { - if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { - completion_callback(true); + auto callback = [request, cb](const InvocationContext* invocation_context) { + if (invocation_context->status.ok() && + google::rpc::Code::OK == invocation_context->response.common().status().code()) { + cb(true); } else { - completion_callback(false); + cb(false); } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncAck(request, invocation_context); } @@ -1136,22 +1071,22 @@ void ClientInstance::nack(const std::string& target_host, const absl::flat_hash_ RpcClientSharedPtr client = getRpcClient(target_host); assert(client); auto invocation_context = new InvocationContext(); - - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [completion_callback](const grpc::Status& status, const grpc::ClientContext& context, - const NackMessageResponse& response) { - if (status.ok() && google::rpc::Code::OK == response.common().status().code()) { + auto callback = [completion_callback](const InvocationContext* invocation_context) { + if (invocation_context->status.ok() && + google::rpc::Code::OK == invocation_context->response.common().status().code()) { completion_callback(true); } else { completion_callback(false); } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncNack(request, invocation_context); } @@ -1170,66 +1105,67 @@ void ClientInstance::endTransaction(const std::string& target_host, SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } // Set RPC deadline. auto deadline = std::chrono::system_clock::now() + timeout; - invocation_context->context_.set_deadline(deadline); + invocation_context->context.set_deadline(deadline); - auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, - const EndTransactionResponse& response) { - if (!status.ok() || google::rpc::Code::OK != response.common().status().code()) { + auto callback = [target_host, cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok() || + google::rpc::Code::OK != invocation_context->response.common().status().code()) { SPDLOG_WARN("Failed to endTransaction. TargetHost={}, gRPC statusCode={}, errorMessage={}", target_host.data(), - status.error_message(), status.error_message()); - cb(false, response); + invocation_context->status.error_message(), invocation_context->status.error_message()); + cb(false, invocation_context->response); return; } - SPDLOG_DEBUG("endTransaction completed OK. Response: {}", response.DebugString()); - cb(true, response); + SPDLOG_DEBUG("endTransaction completed OK. Response: {}", invocation_context->response.DebugString()); + cb(true, invocation_context->response); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncEndTransaction(request, invocation_context); } void ClientInstance::multiplexingCall(const std::string& target_host, const absl::flat_hash_map& metadata, const MultiplexingRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { + const std::function*)>& cb) { RpcClientSharedPtr client = getRpcClient(target_host); if (!client) { SPDLOG_WARN("No RPC client for {}", target_host); - MultiplexingResponse response; - cb(false, response); + cb(nullptr); return; } SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } // Set RPC deadline. auto deadline = std::chrono::system_clock::now() + timeout; - invocation_context->context_.set_deadline(deadline); + invocation_context->context.set_deadline(deadline); - auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& context, - const MultiplexingResponse& response) { - if (!status.ok()) { + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to apply multiplexing-call. TargetHost={}, gRPC statusCode={}, errorMessage={}", - target_host.data(), status.error_message(), status.error_message()); - cb(false, response); + invocation_context->remote_address, invocation_context->status.error_message(), + invocation_context->status.error_message()); + cb(invocation_context); return; } - SPDLOG_DEBUG("endTransaction completed OK. Response: {}", response.DebugString()); - cb(true, response); + SPDLOG_DEBUG("endTransaction completed OK. Response: {}", invocation_context->response.DebugString()); + cb(invocation_context); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncMultiplexingCall(request, invocation_context); } @@ -1245,23 +1181,23 @@ void ClientInstance::pullMessage(const std::string& target_host, } auto invocation_context = new InvocationContext(); - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [target_host, cb](const grpc::Status& status, const grpc::ClientContext& client_context, - const PullMessageResponse& response) { - if (!status.ok()) { - SPDLOG_WARN("Failed to send pullMessage request to {}", target_host); - cb(false, response); + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send pullMessage request to {}", invocation_context->remote_address); + cb(false, invocation_context->response); return; } - SPDLOG_DEBUG("Received pullMessage response from server[host={}]", target_host); - cb(true, response); + SPDLOG_DEBUG("Received pullMessage response from server[host={}]", invocation_context->remote_address); + cb(true, invocation_context->response); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncPull(request, invocation_context); } @@ -1272,4 +1208,8 @@ void ClientInstance::logStats() { SPDLOG_INFO("{}", stats); } +const char* ClientInstance::HEARTBEAT_TASK_NAME = "heartbeat-task"; +const char* ClientInstance::STATS_TASK_NAME = "stats-task"; +const char* ClientInstance::HEALTH_CHECK_TASK_NAME = "health-check-task"; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index b2311405e..70a7ad959 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -12,90 +12,90 @@ ROCKETMQ_NAMESPACE_BEGIN void RpcClientImpl::asyncQueryRoute(const QueryRouteRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader_ = - stub_->PrepareAsyncQueryRoute(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncQueryRoute(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncSend(const SendMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader_ = - stub_->PrepareAsyncSendMessage(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncSendMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncQueryAssignment(const QueryAssignmentRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader_ = - stub_->PrepareAsyncQueryAssignment(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncQueryAssignment(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { return completion_queue_; } void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader_ = - stub_->PrepareAsyncReceiveMessage(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncReceiveMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncAckMessage(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncAckMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncNack(const NackMessageRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncNackMessage(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncNackMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncHeartbeat(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncHeartbeat(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncHealthCheck(const HealthCheckRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncHealthCheck(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncHealthCheck(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncEndTransaction(const EndTransactionRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncEndTransaction(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncEndTransaction(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } bool RpcClientImpl::ok() const { @@ -111,33 +111,35 @@ void RpcClientImpl::addMetadata(grpc::ClientContext& context, bool RpcClientImpl::needHeartbeat() { return need_heartbeat_; } +void RpcClientImpl::needHeartbeat(bool need_heartbeat) { need_heartbeat_ = need_heartbeat; } + void RpcClientImpl::asyncMultiplexingCall(const MultiplexingRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncMultiplexingCall(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncMultiplexingCall(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncQueryOffset(const QueryOffsetRequest& request, InvocationContext* invocation_context) { assert(invocation_context); - invocation_context->response_reader_ = - stub_->PrepareAsyncQueryOffset(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncQueryOffset(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } void RpcClientImpl::asyncPull(const PullMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader_ = - stub_->PrepareAsyncPullMessage(&invocation_context->context_, request, completion_queue_.get()); - invocation_context->response_reader_->StartCall(); - invocation_context->response_reader_->Finish(&invocation_context->response_, &invocation_context->status_, - invocation_context); + invocation_context->response_reader = + stub_->PrepareAsyncPullMessage(&invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index e4569aff1..805d7627a 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -1,7 +1,6 @@ #include "Signature.h" #include "Metadata.h" #include "Protocol.h" -#include "RequestIdGenerator.h" #include "TlsHelper.h" #include "spdlog/spdlog.h" @@ -10,11 +9,6 @@ ROCKETMQ_NAMESPACE_BEGIN void Signature::sign(ClientConfig* client, absl::flat_hash_map& metadata) { assert(client); - auto search = metadata.find(Metadata::REQUEST_ID_KEY); - if (search == metadata.end()) { - metadata.insert({Metadata::REQUEST_ID_KEY, RequestIdGenerator::nextRequestId()}); - } - metadata.insert({Metadata::LANGUAGE_KEY, "CPP"}); // Add common headers metadata.insert({Metadata::CLIENT_VERSION_KEY, ClientConfig::CLIENT_VERSION}); diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp index 6e6ba743d..657d0863c 100644 --- a/src/main/cpp/client/TopicPublishInfo.cpp +++ b/src/main/cpp/client/TopicPublishInfo.cpp @@ -34,7 +34,7 @@ bool TopicPublishInfo::selectOneActiveMessageQueue(absl::flat_hash_set& isolated, - std::vector& candidates, int count) { + std::vector& candidates, uint32_t count) { unsigned int index = ++send_which_queue_; { diff --git a/src/main/cpp/client/include/ClientCallback.h b/src/main/cpp/client/include/ClientCallback.h index 75df022e0..7cd0a4a62 100644 --- a/src/main/cpp/client/include/ClientCallback.h +++ b/src/main/cpp/client/include/ClientCallback.h @@ -1,5 +1,5 @@ -#include "rocketmq/RocketMQ.h" #include "absl/container/flat_hash_set.h" +#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -7,9 +7,18 @@ class ClientCallback { public: virtual ~ClientCallback() = default; - virtual void activeHosts(absl::flat_hash_set& hosts) = 0; + virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; virtual void heartbeat() = 0; + + virtual bool active() = 0; + + /** + * For endpoints that are marked as inactive due to one or multiple business operation failure, this function is to + * initiate health-check RPCs; Once the health-check passes, they are conceptually add back to serve further business + * workload. + */ + virtual void healthCheck() = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 110f469bf..028ad90cd 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -39,6 +39,10 @@ class ClientConfig : public Identifiable { absl::Duration getIoTimeout() const; void setIoTimeout(absl::Duration timeout); + absl::Duration getLongPollingTimeout() const { return long_polling_timeout_; } + + void setLongPollingTimeout(absl::Duration timeout) { long_polling_timeout_ = timeout; } + bool isTracingEnabled() { return enable_tracing_.load(); } void enableTracing(bool enabled) { enable_tracing_.store(enabled); } @@ -86,6 +90,9 @@ class ClientConfig : public Identifiable { CredentialsProviderPtr credentials_provider_; absl::Duration io_timeout_; + + absl::Duration long_polling_timeout_; + std::atomic enable_tracing_{false}; }; diff --git a/src/main/cpp/client/include/ClientInstance.h b/src/main/cpp/client/include/ClientInstance.h index 647bf14db..48ec920f2 100644 --- a/src/main/cpp/client/include/ClientInstance.h +++ b/src/main/cpp/client/include/ClientInstance.h @@ -5,20 +5,20 @@ #include #include #include +#include #include #include "ClientCallback.h" #include "ClientConfig.h" -#include "Functional.h" #include "HeartbeatDataCallback.h" #include "Histogram.h" #include "Identifiable.h" +#include "InvocationContext.h" #include "OrphanTransactionCallback.h" #include "ReceiveMessageCallback.h" #include "RpcClientImpl.h" #include "Scheduler.h" #include "SendMessageContext.h" -#include "ThreadPool.h" #include "TopAddressing.h" #include "TopicRouteChangeCallback.h" #include "TopicRouteData.h" @@ -27,6 +27,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "src/cpp/server/thread_pool_interface.h" #ifdef ENABLE_TRACING #include "opentelemetry/exporters/otlp/otlp_exporter.h" @@ -49,7 +50,7 @@ class ClientInstance : public std::enable_shared_from_this { void start(); - void shutdown() LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + void shutdown() LOCKS_EXCLUDED(rpc_clients_mtx_); static void assignLabels(Histogram& histogram); @@ -66,36 +67,24 @@ class ClientInstance : public std::enable_shared_from_this { const QueryRouteRequest& request, std::chrono::milliseconds timeout, const std::function& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); - bool addInactiveRpcClient(const std::string& target_host) LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); - - bool removeInactiveRpcClient(const std::string& target_host) - LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); - - void doHealthCheck(); + void doHealthCheck() LOCKS_EXCLUDED(clients_mtx_); /** * If inactive RPC clients refer to remote hosts that are absent from topic_route_table_, we need to purge them * immediately. */ - void cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_, inactive_rpc_clients_mtx_); + void cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_); /** - * Check if the RPC client is active or not. - * @param target_host Target host address in form of IP:port. - * @return true if the client for the target host is not temporarily blocked, aka, in inactive_rpc_clients_ map. + * Execute health-check on behalf of the client. */ - bool isRpcClientActive(absl::string_view target_host) LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); - - void getAllInactiveRpcClientHost(absl::flat_hash_set& client_hosts) - LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); - - bool getInactiveRpcClient(absl::string_view target_host, RpcClientSharedPtr& client) - LOCKS_EXCLUDED(inactive_rpc_clients_mtx_); - - void clientHealthCheck() LOCKS_EXCLUDED(inactive_rpc_clients_mtx_, rpc_clients_mtx_); + void healthCheck(const std::string& target_host, const absl::flat_hash_map& metadata, + const HealthCheckRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) + LOCKS_EXCLUDED(rpc_clients_mtx_); - bool send(const std::string& target, const absl::flat_hash_map& metadata, - SendMessageRequest& request, SendCallback* callback) LOCKS_EXCLUDED(rpc_clients_mtx_); + bool send(const std::string& target_host, const absl::flat_hash_map& metadata, + SendMessageRequest& request, SendCallback* cb) LOCKS_EXCLUDED(rpc_clients_mtx_); /** * Get a RpcClient according to the given target hosts, which follows scheme specified @@ -116,32 +105,43 @@ class ClientInstance : public std::enable_shared_from_this { // only for test void addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) LOCKS_EXCLUDED(rpc_clients_mtx_); + // Test purpose only + void cleanRpcClients() LOCKS_EXCLUDED(rpc_clients_mtx_); + void addClientObserver(std::weak_ptr client); void queryAssignment(const std::string& target, const absl::flat_hash_map& metadata, const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, const std::function& cb); - void receiveMessage(absl::string_view target, const absl::flat_hash_map& metadata, + void receiveMessage(const std::string& target, const absl::flat_hash_map& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, std::shared_ptr& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); - void pullMessage(absl::string_view target, absl::flat_hash_map& metadata, - const PullMessageRequest& request, std::shared_ptr& callback) + void pullMessage(const std::string& target, absl::flat_hash_map& metadata, + const PullMessageRequest& request, std::shared_ptr& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + /** + * Translate protobuf message struct to domain model. + * + * @param item + * @param message_ext + * @return true if the translation succeeded; false if something wrong happens, including checksum verification, etc. + */ bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext); Scheduler& getScheduler(); + TopAddressing& topAddressing(); + /** * Ack message asynchronously. * @param target_host Target broker host address. * @param request Ack message request. */ void ack(const std::string& target_host, const absl::flat_hash_map& metadata, - const AckMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& callback); + const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb); void nack(const std::string& target_host, const absl::flat_hash_map& metadata, const NackMessageRequest& request, std::chrono::milliseconds timeout, @@ -167,12 +167,11 @@ class ClientInstance : public std::enable_shared_from_this { void multiplexingCall(const std::string& target, const absl::flat_hash_map& metadata, const MultiplexingRequest& request, std::chrono::milliseconds timeout, - const std::function& cb); + const std::function*)>& cb); template void queryOffset(const std::string& target_host, const absl::flat_hash_map& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, const Callable& cb) { - auto client = getRpcClient(target_host); if (!client) { SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); @@ -180,25 +179,26 @@ class ClientInstance : public std::enable_shared_from_this { } auto invocation_context = new InvocationContext(); - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + timeout); - auto callback = [cb, target_host](const grpc::Status& status, const grpc::ClientContext& client_context, - const QueryOffsetResponse& response) { - if (!status.ok()) { - SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", target_host, status.error_message()); - cb(false, response); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", invocation_context->remote_address, + invocation_context->status.error_message()); + cb(false, invocation_context->response); return; } - if (google::rpc::Code::OK != response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", target_host, - response.common().DebugString()); - cb(false, response); + if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", + invocation_context->remote_address, invocation_context->response.common().DebugString()); + cb(false, invocation_context->response); } - SPDLOG_DEBUG("Query offset from server[host={}] OK", target_host); - cb(true, response); + SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); + cb(true, invocation_context->response); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncQueryOffset(request, invocation_context); } @@ -219,10 +219,12 @@ class ClientInstance : public std::enable_shared_from_this { private: void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, - ReceiveMessageResult& result, absl::string_view target_host); + ReceiveMessageResult& result, const std::string& target_host); void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, absl::string_view target_host); + ReceiveMessageResult& result, const std::string& target_host); + + bool active(); void doHeartbeat(); @@ -232,6 +234,10 @@ class ClientInstance : public std::enable_shared_from_this { Scheduler scheduler_; + static const char* HEARTBEAT_TASK_NAME; + static const char* STATS_TASK_NAME; + static const char* HEALTH_CHECK_TASK_NAME; + /** * Abstract resource namespace. Each user may have one or more instances and each each instance has an independent * abstract resource namespace. @@ -246,23 +252,16 @@ class ClientInstance : public std::enable_shared_from_this { absl::flat_hash_map> rpc_clients_ GUARDED_BY(rpc_clients_mtx_); absl::Mutex rpc_clients_mtx_; // protects rpc_clients_ - absl::flat_hash_map> - inactive_rpc_clients_ GUARDED_BY(inactive_rpc_clients_mtx_); - absl::Mutex inactive_rpc_clients_mtx_; - std::function inactive_rpc_client_detector_; - FunctionalSharePtr inactive_rpc_client_detector_function_; - - std::function heartbeat_loop_; - FunctionalSharePtr heartbeat_loop_function_; + std::uintptr_t heartbeat_handle_{0}; + std::uintptr_t health_check_handle_{0}; + std::uintptr_t stats_handle_{0}; std::shared_ptr completion_queue_; - std::shared_ptr callback_thread_pool_; + std::unique_ptr callback_thread_pool_; std::thread completion_queue_thread_; Histogram latency_histogram_; - std::function stats_functor_; - FunctionalSharePtr stats_function_; absl::flat_hash_set exporter_endpoint_set_ GUARDED_BY(exporter_endpoint_set_mtx_); absl::Mutex exporter_endpoint_set_mtx_; @@ -284,6 +283,8 @@ class ClientInstance : public std::enable_shared_from_this { grpc::ChannelArguments channel_arguments_; bool trace_{false}; + + TopAddressing top_addressing_; }; using ClientInstancePtr = std::shared_ptr; diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index 8de0a64b9..a7200d67c 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -65,7 +65,6 @@ struct ReceiveMessageResult { pop_time_ = other.pop_time_; invisible_time_ = other.invisible_time_; messages_ = other.messages_; - request_id_ = other.request_id_; source_host_ = other.source_host_; next_offset_ = other.next_offset_; } @@ -78,7 +77,6 @@ struct ReceiveMessageResult { pop_time_ = other.pop_time_; invisible_time_ = other.invisible_time_; messages_ = other.messages_; - request_id_ = other.request_id_; source_host_ = other.source_host_; next_offset_ = other.next_offset_; return *this; @@ -111,32 +109,10 @@ struct ReceiveMessageResult { const std::vector& getMsgFoundList() const { return messages_; } - const std::string& requestId() const { return request_id_; } - - void requestId(std::string request_id) { request_id_ = std::move(request_id); } - const std::string& sourceHost() const { return source_host_; } void sourceHost(std::string source_host) { source_host_ = std::move(source_host); } - bool termId(int64_t& term_id) const { - if (request_id_.empty()) { - return false; - } - std::vector bin; - bin.reserve(16); - if (!MixAll::hexToBinary(request_id_, bin)) { - SPDLOG_WARN("Invalid request_id: {}", request_id_); - return false; - } - // IP(int32_t) + PID(int32_t) + term_id(int64_t) - assert(bin.size() == 4 + 4 + 8); - - auto ptr = reinterpret_cast(bin.data()); - term_id = *(ptr + 1); - return true; - } - ReceiveMessageStatus status_{ReceiveMessageStatus::DEADLINE_EXCEEDED}; absl::Time pop_time_; @@ -144,11 +120,6 @@ struct ReceiveMessageResult { std::vector messages_; - /** - * Follow format of hex{int32(IPv4) + int32(pid) + int64(sequence)} - */ - std::string request_id_; - std::string source_host_; int64_t next_offset_{0}; diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index 740a0aab4..b1c24bbe2 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -103,6 +103,8 @@ class RpcClient { */ virtual bool needHeartbeat() = 0; + virtual void needHeartbeat(bool need_heartbeat) = 0; + /** * Indicate if current client connection state is OK or recoverable. * diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index 9cf634666..5008a461e 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -59,6 +59,8 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this& isolated, MQMessageQueue& message_queue) LOCKS_EXCLUDED(partition_list_mtx_); - bool takeMessageQueues(absl::flat_hash_set& isolated, std::vector& candidates, int count) - LOCKS_EXCLUDED(partition_list_mtx_); + bool takeMessageQueues(absl::flat_hash_set& isolated, std::vector& candidates, + uint32_t count) LOCKS_EXCLUDED(partition_list_mtx_); void topicRouteData(TopicRouteDataPtr topic_route_data); diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index feb136a73..b7e8b069b 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -2,6 +2,7 @@ #include "RpcClient.h" #include "gmock/gmock.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -9,7 +10,9 @@ class RpcClientMock : public RpcClient { public: RpcClientMock(); - ~RpcClientMock() override = default; + ~RpcClientMock() override { + std::cout << "~RpcClientMock()" << std::endl; + } MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); @@ -43,6 +46,8 @@ class RpcClientMock : public RpcClient { MOCK_METHOD(bool, needHeartbeat, (), (override)); + MOCK_METHOD(void, needHeartbeat, (bool), (override)); + MOCK_METHOD(bool, ok, (), (const override)); protected: diff --git a/src/main/cpp/concurrent/BUILD.bazel b/src/main/cpp/concurrent/BUILD.bazel index ecb133091..7e82ddedf 100644 --- a/src/main/cpp/concurrent/BUILD.bazel +++ b/src/main/cpp/concurrent/BUILD.bazel @@ -10,6 +10,7 @@ cc_library( strip_include_prefix = "//src/main/cpp/concurrent/include", deps = [ "//src/main/cpp/base:base_library", + "//src/main/cpp/log:log_library", "@com_google_absl//absl/base", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", diff --git a/src/main/cpp/concurrent/CountdownLatch.cpp b/src/main/cpp/concurrent/CountdownLatch.cpp index a839d8e50..6347bb193 100644 --- a/src/main/cpp/concurrent/CountdownLatch.cpp +++ b/src/main/cpp/concurrent/CountdownLatch.cpp @@ -1,7 +1,7 @@ #include "CountdownLatch.h" #include "LoggerImpl.h" -using namespace rocketmq; +ROCKETMQ_NAMESPACE_BEGIN void CountdownLatch::await() { absl::MutexLock lock(&mtx_); @@ -32,3 +32,5 @@ void CountdownLatch::increaseCount() { SPDLOG_TRACE("After increaseCount(), latch[{}]={}", name_, count_); } } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h index 2a1e869f7..d873375af 100644 --- a/src/main/cpp/concurrent/include/CountdownLatch.h +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -3,9 +3,11 @@ #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN + class CountdownLatch { public: explicit CountdownLatch(int32_t count) : CountdownLatch(count, "anonymous") {} @@ -25,4 +27,5 @@ class CountdownLatch { std::string name_; }; -} // namespace rocketmq + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/log/BUILD.bazel b/src/main/cpp/log/BUILD.bazel index 317f1fec9..b0d79b491 100644 --- a/src/main/cpp/log/BUILD.bazel +++ b/src/main/cpp/log/BUILD.bazel @@ -1,9 +1,13 @@ load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "log_library", + hdrs = [ + "include/LoggerImpl.h", + ], srcs = [ "Logger.cpp", ], + strip_include_prefix = "//src/main/cpp/log/include", deps = [ "//api:rocketmq_interface", "@com_github_gabime_spdlog//:spdlog", diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt index cf9b1438d..af3122fd7 100644 --- a/src/main/cpp/log/CMakeLists.txt +++ b/src/main/cpp/log/CMakeLists.txt @@ -2,13 +2,18 @@ add_library(logger_obj OBJECT) target_sources(logger_obj PRIVATE Logger.cpp) target_link_libraries(logger_obj PUBLIC - admin_library - ghc_filesystem) + rocketmq_interface + ghc_filesystem + spdlog::spdlog_header_only) add_library(logger_library STATIC $) +target_include_directories(logger_library + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(logger_library PUBLIC - admin_library - ghc_filesystem) \ No newline at end of file + rocketmq_interface + ghc_filesystem + spdlog::spdlog_header_only) \ No newline at end of file diff --git a/src/main/cpp/log/Logger.cpp b/src/main/cpp/log/Logger.cpp index 186958fe4..cdd317d0e 100644 --- a/src/main/cpp/log/Logger.cpp +++ b/src/main/cpp/log/Logger.cpp @@ -20,7 +20,7 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN Logger::Logger() : level_(Level::Info), file_size_(1048576 * 256), file_count_(16) {} @@ -96,4 +96,5 @@ Logger& getLogger() { const char* Logger::USER_HOME_ENV = "HOME"; const char* Logger::DEFAULT_PATTERN = "[%Y/%m/%d-%H:%M:%S.%e %z] [%n] [%^---%L---%$] [thread %t] %v %@"; -} // namespace rocketmq + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h similarity index 100% rename from src/main/cpp/base/include/LoggerImpl.h rename to src/main/cpp/log/include/LoggerImpl.h diff --git a/src/main/cpp/rocketmq/BaseImpl.cpp b/src/main/cpp/rocketmq/BaseImpl.cpp index 61c7a1fbe..d97ef9983 100644 --- a/src/main/cpp/rocketmq/BaseImpl.cpp +++ b/src/main/cpp/rocketmq/BaseImpl.cpp @@ -1,18 +1,19 @@ #include "BaseImpl.h" #include "ClientManager.h" +#include "InvocationContext.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" #include "Signature.h" #include "absl/strings/str_join.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/MQMessageExt.h" +#include +#include +#include ROCKETMQ_NAMESPACE_BEGIN -BaseImpl::BaseImpl(std::string group_name) - : ClientConfig(std::move(group_name)), state_(State::CREATED), top_addressing_(absl::make_unique()) { - topic_route_info_updater_ = std::bind(&BaseImpl::updateRouteInfo, this); - topic_route_info_updater_function_ = std::make_shared(&topic_route_info_updater_); - - name_server_list_updater_ = std::bind(&BaseImpl::renewNameServerList, this); - name_server_list_updater_function_ = std::make_shared(&name_server_list_updater_); -} +BaseImpl::BaseImpl(std::string group_name) : ClientConfig(std::move(group_name)), state_(State::CREATED) {} void BaseImpl::start() { State expected = CREATED; @@ -23,7 +24,6 @@ void BaseImpl::start() { client_instance_ = ClientManager::getInstance().getClientInstance(*this); client_instance_->start(); - top_addressing_->setUnitName(unit_name_); bool update_name_server_list = false; { absl::MutexLock lock(&name_server_list_mtx_); @@ -32,38 +32,60 @@ void BaseImpl::start() { } } + std::weak_ptr ptr(self()); + if (update_name_server_list) { // Acquire name server list immediately renewNameServerList(); // Schedule to renew name server list periodically SPDLOG_INFO("Name server list was empty. Schedule a task to fetch and renew periodically"); - bool scheduled = client_instance_->getScheduler().schedule(name_server_list_updater_function_, - std::chrono::milliseconds(0), std::chrono::seconds(30)); - if (scheduled) { - SPDLOG_INFO("Name server list updater scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule name server list updater"); + auto name_server_update_functor = [ptr]() { + std::shared_ptr base = ptr.lock(); + if (base) { + base->renewNameServerList(); + } + }; + name_server_update_handle_ = + client_instance_->getScheduler().schedule(name_server_update_functor, UPDATE_NAME_SERVER_LIST_TASK_NAME, + std::chrono::milliseconds(0), std::chrono::seconds(30)); + } + + auto route_update_functor = [ptr]() { + std::shared_ptr base = ptr.lock(); + if (base) { + base->updateRouteInfo(); } + }; + + route_update_handle_ = client_instance_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, + std::chrono::seconds(10), std::chrono::seconds(30)); + state_.store(State::STARTED); +} + +void BaseImpl::shutdown() { + state_.store(State::STOPPING, std::memory_order_relaxed); + if (name_server_update_handle_) { + client_instance_->getScheduler().cancel(name_server_update_handle_); } - bool scheduled = client_instance_->getScheduler().schedule(topic_route_info_updater_function_, - std::chrono::seconds(10), std::chrono::seconds(30)); - if (scheduled) { - SPDLOG_INFO("Topic route info updater scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule topic route info updater"); + if (route_update_handle_) { + client_instance_->getScheduler().cancel(route_update_handle_); } - state_.store(State::STARTED); + + client_instance_.reset(); } -void BaseImpl::activeHosts(absl::flat_hash_set& hosts) { +const char* BaseImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; +const char* BaseImpl::UPDATE_NAME_SERVER_LIST_TASK_NAME = "name_server_list_updater"; + +void BaseImpl::endpointsInUse(absl::flat_hash_set& endpoints) { absl::MutexLock lk(&topic_route_table_mtx_); for (const auto& item : topic_route_table_) { for (const auto& partition : item.second->partitions()) { std::string endpoint = partition.asMessageQueue().serviceAddress(); - if (!hosts.contains(endpoint)) { - hosts.emplace(std::move(endpoint)); + if (!endpoints.contains(endpoint)) { + endpoints.emplace(std::move(endpoint)); } } } @@ -145,29 +167,27 @@ void BaseImpl::renewNameServerList() { std::vector list; SPDLOG_DEBUG("Begin to renew name server list"); - if (!top_addressing_->fetchNameServerAddresses(list)) { - SPDLOG_WARN("Failed to fetch name server list"); - } else { - if (list.empty()) { + auto callback = [this](int code, const std::vector& name_server_list) { + if (GHttpClient::STATUS_OK != code) { + SPDLOG_WARN("Failed to fetch name server list"); + return; + } + + if (name_server_list.empty()) { SPDLOG_WARN("Yuck, got an empty name server list"); - } else { - debugNameServerChanges(list); - { - absl::MutexLock lock(&name_server_list_mtx_); - if (name_server_list_ != list) { - name_server_list_.clear(); - name_server_list_.insert(name_server_list_.begin(), list.begin(), list.end()); - } - { - absl::MutexLock promise_lock(&name_server_promise_list_mtx_); - for (auto& promise : name_server_promise_list_) { - promise.set_value(name_server_list_); - } - name_server_promise_list_.clear(); - } + return; + } + + debugNameServerChanges(name_server_list); + { + absl::MutexLock lock(&name_server_list_mtx_); + if (name_server_list_ != name_server_list) { + name_server_list_.clear(); + name_server_list_.insert(name_server_list_.begin(), name_server_list.begin(), name_server_list.end()); } } - } + }; + client_instance_->topAddressing().fetchNameServerAddresses(callback); } bool BaseImpl::selectNameServer(std::string& selected, bool change) { @@ -247,7 +267,7 @@ void BaseImpl::updateRouteInfo() { void BaseImpl::heartbeat() { absl::flat_hash_set hosts; - activeHosts(hosts); + endpointsInUse(hosts); if (hosts.empty()) { SPDLOG_WARN("No hosts to send heartbeat to at present"); return; @@ -317,4 +337,173 @@ void BaseImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPt } } +void BaseImpl::multiplexing(const std::string& target, const MultiplexingRequest& request) { + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_instance_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + std::bind(&BaseImpl::onMultiplexingResponse, this, std::placeholders::_1)); +} + +void BaseImpl::onMultiplexingResponse(const InvocationContext* ctx) { + if (!ctx->status.ok()) { + std::string remote_address = ctx->remote_address; + auto multiplexingLater = [this, remote_address]() { + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(remote_address, request); + }; + static std::string task_name = "Initiate multiplex request later"; + client_instance_->getScheduler().schedule(multiplexingLater, task_name, std::chrono::seconds(3), + std::chrono::seconds(0)); + return; + } + + switch (ctx->response.type_case()) { + case MultiplexingResponse::TypeCase::kPrintThreadStackRequest: { + MultiplexingRequest request; + request.mutable_print_thread_stack_response()->mutable_common()->mutable_status()->set_code( + google::rpc::Code::UNIMPLEMENTED); + request.mutable_print_thread_stack_response()->set_stack_trace( + "Print thread stack trace is not supported by C++ SDK"); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_instance_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + std::bind(&BaseImpl::onMultiplexingResponse, this, std::placeholders::_1)); + break; + } + + case MultiplexingResponse::TypeCase::kVerifyMessageConsumptionRequest: { + auto data = ctx->response.verify_message_consumption_request().message(); + MQMessageExt message; + MultiplexingRequest request; + if (!client_instance_->wrapMessage(data, message)) { + SPDLOG_WARN("Message to verify consumption is corrupted"); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( + google::rpc::Code::INVALID_ARGUMENT); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message( + "Message to verify is corrupted"); + multiplexing(ctx->remote_address, request); + return; + } + std::string&& result = verifyMessageConsumption(message); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( + google::rpc::Code::OK); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message(result); + multiplexing(ctx->remote_address, request); + break; + } + + case MultiplexingResponse::TypeCase::kResolveOrphanedTransactionRequest: { + auto orphan = ctx->response.resolve_orphaned_transaction_request().orphaned_transactional_message(); + MQMessageExt message; + if (client_instance_->wrapMessage(orphan, message)) { + MessageAccessor::setTargetEndpoint(message, ctx->remote_address); + const std::string& transaction_id = ctx->response.resolve_orphaned_transaction_request().transaction_id(); + resolveOrphanedTransactionalMessage(transaction_id, message); + } else { + SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " + "verification failure."); + } + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; + } + + case MultiplexingResponse::TypeCase::kPollingResponse: { + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; + } + + default: { + SPDLOG_WARN("Unsupported multiplex type"); + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; + } + } +} + +void BaseImpl::healthCheck() { + std::vector endpoints; + { + absl::MutexLock lk(&isolated_endpoints_mtx_); + for (const auto& item : isolated_endpoints_) { + endpoints.push_back(item); + } + } + + std::weak_ptr base(self()); + auto callback = [base](const std::string& endpoint, + const InvocationContext* invocation_context) { + std::shared_ptr ptr = base.lock(); + if (ptr) { + ptr->onHealthCheckResponse(endpoint, invocation_context); + } else { + SPDLOG_INFO("BaseImpl has been destructed"); + } + }; + + for (const auto& endpoint : endpoints) { + HealthCheckRequest request; + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_instance_->healthCheck(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } +} + +void BaseImpl::onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) { + if (!ctx) { + SPDLOG_WARN("ClientInstance does not have RPC client for {}. It might have been offline and thus cleaned", + endpoint); + { + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.erase(endpoint); + } + return; + } + + assert(endpoint == ctx->remote_address); + + if (ctx->status.ok()) { + if (google::rpc::Code::OK == ctx->response.common().status().code()) { + SPDLOG_INFO("Health check to server[host={}] passed. Move it back to active node pool", endpoint); + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.erase(endpoint); + } else { + SPDLOG_INFO("Health check to server[host={}] failed due to application layer reason: {}", + ctx->response.common().DebugString()); + } + } else { + SPDLOG_INFO("Health check to server[host={}] failed due to transport layer reason: {}", endpoint, + ctx->status.error_message()); + } +} + +void BaseImpl::fillGenericPollingRequest(MultiplexingRequest& request) { + auto&& resource_bundle = resourceBundle(); + auto protocol_bundle = request.mutable_polling_request()->mutable_client_resource_bundle(); + protocol_bundle->set_client_id(resource_bundle.client_id); + for (auto& item : resource_bundle.topics) { + auto topic = new rmq::Resource; + topic->set_arn(resource_bundle.arn); + topic->set_name(item); + protocol_bundle->mutable_topics()->AddAllocated(topic); + } + + switch (resource_bundle.group_type) { + case GroupType::PUBLISHER: + protocol_bundle->mutable_producer_group()->set_arn(resource_bundle.arn); + protocol_bundle->mutable_producer_group()->set_name(resource_bundle.group); + break; + case GroupType::SUBSCRIBER: + protocol_bundle->mutable_consumer_group()->set_arn(resource_bundle.arn); + protocol_bundle->mutable_consumer_group()->set_name(resource_bundle.group); + break; + } +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt index 683cd6d46..87645df61 100644 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -14,7 +14,6 @@ target_sources(rocketmq_obj DefaultMQPushConsumerImpl.cpp DefaultMQPullConsumer.cpp DefaultMQPullConsumerImpl.cpp - MessageClientIDSetter.cpp ProcessQueue.cpp TransactionImpl.cpp) target_include_directories(rocketmq_obj diff --git a/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp index 49e44e56a..3c7e22e49 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp @@ -2,9 +2,9 @@ #include "DefaultMQPushConsumerImpl.h" #include "LoggerImpl.h" #include "Protocol.h" -#include "ThreadPool.h" #include "TracingUtility.h" #include "absl/strings/str_join.h" +#include "absl/memory/memory.h" #include ROCKETMQ_NAMESPACE_BEGIN @@ -12,10 +12,8 @@ ROCKETMQ_NAMESPACE_BEGIN ConsumeMessageConcurrentlyService::ConsumeMessageConcurrentlyService(std::weak_ptr consumer, int thread_count, MQMessageListener* message_listener_ptr) - : thread_count_(thread_count), message_listener_ptr_(message_listener_ptr), consumer_weak_ptr_(std::move(consumer)), - state_(State::CREATED) { - pool_ = std::make_shared(thread_count_); -} + : thread_count_(thread_count), message_listener_ptr_(message_listener_ptr), state_(State::CREATED), + pool_(absl::make_unique(thread_count_)), consumer_weak_ptr_(std::move(consumer)) {} void ConsumeMessageConcurrentlyService::start() { // Loop each process queue @@ -110,10 +108,10 @@ void ConsumeMessageConcurrentlyService::submitConsumeTask(const ProcessQueueWeak } // submit batch message - std::function&)> consumer_task = - std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, std::placeholders::_1, std::placeholders::_2); + std::function consume_task = + std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, process_queue_ptr, messages); SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); - pool_->enqueue(consumer_task, process_queue, messages); + pool_->Add(consume_task); } } diff --git a/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp index 2b4b3e13f..5cda5ce97 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp @@ -1,14 +1,13 @@ #include "ConsumeMessageService.h" #include "DefaultMQPushConsumerImpl.h" -#include "ThreadPool.h" ROCKETMQ_NAMESPACE_BEGIN ConsumeMessageOrderlyService ::ConsumeMessageOrderlyService( const std::weak_ptr&& consumer_impl_ptr, int thread_count, MQMessageListener* message_listener_ptr) - : consumer_weak_ptr_(consumer_impl_ptr), message_listener_ptr_(message_listener_ptr), thread_count_(thread_count) { - pool_ = std::make_shared(thread_count_); + : consumer_weak_ptr_(consumer_impl_ptr), message_listener_ptr_(message_listener_ptr), thread_count_(thread_count), + pool_(absl::make_unique(thread_count_)) { // Suppress field not used warning for now (void)message_listener_ptr_; } diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index 760734fd5..4f45cb7ee 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -73,6 +73,10 @@ void DefaultMQProducer::sendOneway(const MQMessage& message, MessageQueueSelecto impl_->sendOneway(message, selector, arg); } +void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { + impl_->setLocalTransactionStateChecker(std::move(checker)); +} + void DefaultMQProducer::setRetryTimes(int retry_times) { impl_->maxAttemptTimes(retry_times); } int DefaultMQProducer::getRetryTimes() const { return impl_->maxAttemptTimes(); } diff --git a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp index 677ead3a8..0483b23ce 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp @@ -1,13 +1,14 @@ #include "DefaultMQProducerImpl.h" -#include "MessageClientIDSetter.h" -#include "MessageSystemFlag.h" +#include "MessageAccessor.h" #include "Metadata.h" #include "MixAll.h" +#include "UtilAll.h" #include "Protocol.h" -#include "RequestIdGenerator.h" #include "SendCallbacks.h" #include "SendMessageContext.h" #include "Signature.h" +#include "TransactionImpl.h" +#include "UniqueIdGenerator.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" @@ -30,7 +31,14 @@ void DefaultMQProducerImpl::start() { client_instance_->addClientObserver(shared_from_this()); } -void DefaultMQProducerImpl::shutdown() { client_instance_.reset(); } +void DefaultMQProducerImpl::shutdown() { + BaseImpl::shutdown(); + + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + SPDLOG_INFO("DefaultMQProducerImpl stopped"); + } +} bool DefaultMQProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } @@ -55,7 +63,7 @@ std::string DefaultMQProducerImpl::wrapSendMessageRequest(const MQMessage& messa system_attribute->mutable_born_timestamp()->set_seconds(seconds); system_attribute->mutable_born_timestamp()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); - system_attribute->set_born_host(UtilAll::getHostIPv4()); + system_attribute->set_born_host(UtilAll::hostname()); system_attribute->mutable_producer_group()->set_arn(arn_); system_attribute->mutable_producer_group()->set_name(group_name_); @@ -86,7 +94,7 @@ std::string DefaultMQProducerImpl::wrapSendMessageRequest(const MQMessage& messa } // Create unique message-id - std::string message_id = MessageClientIDSetter::createUniqId(); + std::string message_id = UniqueIdGenerator::instance().next(); system_attribute->set_message_id(message_id); system_attribute->set_partition_id(message_queue.getQueueId()); @@ -214,9 +222,9 @@ void DefaultMQProducerImpl::sendOneway(const MQMessage& message) { return; } MQMessageQueue message_queue; - absl::flat_hash_set clientsHosts; - client_instance_->getAllInactiveRpcClientHost(clientsHosts); - ptr->selectOneActiveMessageQueue(clientsHosts, message_queue); + absl::flat_hash_set isolated; + isolatedEndpoints(isolated); + ptr->selectOneActiveMessageQueue(isolated, message_queue); std::vector list{message_queue}; send0(message, onewaySendCallback(), list, 1); }; @@ -245,6 +253,10 @@ void DefaultMQProducerImpl::sendOneway(const MQMessage& message, MessageQueueSel asyncPublishInfo(message.getTopic(), callback); } +void DefaultMQProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { + transaction_state_checker_ = std::move(checker); +} + void DefaultMQProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times) { assert(callback); @@ -287,18 +299,18 @@ void DefaultMQProducerImpl::send0(const MQMessage& message, SendCallback* callba } bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, - const std::string& transaction_id, TransactionResolution resolution) { + const std::string& transaction_id, TransactionState resolution) { EndTransactionRequest request; request.set_message_id(message_id); request.set_transaction_id(transaction_id); std::string action; switch (resolution) { - case TransactionResolution::COMMIT: + case TransactionState::COMMIT: request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); action = "commit"; break; - case TransactionResolution::ROLLBACK: + case TransactionState::ROLLBACK: request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); action = "rollback"; break; @@ -334,8 +346,19 @@ bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std return success; } -bool DefaultMQProducerImpl::isClientActive(const std::string& target) { - return client_instance_->isRpcClientActive(target); +void DefaultMQProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + endpoints.insert(isolated_endpoints_.begin(), isolated_endpoints_.end()); +} + +bool DefaultMQProducerImpl::isEndpointIsolated(const std::string& target) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + return isolated_endpoints_.contains(target); +} + +void DefaultMQProducerImpl::isolateEndpoint(const std::string& target) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.insert(target); } std::unique_ptr DefaultMQProducerImpl::prepare(const MQMessage& message) { @@ -352,12 +375,12 @@ std::unique_ptr DefaultMQProducerImpl::prepare(const MQMessage& bool DefaultMQProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionResolution::COMMIT); + return endTransaction0(target, message_id, transaction_id, TransactionState::COMMIT); } bool DefaultMQProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionResolution::ROLLBACK); + return endTransaction0(target, message_id, transaction_id, TransactionState::ROLLBACK); } void DefaultMQProducerImpl::asyncPublishInfo(const std::string& topic, @@ -442,7 +465,7 @@ void DefaultMQProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPt std::vector& message_queues, int number) { assert(publish_info); absl::flat_hash_set isolated; - client_instance_->getAllInactiveRpcClientHost(isolated); + isolatedEndpoints(isolated); publish_info->takeMessageQueues(isolated, message_queues, number); } @@ -478,4 +501,15 @@ void DefaultMQProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { request.mutable_heartbeats()->Add(std::move(entry)); } +void DefaultMQProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, + const MQMessageExt& message) { + if (transaction_state_checker_) { + TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); + const std::string& target_host = MessageAccessor::targetEndpoint(message); + endTransaction0(target_host, message.getMsgId(), transaction_id, state); + } else { + SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); + } +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp index e5339753e..3609d0315 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp @@ -13,7 +13,16 @@ void DefaultMQPullConsumerImpl::start() { client_instance_->addClientObserver(shared_from_this()); } -void DefaultMQPullConsumerImpl::shutdown() {} +void DefaultMQPullConsumerImpl::shutdown() { + // Shutdown services started by current tier + + // Shutdown services that are started by the parent + BaseImpl::shutdown(); + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + SPDLOG_INFO("DefaultMQPullConsumerImpl stopped"); + } +} std::future> DefaultMQPullConsumerImpl::queuesFor(const std::string& topic) { auto promise = std::make_shared>>(); diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp index e182091b5..6a46969dd 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp @@ -12,7 +12,6 @@ ROCKETMQ_NAMESPACE_BEGIN AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) : process_queue_(std::move(process_queue)) { receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); - receive_message_later_functional_ = std::make_shared(&receive_message_later_); } void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { @@ -22,11 +21,6 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { return; } - int64_t term_id = 0; - if (!result.termId(term_id)) { - SPDLOG_WARN("Failed to decode Term-Id from Request-Id: {}", result.requestId()); - } - std::shared_ptr impl = process_queue_shared_ptr->getCallbackOwner().lock(); if (impl->isStopped()) { return; @@ -42,55 +36,40 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::PULL) { process_queue_shared_ptr->nextOffset(result.next_offset_); } - - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - checkThrottleThenReceive(); - } + checkThrottleThenReceive(); break; case ReceiveMessageStatus::DATA_CORRUPTED: if (process_queue_shared_ptr->consumeType() == POP) { process_queue_shared_ptr->cacheMessages(result.messages_); impl->getConsumeMessageService()->dispatch(); } - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - checkThrottleThenReceive(); - } + checkThrottleThenReceive(); break; case ReceiveMessageStatus::OUT_OF_RANGE: assert(ConsumeMessageType::PULL == process_queue_shared_ptr->consumeType()); process_queue_shared_ptr->nextOffset(result.next_offset_); - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - checkThrottleThenReceive(); - } + checkThrottleThenReceive(); break; case ReceiveMessageStatus::DEADLINE_EXCEEDED: SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=DEADLINE_EXCEEDED, queue={}", result.sourceHost(), process_queue_shared_ptr->simpleName()); - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - checkThrottleThenReceive(); - } + checkThrottleThenReceive(); break; case ReceiveMessageStatus::INTERNAL: SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=UNKNOWN, queue={}", result.sourceHost(), process_queue_shared_ptr->simpleName()); - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - receiveMessageLater(); - } + receiveMessageLater(); break; case ReceiveMessageStatus::RESOURCE_EXHAUSTED: SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=RESOURCE_EXHAUSTED, queue={}", result.sourceHost(), process_queue_shared_ptr->simpleName()); - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - receiveMessageLater(); - } + receiveMessageLater(); break; case ReceiveMessageStatus::NOT_FOUND: SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=NOT_FOUND, queue={}", result.sourceHost(), process_queue_shared_ptr->simpleName()); - if (process_queue_shared_ptr->leaseNextTerm(term_id, result.sourceHost())) { - receiveMessageLater(); - } + receiveMessageLater(); break; default: SPDLOG_WARN("Unknown receive message status: {} from broker[host={}], queue={}", result.status(), @@ -100,6 +79,8 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { } } +const char* AsyncReceiveMessageCallback::RECEIVE_LATER_TASK_NAME = "receive-later-task"; + void AsyncReceiveMessageCallback::checkThrottleThenReceive() { auto process_queue = process_queue_.lock(); if (!process_queue) { @@ -134,13 +115,17 @@ void AsyncReceiveMessageCallback::receiveMessageLater() { } auto client_instance = process_queue->getClientInstance(); - bool scheduled = client_instance->getScheduler().schedule(receive_message_later_functional_, std::chrono::seconds(1), - std::chrono::seconds(0)); - if (scheduled) { - SPDLOG_INFO("Receive message later scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule receive message later"); - } + std::weak_ptr receive_callback_weak_ptr(shared_from_this()); + + auto task = [receive_callback_weak_ptr]() { + auto async_receive_ptr = receive_callback_weak_ptr.lock(); + if (async_receive_ptr) { + async_receive_ptr->checkThrottleThenReceive(); + } + }; + + client_instance->getScheduler().schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), + std::chrono::seconds(0)); } void AsyncReceiveMessageCallback::receiveMessageImmediately() { @@ -168,10 +153,7 @@ const int32_t DefaultMQPushConsumerImpl::DEFAULT_CONSUME_THREAD_POOL_SIZE = 20; DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(std::string group_name) : BaseImpl(std::move(group_name)), consume_thread_pool_size_(DEFAULT_CONSUME_THREAD_POOL_SIZE), message_listener_ptr_(nullptr), consume_batch_size_(DEFAULT_CONSUME_MESSAGE_BATCH_SIZE), - max_cached_message_number_per_queue_(DEFAULT_CACHED_MESSAGE_COUNT) { - scan_assignment_executor_ = std::bind(&DefaultMQPushConsumerImpl::scanAssignments, this); - scan_assignment_functional_ = std::make_shared(&scan_assignment_executor_); -} + max_cached_message_number_per_queue_(DEFAULT_CACHED_MESSAGE_COUNT) {} DefaultMQPushConsumerImpl::~DefaultMQPushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } @@ -209,27 +191,28 @@ void DefaultMQPushConsumerImpl::start() { } } } + std::weak_ptr consumer_weak_ptr(shared_from_this()); + auto scan_assignment_functor = [consumer_weak_ptr]() { + std::shared_ptr consumer = consumer_weak_ptr.lock(); + if (consumer) { + consumer->scanAssignments(); + } + }; + + scan_assignment_handle_ = client_instance_->getScheduler().schedule( + scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); - bool scheduled; - scheduled = client_instance_->getScheduler().schedule(scan_assignment_functional_, std::chrono::seconds(0), - std::chrono::seconds(5)); - if (scheduled) { - SPDLOG_INFO("pop message executor scheduled"); - } else { - SPDLOG_ERROR("Failed to schedule pop message executor"); - } state_.store(State::STARTED, std::memory_order_relaxed); SPDLOG_INFO("DefaultMQPushConsumer started, groupName={}", group_name_); } +const char* DefaultMQPushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; + void DefaultMQPushConsumerImpl::shutdown() { - state_.store(State::STOPPING, std::memory_order_relaxed); - bool cancelled; - cancelled = client_instance_->getScheduler().cancel(&scan_assignment_executor_); - if (cancelled) { - SPDLOG_INFO("pop message executor cancelled OK"); - } else { - SPDLOG_ERROR("Failed to cancel pop message executor"); + + if (scan_assignment_handle_) { + client_instance_->getScheduler().cancel(scan_assignment_handle_); + SPDLOG_DEBUG("Scan assignment periodic task cancelled"); } { @@ -241,8 +224,12 @@ void DefaultMQPushConsumerImpl::shutdown() { consume_message_service_->shutdown(); } - // Destruct client instance as soon as possible. - client_instance_.reset(); + // Shutdown services started by parent + BaseImpl::shutdown(); + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + SPDLOG_INFO("DefaultMQPushConsumerImpl stopped"); + } } bool DefaultMQPushConsumerImpl::isStopped() const { @@ -699,4 +686,16 @@ void DefaultMQPushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) } } +ClientResourceBundle DefaultMQPushConsumerImpl::resourceBundle() { + ClientResourceBundle resource_bundle = BaseImpl::resourceBundle(); + { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (auto& item : topic_filter_expression_table_) { + resource_bundle.topics.emplace_back(item.first); + } + } + + return resource_bundle; +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/MessageClientIDSetter.cpp b/src/main/cpp/rocketmq/MessageClientIDSetter.cpp deleted file mode 100644 index 1455e8ff9..000000000 --- a/src/main/cpp/rocketmq/MessageClientIDSetter.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "MessageClientIDSetter.h" -#include "spdlog/spdlog.h" - -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -absl::Mutex MessageClientIDSetter::mtx_; -char MessageClientIDSetter::data[16] = {0}; -int16_t MessageClientIDSetter::counter = 0; -int16_t MessageClientIDSetter::basePos = 0; -int32_t MessageClientIDSetter::hash_code = 0; -std::chrono::system_clock::time_point MessageClientIDSetter::m_startTime = std::chrono::system_clock::now(); -std::chrono::system_clock::time_point MessageClientIDSetter::m_nextStartTime = std::chrono::system_clock::now(); - -std::string MessageClientIDSetter::createUniqId() { - absl::MutexLock lock(&mtx_); - char tmp_buf[4]; - static bool initialized = false; - if (!initialized) { // only first execute - std::string&& ip = UtilAll::getHostIPv4(); - struct in_addr addr = {}; - if (inet_aton(ip.c_str(), &addr)) { - memcpy(data, &(addr.s_addr), sizeof(addr.s_addr)); - } else { - SPDLOG_ERROR("inet_aton failed. IP: {}", ip); - } - - // TO-DO: WIN32 - UtilAll::int16tobyte(tmp_buf, static_cast(getpid())); - memcpy(data + 4, tmp_buf, 2); - - UtilAll::int32tobyte(tmp_buf, static_cast(hash_code++)); - memcpy(data + 6, tmp_buf, 4); - - setStartTime(std::chrono::system_clock::now()); - initialized = true; - } - - UtilAll::int32tobyte(tmp_buf, static_cast(hash_code++)); - memcpy(data + 6, tmp_buf, 4); - std::chrono::system_clock::time_point current = std::chrono::system_clock::now(); - if (current.time_since_epoch() >= m_nextStartTime.time_since_epoch()) { - setStartTime(current); - } - int64_t time_duration = - std::chrono::duration_cast(std::chrono::system_clock::now() - m_startTime).count(); - UtilAll::int32tobyte(tmp_buf, static_cast(time_duration)); - memcpy(data + 10, tmp_buf, 4); - - UtilAll::int16tobyte(tmp_buf, counter++); - memcpy(data + 14, tmp_buf, 2); - - return bytes2string(data, 16); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueue.cpp b/src/main/cpp/rocketmq/ProcessQueue.cpp index 7fe43b490..4ccf64fdd 100644 --- a/src/main/cpp/rocketmq/ProcessQueue.cpp +++ b/src/main/cpp/rocketmq/ProcessQueue.cpp @@ -4,6 +4,7 @@ #include "Metadata.h" #include "Protocol.h" #include "Signature.h" +#include #include #include #include @@ -19,7 +20,7 @@ ProcessQueue::ProcessQueue(MQMessageQueue message_queue, FilterExpression filter : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), consume_type_(consume_type), max_message_number_(MixAll::MAX_MESSAGE_NUMBER_PER_BATCH), invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), message_cached_number_(0), - max_cache_size_(max_cache_size), simple_name_(message_queue_.simpleName()), term_id_(1L), + max_cache_size_(max_cache_size), simple_name_(message_queue_.simpleName()), call_back_owner_(std::move(call_back_owner)), client_instance_(std::move(client_instance)) { SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); } @@ -113,44 +114,8 @@ bool ProcessQueue::consume(int batch_size, std::vector& messages) return !cached_messages_.empty(); } -int64_t ProcessQueue::termId() const { return term_id_.load(std::memory_order_relaxed); } - -bool ProcessQueue::leaseNextTerm(int64_t current_term_id, absl::string_view source_host) { - if (current_term_id) { - if (current_term_id < termId()) { - return false; - } - int64_t term_id = current_term_id; - bool acquired = term_id_.compare_exchange_strong(term_id, current_term_id + 1, std::memory_order_relaxed); - if (acquired) { - SPDLOG_DEBUG("{}: Lease acquired. New termId={}", message_queue_.simpleName(), termId()); - } - return acquired; - } - SPDLOG_WARN("Version of server[host={}] is too old.", source_host.data()); - return true; -} - -std::string ProcessQueue::requestId() const { - int32_t numeric[4]; - std::string request_id; - const std::string& ip = UtilAll::getHostIPv4(); - struct in_addr addr = {}; - int ret = inet_aton(ip.data(), &addr); - - // ip is supposed to be legal and parsable. - assert(ret == 1); - numeric[0] = htonl(addr.s_addr); - numeric[1] = getpid(); - int64_t term_id = term_id_.load(std::memory_order_relaxed); - *(reinterpret_cast(numeric) + 1) = term_id; - return MixAll::hex(numeric, sizeof(numeric)); -} - void ProcessQueue::wrapPopMessageRequest(absl::flat_hash_map& metadata, rmq::ReceiveMessageRequest& request) { - metadata.insert({Metadata::REQUEST_ID_KEY, requestId()}); - std::shared_ptr consumer = call_back_owner_.lock(); assert(consumer); request.set_client_id(consumer->clientId()); @@ -194,7 +159,6 @@ void ProcessQueue::wrapPopMessageRequest(absl::flat_hash_map& metadata, rmq::PullMessageRequest& request) { - metadata.insert({Metadata::REQUEST_ID_KEY, requestId()}); std::shared_ptr consumer = call_back_owner_.lock(); assert(consumer); request.set_client_id(consumer->clientId()); @@ -205,9 +169,9 @@ void ProcessQueue::wrapPullMessageRequest(absl::flat_hash_mapmutable_topic()->set_arn(consumer->arn()); request.set_offset(next_offset_); request.set_batch_size(consumer->receiveBatchSize()); - - auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); - if (consumer->getTopicFilterExpressionTable().end() != search) { + auto filter_expression_table = consumer->getTopicFilterExpressionTable(); + auto search = filter_expression_table.find(message_queue_.getTopic()); + if (filter_expression_table.end() != search) { FilterExpression expression = search->second; switch (expression.type_) { case TAG: diff --git a/src/main/cpp/rocketmq/include/BaseImpl.h b/src/main/cpp/rocketmq/include/BaseImpl.h index 0a2cdafd3..43bee54c2 100644 --- a/src/main/cpp/rocketmq/include/BaseImpl.h +++ b/src/main/cpp/rocketmq/include/BaseImpl.h @@ -1,4 +1,7 @@ #include "ClientInstance.h" +#include "ClientResourceBundle.h" +#include "InvocationContext.h" +#include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN @@ -11,20 +14,32 @@ class BaseImpl : public ClientConfig, public ClientCallback { virtual void start(); + virtual void shutdown(); + void getRouteFor(const std::string& topic, const std::function& cb) LOCKS_EXCLUDED(inflight_route_requests_mtx_, topic_route_table_mtx_); - void activeHosts(absl::flat_hash_set& hosts) override LOCKS_EXCLUDED(topic_route_table_mtx_); + /** + * Gather collection of endpoints that are reachable from latest topic route table. + * + * @param endpoints + */ + void endpointsInUse(absl::flat_hash_set& endpoints) override LOCKS_EXCLUDED(topic_route_table_mtx_); void setNameServerList(std::vector name_server_list) { absl::MutexLock lk(&name_server_list_mtx_); name_server_list_ = std::move(name_server_list); } - virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; - void heartbeat() override; + bool active() override { + State state = state_.load(std::memory_order_relaxed); + return State::STARTING == state || State::STARTED == state; + } + + void healthCheck() LOCKS_EXCLUDED(isolated_endpoints_mtx_) override; + protected: ClientInstancePtr client_instance_; std::atomic state_; @@ -35,22 +50,21 @@ class BaseImpl : public ClientConfig, public ClientCallback { absl::flat_hash_map>> inflight_route_requests_ GUARDED_BY(inflight_route_requests_mtx_); absl::Mutex inflight_route_requests_mtx_ ACQUIRED_BEFORE(topic_route_table_mtx_); // Protects inflight_route_requests_ - - std::function topic_route_info_updater_; - FunctionalSharePtr topic_route_info_updater_function_; + static const char* UPDATE_ROUTE_TASK_NAME; + std::uintptr_t route_update_handle_{0}; // Name server list management std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); absl::Mutex name_server_list_mtx_; // protects name_server_list_ - std::function name_server_list_updater_; - FunctionalSharePtr name_server_list_updater_function_; + static const char* UPDATE_NAME_SERVER_LIST_TASK_NAME; + std::uintptr_t name_server_update_handle_{0}; - std::string unit_name_; - TopAddressingPtr top_addressing_; + absl::flat_hash_map multiplexing_requests_; + absl::Mutex multiplexing_requests_mtx_; - std::vector>> name_server_promise_list_; - absl::Mutex name_server_promise_list_mtx_ ACQUIRED_AFTER(name_server_list_mtx_); + absl::flat_hash_set isolated_endpoints_ GUARDED_BY(isolated_endpoints_mtx_); + absl::Mutex isolated_endpoints_mtx_; void debugNameServerChanges(const std::vector& list) LOCKS_EXCLUDED(name_server_list_mtx_); @@ -60,6 +74,27 @@ class BaseImpl : public ClientConfig, public ClientCallback { void updateRouteInfo() LOCKS_EXCLUDED(topic_route_table_mtx_); + /** + * Sub-class is supposed to inherit from std::enable_shared_from_this. + */ + virtual std::shared_ptr self() = 0; + + virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; + + virtual std::string verifyMessageConsumption(const MQMessageExt& message) { return "Unsupported"; } + + virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) {} + + /** + * Concrete publisher/subscriber client is expected to fill other type-specific resources. + */ + virtual ClientResourceBundle resourceBundle() { + ClientResourceBundle resource_bundle; + resource_bundle.client_id = clientId(); + resource_bundle.arn = arn_; + return resource_bundle; + } + private: /** * This is a low-level API that fetches route data from name server through gRPC unary request/response. Once @@ -86,6 +121,15 @@ class BaseImpl : public ClientConfig, public ClientCallback { */ void updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(topic_route_table_mtx_); + + void multiplexing(const std::string& target, const MultiplexingRequest& request); + + void onMultiplexingResponse(const InvocationContext* ctx); + + void onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) + LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + void fillGenericPollingRequest(MultiplexingRequest& request); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h new file mode 100644 index 000000000..f7bac1df9 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -0,0 +1,21 @@ +#include "rocketmq/RocketMQ.h" + +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class GroupType : int8_t { + PUBLISHER = 0, + SUBSCRIBER = 1, +}; + +struct ClientResourceBundle { + std::string client_id; + std::vector topics; + std::string arn; + std::string group; + GroupType group_type{GroupType::PUBLISHER}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index 846d5ffd1..71f5ff06f 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -6,9 +6,9 @@ #include "ProcessQueue.h" #include "RateLimiter.h" -#include "ThreadPool.h" #include "rocketmq/MQMessageListener.h" #include "rocketmq/State.h" +#include "src/cpp/server/dynamic_thread_pool.h" ROCKETMQ_NAMESPACE_BEGIN @@ -17,6 +17,7 @@ class DefaultMQPushConsumerImpl; class ConsumeMessageService { public: ConsumeMessageService() = default; + virtual ~ConsumeMessageService() = default; /** @@ -80,14 +81,13 @@ class ConsumeMessageConcurrentlyService : public ConsumeMessageService { int thread_count_; MQMessageListener* message_listener_ptr_; - std::shared_ptr pool_; + std::atomic state_; + std::unique_ptr pool_; std::weak_ptr consumer_weak_ptr_; absl::Mutex dispatch_mtx_; std::thread dispatch_thread_; absl::CondVar dispatch_cv_; - - std::atomic state_; }; class ConsumeMessageOrderlyService : public ConsumeMessageService { @@ -108,7 +108,7 @@ class ConsumeMessageOrderlyService : public ConsumeMessageService { std::weak_ptr consumer_weak_ptr_; MQMessageListener* message_listener_ptr_; int thread_count_; - std::shared_ptr pool_; + std::unique_ptr pool_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h index ecb879fac..6b1d4d463 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h @@ -12,6 +12,7 @@ #include "TopicPublishInfo.h" #include "TransactionImpl.h" #include "rocketmq/AsyncCallback.h" +#include "rocketmq/LocalTransactionStateChecker.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQSelector.h" #include "rocketmq/SendResult.h" @@ -19,11 +20,6 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class TransactionResolution : int8_t { - COMMIT = 0, - ROLLBACK = 1, -}; - class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_this { public: explicit DefaultMQProducerImpl(std::string group_name); @@ -34,7 +30,7 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void start() override; - void shutdown(); + void shutdown() override; SendResult send(const MQMessage& message); SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); @@ -50,6 +46,8 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); + std::unique_ptr prepare(const MQMessage& message); bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& target); @@ -57,11 +55,17 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& target); /** - * Check if the RPC client for the target host is active or note. - * @param target Address of target host. + * Check if the RPC client for the target host is isolated or not + * @param endpoint Address of target host. * @return true if client is active; false otherwise. */ - bool isClientActive(const std::string& target); + bool isEndpointIsolated(const std::string& endpoint) LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + /** + * Note: This function is purpose-made public such that the whole isolate/add-back mechanism can be properly tested. + * @param target Endpoint of the target host + */ + void isolateEndpoint(const std::string& target) LOCKS_EXCLUDED(isolated_endpoints_mtx_); int maxAttemptTimes() const { return max_attempt_times_; } @@ -77,6 +81,11 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void compressBodyThreshold(uint32_t threshold) { compress_body_threshold_ = threshold; } +protected: + std::shared_ptr self() override { return shared_from_this(); } + + void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; + private: absl::flat_hash_map topic_publish_info_table_ GUARDED_BY(topic_publish_info_mtx_); absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ @@ -85,6 +94,8 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th int32_t failed_times_{0}; // only for test uint32_t compress_body_threshold_; + LocalTransactionStateCheckerPtr transaction_state_checker_; + void asyncPublishInfo(const std::string& topic, const std::function& cb) LOCKS_EXCLUDED(topic_publish_info_mtx_); @@ -108,7 +119,9 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); bool endTransaction0(const std::string& target, const std::string& message_id, const std::string& transaction_id, - TransactionResolution resolution); + TransactionState resolution); + + void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h index 2b6bd536e..5295709c0 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h @@ -17,7 +17,7 @@ class DefaultMQPullConsumerImpl : public BaseImpl, public std::enable_shared_fro void start() override; - void shutdown(); + void shutdown() override; std::future> queuesFor(const std::string& topic); @@ -26,6 +26,11 @@ class DefaultMQPullConsumerImpl : public BaseImpl, public std::enable_shared_fro void pull(const PullMessageQuery& query, PullCallback* callback); void prepareHeartbeatData(HeartbeatRequest& request) override; + +protected: + std::shared_ptr self() override { + return shared_from_this(); + } }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h index bf33cddfd..e1c2a0b56 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h @@ -9,7 +9,6 @@ #include "ClientInstance.h" #include "ConsumeMessageService.h" #include "FilterExpression.h" -#include "Functional.h" #include "ProcessQueue.h" #include "Scheduler.h" #include "TopicAssignmentInfo.h" @@ -26,8 +25,7 @@ class ConsumeMessageService; class ConsumeMessageOrderlyService; class ConsumeMessageConcurrentlyService; -class DefaultMQPushConsumerImpl : public BaseImpl, - public std::enable_shared_from_this { +class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_from_this { public: explicit DefaultMQPushConsumerImpl(std::string group_name); @@ -37,7 +35,7 @@ class DefaultMQPushConsumerImpl : public BaseImpl, void start() override; - void shutdown(); + void shutdown() override; void subscribe(const std::string& topic, const std::string& expression, ExpressionType expression_type = ExpressionType::TAG) @@ -54,7 +52,7 @@ class DefaultMQPushConsumerImpl : public BaseImpl, void scanAssignments() LOCKS_EXCLUDED(process_queue_table_mtx_); - static bool selectBroker(const TopicRouteDataPtr &route, std::string& broker_host); + static bool selectBroker(const TopicRouteDataPtr& route, std::string& broker_host); void wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, const std::string& client_id, const std::string& strategy_name, @@ -130,6 +128,11 @@ class DefaultMQPushConsumerImpl : public BaseImpl, void offsetStore(std::unique_ptr offset_store) { offset_store_ = std::move(offset_store); } +protected: + std::shared_ptr self() override { return shared_from_this(); } + + ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; + private: absl::flat_hash_map topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); @@ -151,8 +154,8 @@ class DefaultMQPushConsumerImpl : public BaseImpl, int max_cached_message_number_per_queue_; - std::function scan_assignment_executor_; - FunctionalSharePtr scan_assignment_functional_; + std::uintptr_t scan_assignment_handle_{0}; + static const char* SCAN_ASSIGNMENT_TASK_NAME; absl::flat_hash_map process_queue_table_ GUARDED_BY(process_queue_table_mtx_); absl::Mutex process_queue_table_mtx_; @@ -184,7 +187,8 @@ class DefaultMQPushConsumerImpl : public BaseImpl, static const int32_t DEFAULT_CONSUME_THREAD_POOL_SIZE; }; -class AsyncReceiveMessageCallback : public ReceiveMessageCallback { +class AsyncReceiveMessageCallback : public ReceiveMessageCallback, + public std::enable_shared_from_this { public: explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); @@ -205,9 +209,10 @@ class AsyncReceiveMessageCallback : public ReceiveMessageCallback { ProcessQueueWeakPtr process_queue_; std::function receive_message_later_; - FunctionalSharePtr receive_message_later_functional_; void checkThrottleThenReceive(); + + static const char* RECEIVE_LATER_TASK_NAME; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/MessageClientIDSetter.h b/src/main/cpp/rocketmq/include/MessageClientIDSetter.h deleted file mode 100644 index d12380b9d..000000000 --- a/src/main/cpp/rocketmq/include/MessageClientIDSetter.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include "UtilAll.h" -#include "sstream" -#ifndef WIN32 -#include -#include -#endif -#include "LoggerImpl.h" -#include "UtilAll.h" -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" -#include -#include -#include -#include - -#ifdef WIN32 -#include -#endif - -ROCKETMQ_NAMESPACE_BEGIN - -class MessageClientIDSetter { -public: - MessageClientIDSetter() = default; - - static std::string createUniqId() LOCKS_EXCLUDED(mtx_); - - static void setStartTime(std::chrono::system_clock::time_point time_p) { - time_t now = std::chrono::system_clock::to_time_t(time_p); - struct tm* p = localtime(&now); - p->tm_sec = 0; - p->tm_min = 0; - p->tm_hour = 0; - p->tm_mday = 1; - m_startTime = std::chrono::system_clock::from_time_t(mktime(p)); - p->tm_mon = p->tm_mon + 1; // next month - // mktime auto adjust time - m_nextStartTime = std::chrono::system_clock::from_time_t(mktime(p)); - } - - static std::string bytes2string(const char* bytebuffer, int arr_len) { - const auto* byte_arr = reinterpret_cast(bytebuffer); - std::ostringstream ret; - for (int i = 0; i < arr_len; ++i) { - char hex1; - char hex2; - int value = byte_arr[i]; - // parse 16 - int v1 = value / 16; - int v2 = value % 16; - if (v1 >= 0 && v1 <= 9) { - hex1 = static_cast(48 + v1); - } else { - hex1 = static_cast(55 + v1); - } - - if (v2 >= 0 && v2 <= 9) { - hex2 = static_cast(48 + v2); - } else { - hex2 = static_cast(55 + v2); - } - - ret << hex1; - ret << hex2; - } - - return ret.str(); - } - -private: - static char data[16]; - static int32_t hash_code; - static int16_t counter; - static int16_t basePos; - static std::chrono::system_clock::time_point m_startTime; - static std::chrono::system_clock::time_point m_nextStartTime; - static absl::Mutex mtx_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index b88fe860c..8a13b1524 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -70,23 +70,10 @@ class ProcessQueue { */ bool consume(int batch_size, std::vector& messages) LOCKS_EXCLUDED(cached_messages_mtx_); - int64_t termId() const; - - /** - * Try to lease current process queue for another term with term_id from current pop response, which is forwarded - * by broker and is of previous request. - * - * @param current_term_id Term Id of previous request - * @return true if new lease is granted; false otherwise. - */ - bool leaseNextTerm(int64_t current_term_id, absl::string_view source_host); - void updateThrottleTimestamp() { last_throttle_timestamp_ = std::chrono::steady_clock::now(); } std::atomic_int& messageCachedNumber() { return message_cached_number_; } - std::string requestId() const; - ConsumeMessageType consumeType() const { return consume_type_; } void nextOffset(int64_t next_offset) { @@ -129,11 +116,6 @@ class ProcessQueue { std::string simple_name_; - /** - * This field indicate term-id of the in-flight pop-request. - */ - std::atomic term_id_; - // callback std::weak_ptr call_back_owner_; std::shared_ptr client_instance_; diff --git a/src/main/cpp/scheduler/BUILD.bazel b/src/main/cpp/scheduler/BUILD.bazel index 1ee04d08e..23ee433e5 100644 --- a/src/main/cpp/scheduler/BUILD.bazel +++ b/src/main/cpp/scheduler/BUILD.bazel @@ -10,12 +10,11 @@ cc_library( strip_include_prefix = "//src/main/cpp/scheduler/include", deps = [ "//api:rocketmq_interface", - "//src/main/cpp/base:base_library", - "//src/main/cpp/concurrent:countdown_latch_library", - "//external:libuv", + "//src/main/cpp/log:log_library", "@com_google_absl//absl/base", - "@com_google_absl//absl/strings", - "@com_github_progschj_ThreadPool//:ThreadPool", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/container:flat_hash_set", + "@com_github_grpc_grpc//:grpc++", ], visibility = ["//visibility:public"], ) \ No newline at end of file diff --git a/src/main/cpp/scheduler/Scheduler.cpp b/src/main/cpp/scheduler/Scheduler.cpp index 0b7605b2b..0ebcb86df 100644 --- a/src/main/cpp/scheduler/Scheduler.cpp +++ b/src/main/cpp/scheduler/Scheduler.cpp @@ -1,263 +1,116 @@ #include "Scheduler.h" +#include "LoggerImpl.h" #include "absl/memory/memory.h" +#include "src/core/lib/iomgr/closure.h" +#include #include -#include -#include -using namespace rocketmq; +ROCKETMQ_NAMESPACE_BEGIN -Scheduler::Scheduler() - : loop_(), async_handle_(), running_(false), uv_loop_latch_(1), - thread_pool_(absl::make_unique(1)) { - daemon_callback_ = std::bind(&Scheduler::do_daemon, this); - functor_ = std::make_shared(&daemon_callback_); - spdlog::set_level(spdlog::level::trace); +void Scheduler::clear(TimerTask* timer_task) { + absl::MutexLock lk(&tasks_mtx_); + tasks_.erase(reinterpret_cast(timer_task)); } -Scheduler::~Scheduler() { - if (running_.load()) { - SPDLOG_WARN("shutdown() should have been invoked"); - } -} - -void Scheduler::start() { - bool expected = false; - if (running_.compare_exchange_strong(expected, true)) { - // Initialize uv_loop - if (uv_loop_init(&loop_)) { - SPDLOG_ERROR("uv_loop_init failed"); - } - - async_handle_.data = this; - uv_async_init(&loop_, &async_handle_, &Scheduler::uv_async_do_schedule); - - // Initialize a daemon timer task which keeps uv_loop looping. - auto timer_handler = new uv_timer_t(); - auto timer = new Timer(timer_handler, functor_, this); - timer_handler->data = timer; - uv_timer_init(&loop_, timer_handler); - uv_timer_start(timer_handler, &Scheduler::uv_timer_callback, 0, 1000); - - // Event loop lambda - auto loop_lambda = [this] { - spdlog::info("uv_run starts to loop"); - if (uv_run(&loop_, UV_RUN_DEFAULT)) { - spdlog::debug("uv_stop() was called while there are still active handles or requests."); - } else { - spdlog::info("uv_run completes"); - } - }; - - // Run event loop - loop_thread_ = std::thread(loop_lambda); - - // Wait till uv_run actually fires - uv_loop_latch_.await(); - SPDLOG_DEBUG("There are {} active handle", uv_loop_alive(&loop_)); - } -} - -void Scheduler::shutdown() { - // Mark running flag as false - bool expected = true; - if (running_.compare_exchange_strong(expected, false)) { - SPDLOG_TRACE("Scheduler starts to shut down"); - uv_async_send(&async_handle_); - - // Join to wait the thread to quit - SPDLOG_TRACE("Scheduler starts to wait loop_thread"); - if (loop_thread_.joinable()) { - loop_thread_.join(); - } - SPDLOG_TRACE("Scheduler awaits loop_thread OK"); - - // Close uv_loop - if (uv_loop_alive(&loop_)) { - SPDLOG_WARN("There are {} active events", uv_loop_alive(&loop_)); - } else { - SPDLOG_DEBUG("There is no alive uv handles any more"); - } - - int loop_close_status = uv_loop_close(&loop_); - assert(0 == loop_close_status); - if (loop_close_status == UV_EBUSY) { - SPDLOG_ERROR("uv_loop_close failed with UV_EBUSY."); - abort(); - } - SPDLOG_DEBUG("Scheduler has shut down"); - } -} - -bool Scheduler::schedule(const FunctionalWeakPtr& functor, std::chrono::milliseconds delay, - std::chrono::milliseconds interval) { - if (!running_.load()) { - return false; - } - - if (!functor.lock()) { - SPDLOG_DEBUG("Functor pointed by weak_ptr should have been released"); - return false; +void Scheduler::onTrigger(void* arg, grpc_error_handle error) { + auto timer_task = reinterpret_cast(arg); + if (GRPC_ERROR_CANCELLED == error) { + SPDLOG_INFO("Task[{}] has been cancelled", timer_task->task_name); + timer_task->scheduler->clear(timer_task); + delete timer_task; + return; } + /** + * Consider the following fact that grpc_timer_cancel treats fail-to-cancel-in-flight task as success. + * Tasks without proper handles are deleted directly. + * ================================================================================================== + * Cancel an *timer. + * There are three cases: + * 1. We normally cancel the timer + * 2. The timer has already run + * 3. We can't cancel the timer because it is "in flight". + * + * In all of these cases, the cancellation is still considered successful. + * They are essentially distinguished in that the timer_cb will be run + * exactly once from either the cancellation (with error GRPC_ERROR_CANCELLED) + * or from the activation (with error GRPC_ERROR_NONE). + * + * Note carefully that the callback function MAY occur in the same callstack + * as grpc_timer_cancel. It's expected that most timers will be cancelled (their + * primary use is to implement deadlines), and so this code is optimized such + * that cancellation costs as little as possible. Making callbacks run inline + * matches this aim. + * + * Requires: cancel() must happen after init() on a given timer. + * ================================================================================================== + */ { - absl::MutexLock lock(&pending_task_list_mtx_); - pending_task_list_.emplace_back(functor, delay, interval); - } - return 0 == uv_async_send(&async_handle_); -} - -bool Scheduler::cancel(std::function* callable) { - { - absl::MutexLock lk(&pending_task_list_mtx_); - std::weak_ptr ptr; - Task task(ptr, std::chrono::milliseconds(0), std::chrono::milliseconds(0), - TaskType::CANCEL); - task.cancelled_callable_ = callable; - pending_task_list_.push_back(task); - } - uv_async_send(&async_handle_); - return true; -} - -void Scheduler::uv_timer_callback(uv_timer_t* handler) { - if (handler->data) { - auto timer = static_cast(handler->data); - - auto functor_shared_ptr = timer->callback_.lock(); - if (functor_shared_ptr) { - auto req = new uv_work_t; - req->data = timer; - timer->scheduler_->thread_pool_->enqueue(&Scheduler::uv_queue_work_callback, req); + absl::MutexLock lk(&timer_task->scheduler->tasks_mtx_); + uintptr_t ptr = reinterpret_cast(timer_task); + if (!timer_task->scheduler->tasks_.contains(ptr)) { + SPDLOG_WARN("Task[{}] should have been cancelled", timer_task->task_name); + delete timer_task; + return; } } -} - -void Scheduler::uv_close_callback(uv_handle_t* handle) { - if (handle->type == uv_handle_type::UV_TIMER) { - auto attachment = reinterpret_cast(handle->data); - // handle wil be deleted while releasing attachment - delete attachment; - } - SPDLOG_DEBUG("Scheduler#uv_close_callback: a uv_handle was closed OK. ThreadId={}", getThreadId()); -} - -void Scheduler::uv_async_do_schedule(uv_async_t* handle) { - auto scheduler = reinterpret_cast(handle->data); - scheduler->do_schedule(); -} -void Scheduler::do_schedule() { - if (running_.load(std::memory_order_relaxed)) { + SPDLOG_DEBUG("Execute timer-task: {}", timer_task->task_name); + timer_task->callback(); - // Process cancelled job - bool has_job_cancelled = false; - // Handle new scheduled jobs - { - absl::MutexLock lk(&pending_task_list_mtx_); - for (auto it = pending_task_list_.begin(); it != pending_task_list_.end();) { - if (it->type_ == TaskType::TIMER) { - auto timer_handle = new uv_timer_t(); - uv_timer_init(&loop_, timer_handle); - auto delay = it->delay_; - auto duration = it->interval_; - auto timer = new Timer(timer_handle, it->functor_ptr_, this); - // Make handle#data point to the actual functor - timer_handle->data = timer; - if (uv_timer_start(timer_handle, &Scheduler::uv_timer_callback, delay.count(), duration.count())) { - // As it failed to arm a timer, we just delete the resources. - delete timer; - timer_handle->data = nullptr; - SPDLOG_ERROR("uv_timer_start failed"); - } - it = pending_task_list_.erase(it); - } else { - if (it->type_ == TaskType::CANCEL) { - has_job_cancelled = true; - } - it++; - } - } - } - - if (has_job_cancelled) { - uv_walk(&loop_, uv_cancel_walk_cb, this); - } - } else { - uv_walk(&loop_, &Scheduler::uv_stop_walk_cb, nullptr); - } - SPDLOG_DEBUG("do_schedule threadId={}", getThreadId()); -} - -void Scheduler::uv_stop_walk_cb(uv_handle_t* handle, void* arg) { - if (handle->type == uv_handle_type::UV_TIMER) { - auto timer_handle = reinterpret_cast(handle); - SPDLOG_DEBUG("uv_timer_stop a timer handle"); - uv_timer_stop(timer_handle); + if (!timer_task->interval) { + SPDLOG_DEBUG("Completed single-shot timer task[{}]", timer_task->task_name); + timer_task->scheduler->clear(timer_task); + delete timer_task; + return; } - uv_close(handle, &Scheduler::uv_close_callback); -} -void Scheduler::uv_cancel_walk_cb(uv_handle_t* handle, void* arg) { - auto scheduler = reinterpret_cast(arg); - if (scheduler->running_.load(std::memory_order_relaxed)) { - scheduler->check_and_cancel(handle); - } + SPDLOG_DEBUG("Periodic task[name={}] is scheduled for the next round", timer_task->task_name); + grpc_core::ExecCtx exec_ctx; + grpc_timer_init(&timer_task->g_timer, grpc_core::ExecCtx::Get()->Now() + timer_task->interval, + GRPC_CLOSURE_CREATE(&Scheduler::onTrigger, timer_task, grpc_schedule_on_exec_ctx)); } -void Scheduler::check_and_cancel(uv_handle_t* handle) { - if (!handle) { - return; - } +Scheduler::Scheduler() { spdlog::set_level(spdlog::level::debug); } - if (handle->type != uv_handle_type::UV_TIMER) { - return; - } +Scheduler::~Scheduler() {} - auto timer = reinterpret_cast(handle->data); - if (!timer) { - return; - } +std::uintptr_t Scheduler::schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) { + auto timer_task = new TimerTask(); + timer_task->callback = std::move(functor); + timer_task->scheduler = this; + timer_task->interval = interval.count(); + timer_task->task_name = task_name; - auto ptr = timer->callback_.lock(); - if (!ptr) { - return; + { + absl::MutexLock lk(&tasks_mtx_); + tasks_.insert(reinterpret_cast(timer_task)); } - auto callable = ptr->getFunction(); + grpc_core::ExecCtx exec_ctx; + grpc_timer_init(&timer_task->g_timer, grpc_core::ExecCtx::Get()->Now() + delay.count(), + GRPC_CLOSURE_CREATE(&Scheduler::onTrigger, timer_task, grpc_schedule_on_exec_ctx)); + SPDLOG_DEBUG("Task[name={}] scheduled OK", timer_task->task_name); + return reinterpret_cast(timer_task); +} - bool found = false; +void Scheduler::cancel(std::uintptr_t task_id) { { - absl::MutexLock lk(&pending_task_list_mtx_); - for (auto it = pending_task_list_.begin(); it != pending_task_list_.end(); it++) { - if (it->type_ == TaskType::CANCEL && it->cancelled_callable_ == callable) { - pending_task_list_.erase(it); - found = true; - break; - } + absl::MutexLock lk(&tasks_mtx_); + if (!tasks_.contains(task_id)) { + SPDLOG_WARN("No matched schedule task found. Cancel ignored"); + return; } } - if (found) { - uv_timer_stop(reinterpret_cast(handle)); - uv_close(handle, &Scheduler::uv_close_callback); - } -} - -void Scheduler::do_daemon() { uv_loop_latch_.countdown(); } + auto timer_task = reinterpret_cast(task_id); + grpc_core::ExecCtx exec_ctx; + grpc_timer_cancel(&timer_task->g_timer); + SPDLOG_DEBUG("Scheduled task[name={}] will get cancelled", timer_task->task_name); -std::string Scheduler::getThreadId() { - std::stringstream ss; - ss << std::this_thread::get_id(); - return ss.str(); + clear(timer_task); } -void Scheduler::uv_queue_work_callback(uv_work_t* req) { - auto timer = static_cast(req->data); - FunctionalSharePtr callback = timer->callback_.lock(); - if (callback) { - SPDLOG_TRACE("ThreadID={} executes scheduled task", getThreadId()); - (*(callback->getFunction()))(); - } - delete req; -} \ No newline at end of file +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index a60256d17..492f13e2d 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -1,129 +1,55 @@ #pragma once - -#include -#include +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" +#include "src/core/lib/iomgr/timer.h" +#include #include #include -#include -#include #include -#include "CountdownLatch.h" -#include "Functional.h" -#include "LoggerImpl.h" -#include "ThreadPool.h" -#include "uv.h" - -namespace rocketmq { -class Scheduler { - /** - * uv_handle_t attachment structure. - */ - enum TaskType { TIMER, CANCEL }; - - template struct Task { - std::weak_ptr functor_ptr_; - Duration delay_; - Duration interval_; - TaskType type_; - - // pointer to cancelled task - void* cancelled_callable_; - Task(std::weak_ptr functor_ptr, Duration delay, Duration interval, TaskType type = TIMER) - : functor_ptr_(std::move(functor_ptr)), delay_(delay), interval_(interval), type_(type), - cancelled_callable_(nullptr) {} - }; - - struct Attachment { - TaskType type_; - uv_handle_t* uv_handle_; - explicit Attachment(TaskType type, uv_handle_t* uv_handle) : type_(type), uv_handle_(uv_handle) {} - - virtual ~Attachment() { - if (TIMER == type_) { - delete reinterpret_cast(uv_handle_); - } else { - delete reinterpret_cast(uv_handle_); - } - } - }; +ROCKETMQ_NAMESPACE_BEGIN - struct Timer : public Attachment { - FunctionalWeakPtr callback_; - Scheduler* scheduler_; - Timer(uv_timer_t* timer_handle, FunctionalWeakPtr callback, Scheduler* scheduler) - : Attachment(TIMER, reinterpret_cast(timer_handle)), callback_(std::move(callback)), - scheduler_(scheduler) {} +class Scheduler; - ~Timer() override { - SPDLOG_DEBUG("~Timer invoked"); - } - }; +struct TimerTask { + Scheduler* scheduler; + grpc_timer g_timer; + std::function callback; + grpc_millis interval; + std::string task_name; +}; +class Scheduler { public: Scheduler(); ~Scheduler(); - void start(); - - void shutdown(); - /** - * @callable Pointer to the functor. Lifecycle of this functor should be maintained by the caller. + * @functor Pointer to the functor. Lifecycle of this functor should be maintained by the caller. + * @task_name Name of the task. Task name would be very helpful at debug site. * @delay The amount of time to wait before the first shot. * @interval The interval between each fire-shot. If it is 0ms, callable will just fired once. - * @fixed_delay Execute callable with fixed delay if true; otherwise, scheduler will try its best to execute callable - * with fixed rate */ - bool schedule(const FunctionalWeakPtr& functor, std::chrono::milliseconds delay, std::chrono::milliseconds interval) - LOCKS_EXCLUDED(pending_task_list_mtx_); - - bool cancel(std::function* callable) LOCKS_EXCLUDED(pending_task_list_mtx_); - -private: - uv_loop_t loop_; + std::uintptr_t schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) + LOCKS_EXCLUDED(tasks_mtx_); /** - * Used to notify loop thread that some external events are pending to process + * Note: + * Periodic tasks should be explicitly cancelled once they are no longer needed. */ - uv_async_t async_handle_; - - std::atomic_bool running_; + void cancel(std::uintptr_t task_id) LOCKS_EXCLUDED(tasks_mtx_); - std::thread loop_thread_; - - CountdownLatch uv_loop_latch_; - - std::function daemon_callback_; - FunctionalSharePtr functor_; - - std::vector> pending_task_list_ GUARDED_BY(pending_task_list_mtx_); - absl::Mutex pending_task_list_mtx_; // Protects pending_task_list_. - - std::unique_ptr thread_pool_; - - static void uv_timer_callback(uv_timer_t* handle); - - static void uv_close_callback(uv_handle_t* handle); - - static void uv_async_do_schedule(uv_async_t* handle); - - static void uv_queue_work_callback(uv_work_t* req); - - void do_daemon(); - - /** - * Will execute in uv_loop thread. - */ - void do_schedule(); - - static void uv_stop_walk_cb(uv_handle_t* handle, void* arg); - - static void uv_cancel_walk_cb(uv_handle_t* handle, void* arg); +private: + static void onTrigger(void* arg, grpc_error_handle error); - void check_and_cancel(uv_handle_t* handle) LOCKS_EXCLUDED(pending_task_list_mtx_); + void clear(TimerTask* timer_task) LOCKS_EXCLUDED(tasks_mtx_); - static std::string getThreadId(); + absl::flat_hash_set tasks_ GUARDED_BY(tasks_mtx_); + absl::Mutex tasks_mtx_; }; -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index 4cd8bf76f..45c57b92b 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -6,9 +6,9 @@ cc_test( ], deps = [ "//src/main/cpp/base:top_addressing", + "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -24,7 +24,6 @@ cc_test( copts = [ "-Isrc/main/client/mocks/include", ], - linkstatic = True, ) cc_test( @@ -37,5 +36,4 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientIntegrationTest.cpp b/src/test/cpp/it/RpcClientIntegrationTest.cpp index 29229ab2c..01de2c05d 100644 --- a/src/test/cpp/it/RpcClientIntegrationTest.cpp +++ b/src/test/cpp/it/RpcClientIntegrationTest.cpp @@ -151,9 +151,9 @@ TEST_F(RpcClientIntegrationTest, testPop) { } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); - invocation_context->context_.set_deadline(deadline); + invocation_context->context.set_deadline(deadline); rpcClient.asyncReceive(receive_message_request, invocation_context); void* tag; @@ -207,7 +207,7 @@ TEST_F(RpcClientIntegrationTest, testPopAndAck) { google::rpc::Status ack_status; std::atomic_bool ack_done(false); - invocation_context.callback_ = [&](const grpc::Status& status, const grpc::ClientContext& client_context, + invocation_context.callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, const AckMessageResponse& response) { ack_rpc_status = status; ack_status = response.common().status(); @@ -231,9 +231,9 @@ TEST_F(RpcClientIntegrationTest, testPopAndAck) { } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); - invocation_context->context_.set_deadline(deadline); + invocation_context->context.set_deadline(deadline); rpcClient.asyncReceive(receive_message_request, invocation_context); void* tag; diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 0470d1f76..7c38f58ff 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -1,5 +1,6 @@ #include "ClientConfig.h" #include "IdentifiableMock.h" +#include "InvocationContext.h" #include "LogInterceptorFactory.h" #include "MixAll.h" #include "RpcClientImpl.h" @@ -72,22 +73,21 @@ class RpcClientTest : public ::testing::Test { absl::CondVar cv; bool completed = false; - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, - const QueryRouteResponse& response) { - ASSERT_TRUE(status.ok()); + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); std::cout << "Route Response:" << route_response.DebugString() << std::endl; absl::MutexLock lk(&mtx); - route_response = response; + route_response = invocation_context->response; completed = true; cv.SignalAll(); }; auto invocation_context = new InvocationContext(); - invocation_context->callback_ = callback; - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); for (const auto& item : metadata_) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } name_server_client.asyncQueryRoute(route_request, invocation_context); @@ -128,7 +128,7 @@ class RpcClientTest : public ::testing::Test { system_attribute->set_message_id(msgId); system_attribute->set_message_type(rmq::MessageType::NORMAL); system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); - system_attribute->set_born_host(UtilAll::getHostIPv4()); + system_attribute->set_born_host(UtilAll::hostname()); system_attribute->set_tag("TagA"); send_message_request.mutable_message()->set_body("Example data"); } @@ -192,22 +192,22 @@ TEST_F(RpcClientTest, testRouteInfo) { bool completed = false; absl::Mutex mtx; absl::CondVar cv; - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, - const QueryRouteResponse& response) { - if (!status.ok()) { - std::cout << "code: " << status.error_code() << ", message: " << status.error_message() << std::endl; + auto callback = [&](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + std::cout << "code: " << invocation_context->status.error_code() + << ", message: " << invocation_context->status.error_message() << std::endl; } - ASSERT_TRUE(status.ok()); - EXPECT_TRUE(google::rpc::Code::OK == response.common().status().code()); - EXPECT_FALSE(response.partitions().empty()); + ASSERT_TRUE(invocation_context->status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); + EXPECT_FALSE(invocation_context->response.partitions().empty()); absl::MutexLock lk(&mtx); cv.SignalAll(); }; - invocation_context->callback_ = callback; - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); + invocation_context->callback = callback; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); for (const auto& item : metadata_) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } client.asyncQueryRoute(request, invocation_context); @@ -224,15 +224,15 @@ TEST_F(RpcClientTest, testSendMessageAsync) { auto context = new InvocationContext(); for (const auto& entry : metadata_) { - context->context_.AddMetadata(entry.first, entry.second); + context->context.AddMetadata(entry.first, entry.second); } - context->callback_ = [](const grpc::Status& status, const grpc::ClientContext& client_context, - const SendMessageResponse& response) { - if ((!status.ok())) { - std::cout << "error code: " << status.error_code() << ", error message: " << status.error_message() << std::endl; + context->callback = [](const InvocationContext* invocation_context) { + if ((!invocation_context->status.ok())) { + std::cout << "error code: " << invocation_context->status.error_code() + << ", error message: " << invocation_context->status.error_message() << std::endl; } - ASSERT_TRUE(status.ok()); + ASSERT_TRUE(invocation_context->status.ok()); }; client->asyncSend(request, context); std::thread th([&]() { @@ -260,13 +260,12 @@ TEST_F(RpcClientTest, testHeartbeat) { auto invocation_context = new InvocationContext(); for (const auto& entry : metadata_) { - invocation_context->context_.AddMetadata(entry.first, entry.second); + invocation_context->context.AddMetadata(entry.first, entry.second); } - invocation_context->callback_ = [&](const grpc::Status& status, const grpc::ClientContext& context, - const HeartbeatResponse& response) { - ASSERT_TRUE(status.ok()); - EXPECT_TRUE(google::rpc::Code::OK == response.common().status().code()); + invocation_context->callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); { completed = true; absl::MutexLock lk(&mtx); @@ -294,23 +293,22 @@ TEST_F(RpcClientTest, testQueryAssignment) { auto invocation_context = new InvocationContext(); for (const auto& entry : metadata_) { - invocation_context->context_.AddMetadata(entry.first, entry.second); + invocation_context->context.AddMetadata(entry.first, entry.second); } bool completed = false; absl::Mutex mtx; absl::CondVar cv; - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, - const QueryAssignmentResponse& response) { - ASSERT_TRUE(status.ok()); - ASSERT_FALSE(response.assignments().empty()); + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); + ASSERT_FALSE(invocation_context->response.assignments().empty()); completed = true; { absl::MutexLock lk(&mtx); cv.SignalAll(); } }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client->asyncQueryAssignment(request, invocation_context); while (!completed) { absl::MutexLock lk(&mtx); @@ -321,27 +319,26 @@ TEST_F(RpcClientTest, testQueryAssignment) { TEST_F(RpcClientTest, testHealthCheck) { auto client = brokerRpcClient(); HealthCheckRequest request; - const std::string& client_host = UtilAll::getHostIPv4(); + const std::string& client_host = UtilAll::hostname(); request.set_client_host(client_host); HealthCheckResponse response; auto invocation_context = new InvocationContext(); for (const auto& entry : metadata_) { - invocation_context->context_.AddMetadata(entry.first, entry.second); + invocation_context->context.AddMetadata(entry.first, entry.second); } absl::Mutex mtx; absl::CondVar cv; bool completed = false; - invocation_context->callback_ = [&](const grpc::Status& status, const grpc::ClientContext& context, - const HealthCheckResponse& response) { + invocation_context->callback = [&](const InvocationContext* invocation_context) { { absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); } - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); + EXPECT_TRUE(invocation_context->status.ok()); + EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); }; client->asyncHealthCheck(request, invocation_context); while (!completed) { diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index 99e3c416c..06156110f 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -1,5 +1,10 @@ #include "TopAddressing.h" +#include "RateLimiter.h" +#include "grpc/grpc.h" +#include "spdlog/spdlog.h" #include "gtest/gtest.h" +#include +#include #include #include @@ -7,21 +12,42 @@ ROCKETMQ_NAMESPACE_BEGIN class TopAddressingTest : public testing::Test { public: - void SetUp() override {} - - void TearDown() override {} + void SetUp() override { + grpc_init(); + spdlog::set_level(spdlog::level::debug); + } -protected: - TopAddressing top_addressing; + void TearDown() override { grpc_shutdown(); } }; TEST_F(TopAddressingTest, testFetchNameServerAddresses) { std::vector list; - EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); - EXPECT_FALSE(list.empty()); - for (const auto& item : list) { - std::cout << item << std::endl; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + bool success = false; + auto callback = [&](bool ok, const std::vector& name_server_list) { + success = ok; + list.insert(list.end(), name_server_list.begin(), name_server_list.end()); + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + }; + TopAddressing top_addressing; + top_addressing.fetchNameServerAddresses(callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithTimeout(&mtx, absl::Seconds(3)); + } } + + ASSERT_TRUE(success); + EXPECT_FALSE(list.empty()); } TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { @@ -29,33 +55,79 @@ TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { setenv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center", override); setenv(HostInfo::ENV_LABEL_STAGE, "DAILY", override); std::vector list; - EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); - EXPECT_FALSE(list.empty()); - for (const auto& item : list) { - std::cout << item << std::endl; + + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + + bool success = false; + auto callback = [&](bool ok, const std::vector& name_server_list) { + success = ok; + list.insert(list.end(), name_server_list.begin(), name_server_list.end()); + { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + } + }; + TopAddressing top_addressing; + top_addressing.fetchNameServerAddresses(callback); + + while (!completed) { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithTimeout(&mtx, absl::Seconds(3)); + } } + + ASSERT_TRUE(success); + EXPECT_FALSE(list.empty()); } -TEST_F(TopAddressingTest, testFetchNameServerAddresses_benchmark) { - int override = 1; - setenv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center", override); - setenv(HostInfo::ENV_LABEL_STAGE, "DAILY", override); +TEST_F(TopAddressingTest, testPerf) { + TopAddressing top_addressing; + RateLimiter<10> rate_limiter(100); + + std::atomic_bool stopped(false); + std::atomic_long qps(0); + auto callback = [&](bool ok, const std::vector& name_sever_list) { + if (ok) { + qps++; + } else { + SPDLOG_WARN("Yuck, Bad HTTP response"); + } + }; + auto benchmark = [&]() { - for (int i = 0; i < 256; ++i) { - std::vector list; - EXPECT_TRUE(top_addressing.fetchNameServerAddresses(list)); - EXPECT_FALSE(list.empty()); + while (!stopped) { + // rate_limiter.acquire(); + SPDLOG_DEBUG("Submit a fetch request"); + top_addressing.fetchNameServerAddresses(callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }; + + auto stats = [&]() { + while (!stopped) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + long value = qps.load(std::memory_order_relaxed); + qps.fetch_sub(value, std::memory_order_relaxed); + SPDLOG_INFO("QPS: {}", value); } }; - std::vector threads; - threads.reserve(std::thread::hardware_concurrency() + 1); - for (int i = 0; i < std::thread::hardware_concurrency() + 1; i++) { - threads.emplace_back(benchmark); + std::thread benchmark_thread(benchmark); + std::thread stats_thread(stats); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + stopped.store(true); + + if (stats_thread.joinable()) { + stats_thread.join(); } - for (auto& thread : threads) { - thread.join(); + if (benchmark_thread.joinable()) { + benchmark_thread.join(); } } diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index 5b6f254e0..34e80fe47 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -81,23 +81,23 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { request.mutable_topic()->set_arn(arn_); request.mutable_topic()->set_name(topic_); auto invocation_context = new InvocationContext(); - invocation_context->context_.set_deadline(std::chrono::system_clock::now() + + invocation_context->context.set_deadline(std::chrono::system_clock::now() + absl::ToChronoMilliseconds(client_config_.getIoTimeout())); for (const auto& item : metadata_) { - invocation_context->context_.AddMetadata(item.first, item.second); + invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [this](const grpc::Status& status, const grpc::ClientContext& context, - const QueryRouteResponse& response) { - if (!status.ok()) { - std::cout << "code: " << status.error_code() << ", message: " << status.error_message() << std::endl; + auto callback = [this](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + std::cout << "code: " << invocation_context->status.error_code() + << ", message: " << invocation_context->status.error_message() << std::endl; } else { - std::cout << "Response debug string:" << response.DebugString() << std::endl; + std::cout << "Response debug string:" << invocation_context->response.DebugString() << std::endl; } - ASSERT_TRUE(status.ok()); + ASSERT_TRUE(invocation_context->status.ok()); std::vector partitions; - for (const auto& item : response.partitions()) { + for (const auto& item : invocation_context->response.partitions()) { Topic topic(arn_, topic_); Permission permission; switch (item.permission()) { @@ -141,7 +141,8 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { Partition partition(topic, item.id(), permission, broker); partitions.emplace_back(partition); } - auto topic_route_data = std::make_shared(partitions, response.common().DebugString()); + auto topic_route_data = + std::make_shared(partitions, invocation_context->response.common().DebugString()); rocketmq::TopicPublishInfo topic_publish_info(topic_, topic_route_data); rocketmq::MQMessageQueue message_queue; EXPECT_TRUE(topic_publish_info.selectOneMessageQueue(message_queue)); @@ -149,7 +150,7 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { EXPECT_STREQ(topic_.c_str(), message_queue.getTopic().c_str()); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; client_->asyncQueryRoute(request, invocation_context); } diff --git a/src/test/cpp/ut/BUILD b/src/test/cpp/ut/BUILD index 34c2f6d11..aca78b243 100644 --- a/src/test/cpp/ut/BUILD +++ b/src/test/cpp/ut/BUILD @@ -9,17 +9,6 @@ cc_test( ] ) -cc_test( - name = "event_test", - srcs = [ - "EventTest.cpp", - ], - deps = [ - "//external:libuv", - "@com_google_googletest//:gtest_main", - ], -) - cc_test( name = "log_test", srcs = [ @@ -41,17 +30,6 @@ cc_test( ] ) -cc_test( - name = "curl_test", - srcs = [ - "CurlTest.cpp", - ], - deps = [ - "@curl//:curl", - "@com_google_googletest//:gtest_main", - ], -) - cc_test( name = "absl_time_test", srcs = [ @@ -85,14 +63,9 @@ cc_test( ], ) -cc_test( - name = "thread_pool_test", +cc_binary( + name = "mac_test", srcs = [ - "ThreadPoolTest.cpp", + "MacTest.cpp", ], - deps = [ - "@com_github_progschj_ThreadPool//:ThreadPool", - "@com_github_gabime_spdlog//:spdlog", - "@com_google_googletest//:gtest_main", - ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/CMakeLists.txt b/src/test/cpp/ut/CMakeLists.txt index 8b87dcf32..40f6f66b6 100644 --- a/src/test/cpp/ut/CMakeLists.txt +++ b/src/test/cpp/ut/CMakeLists.txt @@ -8,9 +8,6 @@ target_link_libraries(log_test gtest_main) add_test(ut_log_test log_test) -add_executable(thread_pool_test ThreadPoolTest.cpp) -target_link_libraries(thread_pool_test gtest_main spdlog thread_pool) - add_executable(event_test EventTest.cpp) target_link_libraries(event_test gtest_main @@ -35,7 +32,10 @@ target_link_libraries(hex_test gtest_main) add_subdirectory(base) +add_subdirectory(concurrent) +add_subdirectory(scheduler) add_subdirectory(client) +add_subdirectory(grpc) add_subdirectory(rocketmq) add_subdirectory(logger) add_subdirectory(admin) \ No newline at end of file diff --git a/src/test/cpp/ut/CurlTest.cpp b/src/test/cpp/ut/CurlTest.cpp deleted file mode 100644 index b130c2512..000000000 --- a/src/test/cpp/ut/CurlTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "curl/curl.h" -#include - -size_t write_callback(void* contents, size_t size, size_t nmemb, void* user_param) { - std::string result; - result.reserve(size * nmemb); - result.append(reinterpret_cast(contents), size * nmemb); - std::cout << "Callback invoked. Size: " << size << ", nmemb: " << nmemb << ". Content: " << result << std::endl; - return size * nmemb; -} - -int main(int argc, char* argv[]) { - CURL* curl; - CURLcode res; - curl_global_init(CURL_GLOBAL_DEFAULT); - curl = curl_easy_init(); - const char* url = "http://www.taobao.com"; - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - res = curl_easy_perform(curl); - if (res != CURLE_OK) { - std::cerr << "curl_easy_perform() failed" << curl_easy_strerror(res) << std::endl; - } - curl_easy_cleanup(curl); - } - curl_global_cleanup(); - return 0; -} \ No newline at end of file diff --git a/src/test/cpp/ut/MacTest.cpp b/src/test/cpp/ut/MacTest.cpp new file mode 100644 index 000000000..9a4adc50a --- /dev/null +++ b/src/test/cpp/ut/MacTest.cpp @@ -0,0 +1,84 @@ +/* +** showip.c -- show IP addresses for a host given on the command line +*/ + +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#define AF_FAMILY AF_LINK +#elif defined(__linux__) +#include +#include +#include +#define AF_FAMILY AF_PACKET +#endif + +#include +#include +#include +#include +#include +#include + +#include + +bool macAddress(std::vector &mac) { + struct ifaddrs *ifap = nullptr, *ifaptr = nullptr; + if (getifaddrs(&ifap)) { + return false; + } + + for (ifaptr = ifap; ifaptr; ifaptr = ifaptr->ifa_next) { + if (!ifaptr->ifa_addr) { + continue; + } + + if (ifaptr->ifa_addr->sa_family == AF_FAMILY) { +#if defined(__APPLE__) + unsigned char* ptr = + reinterpret_cast(LLADDR(reinterpret_cast(ifaptr->ifa_addr))); +#elif defined(__linux__) + unsigned char* ptr = reinterpret_cast(reinterpret_cast(ifaptr->ifa_addr)->sll_addr); +#endif + bool all_zero = true; + for (int i = 0; i < 6; i++) { + if (*(ptr + i) != 0) { + all_zero = false; + break; + } + } + if (all_zero) { + std::cout << "Skip MAC address of Network Interface: " << ifaptr->ifa_name << std::endl; + continue; + } + std::cout << "Use MAC address of Network Interface: " << ifaptr->ifa_name << std::endl; + // MAC address has 48 bits + mac.resize(6); + memcpy(mac.data(), ptr, 6); + // break; + } else if (ifaptr->ifa_addr->sa_family == AF_INET) { + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, &reinterpret_cast(ifaptr->ifa_addr)->sin_addr, buffer, INET_ADDRSTRLEN); + std::cout << "Inet: " << ifaptr->ifa_name << ": " << buffer << std::endl; + } + } + freeifaddrs(ifap); + return true; +} + +int main(int argc, char* argv[]) { + std::vector data; + if (macAddress(data)) { + for (auto& item : data) { + std::printf("%02X:", item); + } + std::cout << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/src/test/cpp/ut/ThreadPoolTest.cpp b/src/test/cpp/ut/ThreadPoolTest.cpp deleted file mode 100644 index 9e0299c68..000000000 --- a/src/test/cpp/ut/ThreadPoolTest.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include - -#include "ThreadPool.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" - -TEST(ThreadPoolTest, testLambda) { - ThreadPool pool(4); - std::vector> results; - auto task = [](int i) { - { spdlog::info("hello {}", i); } - std::this_thread::sleep_for(std::chrono::seconds(1)); - { spdlog::info("world {}", i); } - return i * i; - }; - - for (int i = 0; i < 8; ++i) { - results.emplace_back(pool.enqueue(task, i)); - } - - for (auto&& result : results) { - { spdlog::info("result {}", result.get()); } - } -} - -int task(int input) { - { spdlog::info("hello {}", input); } - std::this_thread::sleep_for(std::chrono::seconds(1)); - { spdlog::info("world {}", input); } - return input * input; -} - -TEST(ThreadPoolTest, testFuntion) { - - ThreadPool pool(4); - std::vector> results; - - for (int i = 0; i < 8; ++i) { - results.emplace_back(pool.enqueue(&task, i)); - } - - for (auto&& result : results) { - { spdlog::info("result {}", result.get()); } - } -} diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index 5d55ecb47..9a499258c 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -8,7 +8,6 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -21,7 +20,6 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -33,7 +31,6 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -46,17 +43,27 @@ cc_test( "@com_google_googletest//:gtest_main", "@zlib//:zlib", ], - linkstatic = True, ) cc_test( - name = "ut_request_id_generator", + name = "ghttp_client_test", srcs = [ - "RequestIdGeneratorTest.cpp", + "GHttpClientTest.cpp", + ], + deps = [ + "//src/main/cpp/base:ghttp_client", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "unique_id_generator_test", + srcs = [ + "UniqueIdGeneratorTest.cpp", ], deps = [ "//src/main/cpp/base:base_library", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/CMakeLists.txt b/src/test/cpp/ut/base/CMakeLists.txt index e537e45dd..c4a88fe34 100644 --- a/src/test/cpp/ut/base/CMakeLists.txt +++ b/src/test/cpp/ut/base/CMakeLists.txt @@ -24,8 +24,10 @@ target_link_libraries(histogram_test base_library) add_test(test_histogram histogram_test) -add_executable(ut_request_id_generator RequestIdGeneratorTest.cpp) -target_link_libraries(ut_request_id_generator + +add_executable(unique_id_generator_test UniqueIdGeneratorTest.cpp) +target_link_libraries(unique_id_generator_test PRIVATE gtest_main - base_library) \ No newline at end of file + base_library) +add_test(ut_unique_id_generator unique_id_generator_test) \ No newline at end of file diff --git a/src/test/cpp/ut/base/GHttpClientTest.cpp b/src/test/cpp/ut/base/GHttpClientTest.cpp new file mode 100644 index 000000000..87407d96b --- /dev/null +++ b/src/test/cpp/ut/base/GHttpClientTest.cpp @@ -0,0 +1,51 @@ +#include "GHttpClient.h" +#include "LoggerImpl.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include +#include +ROCKETMQ_NAMESPACE_BEGIN + +class GHttpClientTest : public testing::Test { +public: + void SetUp() override { + SPDLOG_DEBUG("GHttpClient::SetUp() starts"); + grpc_init(); + SPDLOG_DEBUG("GHttpClient::SetUp() completed"); + } + + void TearDown() override { + SPDLOG_DEBUG("GHttpClientTest::TearDown() starts"); + grpc_shutdown(); + SPDLOG_DEBUG("GHttpClientTest::TearDown() completed"); + } +}; + +TEST_F(GHttpClientTest, testCtor) { + spdlog::set_level(spdlog::level::debug); + GHttpClient http_client; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto callback = [&](int status, const absl::flat_hash_map& metadata, + const std::string& body) { + absl::MutexLock lk(&mtx); + completed = true; + SPDLOG_DEBUG("HTTP status-code: {}, response body: {}", status, body); + cv.SignalAll(); + }; + + std::string host("grpc.io"); + http_client.start(); + http_client.get(HttpProtocol::HTTP, host, 8080, "/", callback); + if (!completed) { + absl::MutexLock lk(&mtx); + cv.WaitWithTimeout(&mtx, absl::Seconds(5)); + } + ASSERT_TRUE(completed); + http_client.shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp index 6ce7a7087..8dfb408cc 100644 --- a/src/test/cpp/ut/base/HistogramTest.cpp +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -1,7 +1,8 @@ #include "Histogram.h" #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN + class HistogramTest : public testing::Test { public: HistogramTest() : histogram_("Test", 3) {} @@ -38,4 +39,4 @@ TEST_F(HistogramTest, testHistogram) { EXPECT_STREQ(result.c_str(), expect.c_str()); } -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index b0e122510..6c2c5fec0 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -34,7 +34,7 @@ TEST_F(MixAllTest, testValidate_body_too_large) { std::string body; body.reserve(MixAll::MAX_MESSAGE_BODY_SIZE + 1); - for (int i = 0; i <= MixAll::MAX_MESSAGE_BODY_SIZE; ++i) { + for (uint32_t i = 0; i <= MixAll::MAX_MESSAGE_BODY_SIZE; ++i) { body.append("a"); } ASSERT_FALSE(MixAll::validate(MQMessage(topic, body))); diff --git a/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp b/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp deleted file mode 100644 index 2fceb3f0f..000000000 --- a/src/test/cpp/ut/base/RequestIdGeneratorTest.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "RequestIdGenerator.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class RequestIdGeneratorTest : public testing::Test {}; - -TEST_F(RequestIdGeneratorTest, testNextRequestId) { - std::string request_id = RequestIdGenerator::nextRequestId(); - ASSERT_TRUE(!request_id.empty()); - EXPECT_EQ(1, RequestIdGenerator::sequenceOf(request_id)); - std::cout << request_id << std::endl; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp new file mode 100644 index 000000000..227e1c815 --- /dev/null +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -0,0 +1,25 @@ +#include "UniqueIdGenerator.h" +#include "absl/container/flat_hash_set.h" +#include "rocketmq/RocketMQ.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(UniqueIdGeneratorTest, testNext) { + absl::flat_hash_set id_set; + uint32_t total = 500000; + uint32_t count = 0; + while (total--) { + std::string id = UniqueIdGenerator::instance().next(); + if (id_set.contains(id)) { + SPDLOG_WARN("Yuck, found an duplicated ID: {}", id); + } else { + id_set.insert(id); + } + ++count; + } + EXPECT_EQ(count, id_set.size()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index e3989ce23..326dedf03 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -14,25 +14,6 @@ class UtilAllTest : public testing::Test { void TearDown() override {} }; -TEST_F(UtilAllTest, testGetIPv4Addresses) { - spdlog::set_level(spdlog::level::debug); - std::vector addresses = UtilAll::getIPv4Addresses(); - EXPECT_FALSE(addresses.empty()); - for (const auto& ip : addresses) { - spdlog::debug("IP: {}", ip); - } - std::string chosen; - if (UtilAll::pickIPv4Address(addresses, chosen)) { - spdlog::debug("The chosen one is: {}", chosen); - } -} - -TEST_F(UtilAllTest, testGetHostIPv4) { - std::string ip = UtilAll::getHostIPv4(); - EXPECT_TRUE(UtilAll::LOOP_BACK_IP != ip); - spdlog::debug("Host IP: {}", ip); -} - TEST_F(UtilAllTest, testCompress) { std::string src("How are you doing?"); std::string dst; diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel index d2ee5a1ac..cecd8e4a3 100644 --- a/src/test/cpp/ut/client/BUILD.bazel +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -1,31 +1,4 @@ load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "scheduler_test", - srcs = [ - "SchedulerTest.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "//src/main/cpp/base:base_library", - "//src/main/cpp/concurrent:countdown_latch_library", - "//src/main/cpp/scheduler:scheduler_library", - "@com_google_googletest//:gtest_main", - ], - linkstatic = True, -) - -cc_test( - name = "countdown_latch_test", - srcs = [ - "CountdownLatchTest.cpp", - ], - deps = [ - "//src/main/cpp/concurrent:countdown_latch_library", - "@com_google_googletest//:gtest_main", - ], - linkstatic = True, -) - cc_test( name = "tls_helper_test", srcs = [ @@ -37,7 +10,6 @@ cc_test( "@com_google_googletest//:gtest_main", "@boringssl//:ssl", ], - linkstatic = True, ) cc_test( @@ -51,5 +23,4 @@ cc_test( "@com_google_googletest//:gtest_main", "@boringssl//:ssl", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CMakeLists.txt b/src/test/cpp/ut/client/CMakeLists.txt index bedefc8a1..be090ed9b 100644 --- a/src/test/cpp/ut/client/CMakeLists.txt +++ b/src/test/cpp/ut/client/CMakeLists.txt @@ -1,20 +1,3 @@ -add_executable(countdown_latch_test CountdownLatchTest.cpp) -target_link_libraries(countdown_latch_test - PRIVATE - gtest_main - countdown_latch_library - spdlog) -add_test(ut_countdown_latch_test countdown_latch_test) - -add_executable(scheduler_test SchedulerTest.cpp) -target_link_libraries(scheduler_test - PRIVATE - absl::synchronization - base_library - gtest_main - scheduler_library) -add_test(ut_scheduler_test scheduler_test) - if (ENABLE_TRACING) # add_executable(tracing_utility_test TracingUtilityTest.cpp) # target_link_libraries(tracing_utility_test diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp index d41d4e576..854dcecb3 100644 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -13,7 +13,7 @@ class RpcClientTest : public testing::Test { static void mockQueryRouteInfo(const QueryRouteRequest& request, InvocationContext* invocation_context) { - invocation_context->response_.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); for (int i = 0; i < 3; ++i) { auto partition = new rmq::Partition; partition->mutable_topic()->set_name(request.topic().name()); @@ -24,12 +24,10 @@ class RpcClientTest : public testing::Test { address->set_host(fmt::format("10.0.0.{}", i)); address->set_port(10911); endpoint->mutable_addresses()->AddAllocated(address); - invocation_context->response_.mutable_partitions()->AddAllocated(partition); + invocation_context->response.mutable_partitions()->AddAllocated(partition); } - // Mock invoke callback. - invocation_context->callback_(invocation_context->status_, invocation_context->context_, - invocation_context->response_); + invocation_context->onCompletion(true); } }; @@ -39,22 +37,20 @@ TEST_F(RpcClientTest, testMockedGetRouteInfo) { std::string topic = "sample_topic"; QueryRouteRequest request; request.mutable_topic()->set_name(topic); - QueryRouteResponse response; absl::flat_hash_map metadata; auto invocation_context = new InvocationContext(); absl::Mutex mtx; absl::CondVar cv; bool completed = false; - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, - const QueryRouteResponse& response) { - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - EXPECT_EQ(3, response.partitions().size()); + auto callback = [&](const InvocationContext* invocation_context) { + EXPECT_TRUE(invocation_context->status.ok()); + EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); + EXPECT_EQ(3, invocation_context->response.partitions().size()); absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; rpc_client_mock.asyncQueryRoute(request, invocation_context); while (!completed) { absl::MutexLock lk(&mtx); diff --git a/src/test/cpp/ut/client/SchedulerTest.cpp b/src/test/cpp/ut/client/SchedulerTest.cpp deleted file mode 100644 index 50bbba1b3..000000000 --- a/src/test/cpp/ut/client/SchedulerTest.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include - -#include "Functional.h" -#include "Scheduler.h" -#include "gtest/gtest.h" - -#include "absl/synchronization/mutex.h" - -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class SchedulerTest : public testing::Test { -public: - void SetUp() override { - spdlog::set_level(spdlog::level::debug); - std::cout << "SetUp invoked" << std::endl; - } - - void TearDown() override { std::cout << "TearDown invoked" << std::endl; } - -protected: - Scheduler scheduler; -}; - -TEST_F(SchedulerTest, testStartShutdown) { - scheduler.start(); - scheduler.shutdown(); -} - -TEST_F(SchedulerTest, testStartReentrancy) { - scheduler.start(); - scheduler.start(); - scheduler.shutdown(); -} - -TEST_F(SchedulerTest, testShutdownReentrancy) { - scheduler.start(); - scheduler.shutdown(); - scheduler.shutdown(); -} - -TEST_F(SchedulerTest, testShutdownWithoutStart) { scheduler.shutdown(); } - -absl::Mutex mtx; -absl::CondVar cv; -int callback_fire_count = 0; - -std::function callback = []() { - { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - } - std::cout << "It works from callback" << std::endl; -}; - -TEST_F(SchedulerTest, testScheduler) { - scheduler.start(); - FunctionalSharePtr functor = std::make_shared(&callback); - - bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(10), std::chrono::milliseconds(0)); - - EXPECT_TRUE(scheduled); - - // Wait till callback is executed. - { - absl::MutexLock lock(&mtx); - EXPECT_TRUE(!cv.WaitWithTimeout(&mtx, absl::Seconds(3))); - } - - scheduler.shutdown(); -} - -TEST_F(SchedulerTest, testCancel) { - scheduler.start(); - FunctionalSharePtr functor = std::make_shared(&callback); - bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(10), std::chrono::seconds(1)); - EXPECT_TRUE(scheduled); - std::this_thread::sleep_for(std::chrono::seconds(3)); - bool cancelled = scheduler.cancel(&callback); - EXPECT_TRUE(cancelled); - - { - absl::MutexLock lock(&mtx); - callback_fire_count = 0; - } - - std::this_thread::sleep_for(std::chrono::seconds(3)); - ASSERT_EQ(0, callback_fire_count); - - scheduler.shutdown(); -} - -TEST_F(SchedulerTest, testSchedulerManyTimes) { - scheduler.start(); - FunctionalSharePtr functor = std::make_shared(&callback); - bool scheduled = scheduler.schedule(functor, std::chrono::milliseconds(0), std::chrono::seconds(1)); - EXPECT_TRUE(scheduled); - - // Wait till callback is executed. - std::this_thread::sleep_for(std::chrono::seconds(5)); - - scheduler.shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/BUILD.bazel b/src/test/cpp/ut/concurrent/BUILD.bazel new file mode 100644 index 000000000..993fe1f84 --- /dev/null +++ b/src/test/cpp/ut/concurrent/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "countdown_latch_test", + srcs = [ + "CountdownLatchTest.cpp", + ], + deps = [ + "//src/main/cpp/concurrent:countdown_latch_library", + "@com_github_grpc_grpc//:grpc++", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/CMakeLists.txt b/src/test/cpp/ut/concurrent/CMakeLists.txt new file mode 100644 index 000000000..d8960f064 --- /dev/null +++ b/src/test/cpp/ut/concurrent/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(countdown_latch_test CountdownLatchTest.cpp) +target_link_libraries(countdown_latch_test + PRIVATE + gtest_main + countdown_latch_library + spdlog) +add_test(ut_countdown_latch_test countdown_latch_test) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CountdownLatchTest.cpp b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp similarity index 100% rename from src/test/cpp/ut/client/CountdownLatchTest.cpp rename to src/test/cpp/ut/concurrent/CountdownLatchTest.cpp diff --git a/src/test/cpp/ut/grpc/BUILD.bazel b/src/test/cpp/ut/grpc/BUILD.bazel new file mode 100644 index 000000000..6194131f6 --- /dev/null +++ b/src/test/cpp/ut/grpc/BUILD.bazel @@ -0,0 +1,24 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "grpc_thread_pool_test", + srcs = [ + "GrpcThreadPoolTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_github_grpc_grpc//:grpc++", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "grpc_timer_test", + srcs = [ + "GrpcTimer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_github_grpc_grpc//:grpc++", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/CMakeLists.txt b/src/test/cpp/ut/grpc/CMakeLists.txt new file mode 100644 index 000000000..b1fb84776 --- /dev/null +++ b/src/test/cpp/ut/grpc/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(grpc_thread_pool_test GrpcThreadPoolTest.cpp) +target_link_libraries(grpc_thread_pool_test + PRIVATE + grpc + base_library + gtest_main) +add_test(ut_grpc_thread_pool grpc_thread_pool_test) \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp new file mode 100644 index 000000000..1d5ff7128 --- /dev/null +++ b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp @@ -0,0 +1,34 @@ +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" +#include "src/cpp/server/dynamic_thread_pool.h" +#include "src/cpp/server/thread_pool_interface.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(GrpcThreadPoolTest, testSetUp) { + auto thread_pool = std::unique_ptr(grpc::CreateDefaultThreadPool()); + + absl::Mutex mtx; + absl::CondVar cv; + + bool invoked = false; + + auto callback = [&]() { + absl::MutexLock lk(&mtx); + invoked = true; + cv.SignalAll(); + }; + + thread_pool->Add(callback); + + if (!invoked) { + absl::MutexLock lk(&mtx); + cv.WaitWithTimeout(&mtx, absl::Seconds(3)); + } + + ASSERT_TRUE(invoked); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/GrpcTimer.cpp b/src/test/cpp/ut/grpc/GrpcTimer.cpp new file mode 100644 index 000000000..993d86aae --- /dev/null +++ b/src/test/cpp/ut/grpc/GrpcTimer.cpp @@ -0,0 +1,95 @@ + +#include "absl/time/time.h" +#include "grpc/grpc.h" +#include "rocketmq/RocketMQ.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/timer.h" +#include "gtest/gtest.h" +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +struct Timer { + grpc_timer g_timer; + absl::Mutex mtx; + absl::CondVar cv; + bool invoked{false}; +}; + +struct PeriodicTimer { + grpc_timer g_timer; + absl::Duration initial_delay{absl::Seconds(3)}; + absl::Duration interval{absl::Seconds(1)}; + int times{10}; +}; + +class GrpcTimerTest : public testing::Test { +public: + void SetUp() override { grpc_init(); } + void TearDown() override { grpc_shutdown(); } + + static void schedule(void* arg, grpc_error_handle error) { + auto timer = static_cast(arg); + if (error == GRPC_ERROR_CANCELLED) { + std::cout << absl::FormatTime(absl::Now()) << ": job cancelled" << std::endl; + delete timer; + return; + } + + std::cout << "Thread-" << std::this_thread::get_id() << ": Periodic Callback" << std::endl; + grpc_core::ExecCtx exec_ctx; + std::cout << absl::FormatTime(absl::Now()) << ": Timer->times = " << timer->times << std::endl; + if (!--timer->times) { + delete timer; + std::cout << "Delete periodic timer" << std::endl; + return; + } + + grpc_timer_init(&timer->g_timer, grpc_core::ExecCtx::Get()->Now() + 1000, + GRPC_CLOSURE_CREATE(schedule, timer, grpc_schedule_on_exec_ctx)); + } +}; + +TEST_F(GrpcTimerTest, testSetUp) {} + +TEST_F(GrpcTimerTest, testSingleShot) { + grpc_core::ExecCtx exec_ctx; + Timer timer; + + auto callback = [](void* arg, grpc_error_handle error) { + std::cout << "Callback" << std::endl; + auto t = static_cast(arg); + absl::MutexLock lk(&t->mtx); + t->invoked = true; + t->cv.SignalAll(); + }; + + grpc_timer_init(&timer.g_timer, grpc_core::ExecCtx::Get()->Now() + 500, + GRPC_CLOSURE_CREATE(callback, &timer, grpc_schedule_on_exec_ctx)); + + std::cout << "Waiting callback invocation" << std::endl; + + while (!timer.invoked) { + absl::MutexLock lk(&timer.mtx); + timer.cv.Wait(&timer.mtx); + } + ASSERT_TRUE(timer.invoked); +} + +TEST_F(GrpcTimerTest, testMultiShot) { + grpc_core::ExecCtx exec_ctx; + auto timer = new PeriodicTimer(); + std::cout << absl::FormatTime(absl::Now()) << ": Arm the timer" << std::endl; + std::cout << "Now = " << grpc_core::ExecCtx::Get()->Now() << std::endl; + grpc_timer_init(&timer->g_timer, grpc_core::ExecCtx::Get()->Now() + 3000, + GRPC_CLOSURE_CREATE(schedule, timer, grpc_schedule_on_exec_ctx)); + + absl::SleepFor(absl::Seconds(5)); + grpc_timer_cancel(&timer->g_timer); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 9cdf75f09..1cb7e4760 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -9,19 +9,6 @@ cc_test( "//api:rocketmq_interface", "@com_google_googletest//:gtest_main", ], - linkstatic = True, -) - -cc_test( - name = "message_client_id_setter_test", - srcs = [ - "MessageClientIDSetterTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], - linkstatic = True, ) cc_test( @@ -33,7 +20,6 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_library( @@ -61,7 +47,6 @@ cc_test( "@com_github_gabime_spdlog//:spdlog", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -73,7 +58,6 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_library( @@ -99,7 +83,6 @@ cc_test( "//src/main/cpp/admin:admin_server_library", "//src/main/cpp/rocketmq:rocketmq_library", ], - linkstatic = True, ) cc_test( @@ -112,7 +95,6 @@ cc_test( "//src/main/cpp/admin:admin_server_library", "//src/main/cpp/rocketmq:rocketmq_library", ], - linkstatic = True, ) cc_test( @@ -124,7 +106,6 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) cc_test( @@ -136,5 +117,4 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt index d4498a094..709bb8493 100644 --- a/src/test/cpp/ut/rocketmq/CMakeLists.txt +++ b/src/test/cpp/ut/rocketmq/CMakeLists.txt @@ -22,13 +22,6 @@ target_link_libraries(semaphore_test spdlog::spdlog) add_test(ut_semaphore_test semaphore_test) -add_executable(message_client_id_setter_test MessageClientIDSetterTest.cpp) -target_link_libraries(message_client_id_setter_test - PRIVATE - gtest_main - rocketmq_library) -add_test(ut_message_client_id_setter_test message_client_id_setter_test) - add_executable(default_mq_producer_test DefaultMQProducerTest.cpp) target_link_libraries(default_mq_producer_test PRIVATE diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 08de01fd2..6991176c5 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -10,33 +10,36 @@ ROCKETMQ_NAMESPACE_BEGIN class DefaultMQProducerUnitTest : public MQClientTest { public: - DefaultMQProducerUnitTest() : MQClientTest() { + DefaultMQProducerUnitTest() : MQClientTest() {} + + void SetUp() override { + MQClientTest::SetUp(); // More set-up rpc_client_broker_ = std::make_shared>(); - ON_CALL(*rpc_client_broker_, needHeartbeat).WillByDefault(testing::Return(true)); + ON_CALL(*rpc_client_broker_, needHeartbeat()).WillByDefault(testing::Return(true)); ON_CALL(*rpc_client_broker_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); ON_CALL(*rpc_client_broker_, asyncHealthCheck) .WillByDefault(testing::Invoke( [this](const HealthCheckRequest& request, InvocationContext* invocation_context) { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); invocation_context->onCompletion(true); })); ON_CALL(*rpc_client_broker_, asyncHeartbeat(testing::_, testing::_)) .WillByDefault(testing::Invoke( [this](const HeartbeatRequest& request, InvocationContext* invocation_context) { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); invocation_context->onCompletion(true); })); ON_CALL(*rpc_client_broker_, asyncSend) .WillByDefault(testing::Invoke( [this](const SendMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response_.set_message_id(message_id_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.set_message_id(message_id_); invocation_context->onCompletion(true); })); @@ -47,6 +50,11 @@ class DefaultMQProducerUnitTest : public MQClientTest { } } + void TearDown() override { + rpc_client_broker_.reset(); + MQClientTest::TearDown(); + } + protected: absl::flat_hash_map metadata_; std::shared_ptr> rpc_client_broker_; @@ -59,14 +67,13 @@ TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { absl::CondVar cv; bool completed = false; auto invocation_context = new InvocationContext(); - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& context, - const QueryRouteResponse& response) { - ASSERT_TRUE(status.ok()); + auto callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->status.ok()); absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); }; - invocation_context->callback_ = callback; + invocation_context->callback = callback; rpc_client_ns_->asyncQueryRoute(request, invocation_context); while (!completed) { absl::MutexLock lk(&mtx); @@ -76,14 +83,13 @@ TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { completed = false; SendMessageRequest send_message_request; auto send_message_invocation_context = new InvocationContext(); - auto send_callback = [&](const grpc::Status& status, const grpc::ClientContext& context, - const SendMessageResponse& response) { - ASSERT_TRUE(response.message_id() == message_id_); + auto send_callback = [&](const InvocationContext* invocation_context) { + ASSERT_TRUE(invocation_context->response.message_id() == message_id_); absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); }; - send_message_invocation_context->callback_ = send_callback; + send_message_invocation_context->callback = send_callback; rpc_client_broker_->asyncSend(send_message_request, send_message_invocation_context); @@ -130,11 +136,12 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { std::string msg_id; auto send_callback = new UnitTestSendCallback(mtx, cv, msg_id, completed); producer->send(message, send_callback); - while (!completed) { + if (!completed) { absl::MutexLock lk(&mtx); - cv.Wait(&mtx); + cv.WaitWithTimeout(&mtx, absl::Seconds(10)); } ASSERT_EQ(msg_id, message_id_); + producer->shutdown(); } TEST_F(DefaultMQProducerUnitTest, testSendMessage) { @@ -145,6 +152,20 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { MQMessage message; SendResult send_result = producer->send(message); ASSERT_EQ(send_result.getMsgId(), message_id_); + producer->shutdown(); +} + +TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { + auto producer = std::make_shared(group_name_); + producer->arn(arn_); + producer->setNameServerList(name_server_list_); + producer->start(); + + const char* isolated_endpoint = "ipv4:10.0.0.0:10911"; + producer->isolateEndpoint(isolated_endpoint); + absl::SleepFor(absl::Seconds(10)); + ASSERT_FALSE(producer->isEndpointIsolated(isolated_endpoint)); + producer->shutdown(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 6d05c8c2e..16d4172fe 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -2,6 +2,7 @@ #include "ClientManager.h" #include "DefaultMQPushConsumerImpl.h" #include "MQClientTest.h" +#include "absl/time/time.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/MQMessageListener.h" #include "spdlog/spdlog.h" @@ -19,10 +20,15 @@ std::atomic_int message_index(0); class DefaultMQPushConsumerUnitTest : public MQClientTest { public: - DefaultMQPushConsumerUnitTest() { + DefaultMQPushConsumerUnitTest() : MQClientTest() {} + + void SetUp() override { + MQClientTest::SetUp(); rpc_client_for_broker_ = std::make_shared>(); - ON_CALL(*rpc_client_for_broker_, needHeartbeat).WillByDefault(testing::Return(true)); + testing::Mock::AllowLeak(&rpc_client_for_broker_); + + ON_CALL(*rpc_client_for_broker_, needHeartbeat()).WillByDefault(testing::Return(true)); ON_CALL(*rpc_client_for_broker_, ok()).WillByDefault(Invoke([this]() { std::cout << "Check ok()" << std::endl; @@ -45,6 +51,10 @@ class DefaultMQPushConsumerUnitTest : public MQClientTest { .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockQueryAssignment, this, std::placeholders::_1, std::placeholders::_2))); + ON_CALL(*rpc_client_for_broker_, asyncReceive) + .WillByDefault(testing::Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockReceiveMessage, this, + std::placeholders::_1, std::placeholders::_2))); + const char* address_format = "ipv4:10.0.0.{}:10911"; for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { std::string&& address = fmt::format(address_format, i); @@ -52,23 +62,28 @@ class DefaultMQPushConsumerUnitTest : public MQClientTest { } } + void TearDown() override { + rpc_client_for_broker_.reset(); + MQClientTest::TearDown(); + } + void mockHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) const { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); invocation_context->onCompletion(true); } void mockQueryOffset(const QueryOffsetRequest& request, InvocationContext* invocation_context) const { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response_.set_offset(max_offset_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.set_offset(max_offset_); invocation_context->onCompletion(true); } void mockAsyncPull(const PullMessageRequest& request, InvocationContext* invocation_context) const { - invocation_context->response_.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response_.set_next_offset(max_offset_); + invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); + invocation_context->response.set_next_offset(max_offset_); for (int i = 0; i < pull_batch_size_; i++) { auto message = new rmq::Message; message->mutable_topic()->set_arn(arn_); @@ -80,7 +95,7 @@ class DefaultMQPushConsumerUnitTest : public MQClientTest { std::string checksum; MixAll::md5(body_, checksum); message->mutable_system_attribute()->mutable_body_digest()->set_checksum(checksum); - invocation_context->response_.mutable_messages()->AddAllocated(message); + invocation_context->response.mutable_messages()->AddAllocated(message); } invocation_context->onCompletion(true); } @@ -105,8 +120,31 @@ class DefaultMQPushConsumerUnitTest : public MQClientTest { address->set_host(fmt::format("10.0.0.{}", i / 8)); address->set_port(10911); addresses->AddAllocated(address); - invocation_context->response_.mutable_assignments()->AddAllocated(load_assignment); + invocation_context->response.mutable_assignments()->AddAllocated(load_assignment); + } + invocation_context->onCompletion(true); + } + + void mockReceiveMessage(const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + + // Set delivery timestamp + auto since_unix_epoch = absl::Now() - absl::UnixEpoch(); + auto seconds = absl::ToInt64Seconds(since_unix_epoch); + invocation_context->response.mutable_delivery_timestamp()->set_seconds(seconds); + invocation_context->response.mutable_delivery_timestamp()->set_nanos( + absl::ToInt64Nanoseconds(since_unix_epoch - absl::Seconds(seconds))); + + // Set invisible duration + invocation_context->response.mutable_invisible_duration()->set_seconds(300); + invocation_context->response.mutable_invisible_duration()->set_nanos(0); + + for (int32_t i = 0; i < request.batch_size(); i++) { + auto message = new rmq::Message; + message->set_body("Sample body"); } + invocation_context->onCompletion(true); } @@ -152,6 +190,27 @@ TEST_F(DefaultMQPushConsumerUnitTest, testBroadcasting) { auto listener = new MessageListenerUnitTests; push_consumer->registerMessageListener(listener); push_consumer->start(); + { + std::unique_lock lk(completion_mtx_); + completion_cv_.wait(lk, [&]() { return completed_.load(); }); + } + push_consumer->shutdown(); + absl::SleepFor(absl::Seconds(10)); + delete listener; +} + +TEST_F(DefaultMQPushConsumerUnitTest, DISABLED_testClustering) { + spdlog::set_level(spdlog::level::debug); + auto push_consumer = std::make_shared(group_name_); + push_consumer->setMessageModel(MessageModel::CLUSTERING); + push_consumer->arn(arn_); + push_consumer->setNameServerList(name_server_list_); + push_consumer->subscribe(topic_, "*"); + + auto listener = new MessageListenerUnitTests(); + push_consumer->registerMessageListener(listener); + push_consumer->start(); + { std::unique_lock lk(completion_mtx_); completion_cv_.wait(lk, [&]() { return completed_.load(); }); diff --git a/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp b/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp deleted file mode 100644 index c0e27f6a4..000000000 --- a/src/test/cpp/ut/rocketmq/MessageClientIDSetterTest.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "MessageClientIDSetter.h" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(MessageClientIDSetterTest, testMessageClientIDSetter) { - std::set msg_id_set; - int64_t total = 500000; - int64_t count = 0; - - while (total--) { - std::string msgId = MessageClientIDSetter::createUniqId(); - if (0 == total % 10000) { - SPDLOG_INFO("MsgId: {}", msgId); - } - msg_id_set.insert(msgId); - ++count; - } - EXPECT_EQ(count, msg_id_set.size()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 75ca49c4e..0526a9ca5 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -5,13 +5,4 @@ ROCKETMQ_NAMESPACE_BEGIN class ProcessQueueTest : public testing::Test {}; -TEST_F(ProcessQueueTest, testRequestId) { - std::weak_ptr owner; - ProcessQueue process_queue(MQMessageQueue(), FilterExpression("*"), ConsumeMessageType::POP, 1, owner, nullptr); - std::string&& request_id = process_queue.requestId(); - std::vector bin; - EXPECT_TRUE(MixAll::hexToBinary(request_id, bin)); - EXPECT_EQ(request_id, MixAll::hex(bin.data(), bin.size())); -} - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 3df2b8619..f9bba3d5b 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -1,27 +1,26 @@ #pragma once -#include "RpcClientMock.h" #include "ClientManager.h" +#include "RpcClientMock.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include +#include "grpc/grpc.h" ROCKETMQ_NAMESPACE_BEGIN class MQClientTest : public testing::Test { public: + MQClientTest() = default; - MQClientTest() { + void SetUp() override { name_server_list_.emplace_back(name_server_address_); client_instance_ = std::make_shared(arn_); rpc_client_ns_ = std::make_shared>(); - - ON_CALL(*rpc_client_ns_, needHeartbeat).WillByDefault(testing::Return(false)); - + ON_CALL(*rpc_client_ns_, needHeartbeat()).WillByDefault(testing::Return(false)); ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); - ON_CALL(*rpc_client_ns_, asyncQueryRoute(testing::_, testing::_)) .WillByDefault(testing::Invoke( std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); @@ -29,6 +28,12 @@ class MQClientTest : public testing::Test { ClientManager::getInstance().addClientInstance(arn_, client_instance_); } + void TearDown() override { + rpc_client_ns_.reset(); + client_instance_->cleanRpcClients(); + client_instance_.reset(); + } + protected: grpc::internal::GrpcLibraryInitializer initializer_; std::shared_ptr client_instance_; @@ -47,8 +52,8 @@ class MQClientTest : public testing::Test { private: void mockQueryRoute(const QueryRouteRequest& request, InvocationContext* invocation_context) const { - invocation_context->response_.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); - auto partitions = invocation_context->response_.mutable_partitions(); + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + auto partitions = invocation_context->response.mutable_partitions(); for (int i = 0; i < partition_num_; i++) { auto partition = new rmq::Partition(); partition->set_id(i % avg_partition_per_host_); diff --git a/src/test/cpp/ut/scheduler/BUILD.bazel b/src/test/cpp/ut/scheduler/BUILD.bazel new file mode 100644 index 000000000..f9e32e05d --- /dev/null +++ b/src/test/cpp/ut/scheduler/BUILD.bazel @@ -0,0 +1,15 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") +cc_test( + name = "scheduler_test", + srcs = [ + "SchedulerTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/base:base_library", + "//src/main/cpp/concurrent:countdown_latch_library", + "//src/main/cpp/scheduler:scheduler_library", + "@com_google_googletest//:gtest_main", + ], + linkstatic = True, +) \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/CMakeLists.txt b/src/test/cpp/ut/scheduler/CMakeLists.txt new file mode 100644 index 000000000..34d6064da --- /dev/null +++ b/src/test/cpp/ut/scheduler/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(scheduler_test SchedulerTest.cpp) +target_link_libraries(scheduler_test + PRIVATE + absl::synchronization + base_library + gtest_main + scheduler_library) +add_test(ut_scheduler_test scheduler_test) \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp new file mode 100644 index 000000000..0c14df83c --- /dev/null +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +#include "Scheduler.h" +#include "grpc/grpc.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class SchedulerTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + callback_fire_count = 0; + + callback = [this]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + } + + void TearDown() override { grpc_shutdown(); } + +protected: + Scheduler scheduler; + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + + std::function callback; +}; + +TEST_F(SchedulerTest, testSingleShot) { + scheduler.schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + EXPECT_TRUE(!cv.WaitWithTimeout(&mtx, absl::Seconds(3))); + } +} + +TEST_F(SchedulerTest, testCancel) { + std::uintptr_t task_id = + scheduler.schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); + scheduler.cancel(task_id); + std::this_thread::sleep_for(std::chrono::seconds(2)); + ASSERT_EQ(0, callback_fire_count); +} + +TEST_F(SchedulerTest, testPeriodicShot) { + std::uintptr_t task_id = + scheduler.schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::seconds(1)); + // Wait till callback is executed. + std::this_thread::sleep_for(std::chrono::seconds(5)); + ASSERT_TRUE(callback_fire_count >= 4); + scheduler.cancel(task_id); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/tools/coverage.sh b/tools/coverage.sh index e28402db4..b036b6860 100755 --- a/tools/coverage.sh +++ b/tools/coverage.sh @@ -2,5 +2,16 @@ TOOLS_DIR=$(dirname "$0") WORKSPACE_DIR=$(dirname "$TOOLS_DIR") cd $WORKSPACE_DIR -bazel coverage --config=remote_cache //src/test/cpp/ut/... + +if test "$#" -lt 1; then + echo "Use bazel cache for Development Environment" + bazel coverage --config=remote_cache //src/test/cpp/ut/... +elif test "$1" = "ci"; then + echo "Use bazel cache for CI" + bazel coverage --config=ci_remote_cache //src/test/cpp/ut/... +else + echo "Unknown argument $*. Use bazel cache for Development Environment" + bazel coverage --config=remote_cache //src/test/cpp/ut/... +fi + genhtml bazel-out/_coverage/_coverage_report.dat \ No newline at end of file From 937200cf39faf938a15d0ea55ee1c0ea817d7b68 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 22 Jul 2021 13:43:51 +0800 Subject: [PATCH 017/133] Add send function with message_group argument --- api/rocketmq/DefaultMQProducer.h | 19 ++++++++++++++----- src/main/cpp/rocketmq/CMakeLists.txt | 1 + src/main/cpp/rocketmq/DefaultMQProducer.cpp | 16 ++++++++++------ .../cpp/rocketmq/DefaultMQProducerImpl.cpp | 8 +++++++- .../rocketmq/MessageGroupQueueSelector.cpp | 18 ++++++++++++++++++ .../rocketmq/include/DefaultMQProducerImpl.h | 1 + .../include/MessageGroupQueueSelector.h | 17 +++++++++++++++++ 7 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp create mode 100644 src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 5a17555b9..c2dfca56c 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -33,13 +34,15 @@ class DefaultMQProducer { * Acquire previous send timeout in milliseconds. * @return Send timeout in milliseconds */ - int getSendMsgTimeout() const; + std::chrono::milliseconds getSendMsgTimeout() const; /** * Set default send message timeout in milliseconds. * @param timeout_millis Timeout used when sending messages. */ - void setSendMsgTimeout(int timeout_millis); + void setSendMsgTimeout(std::chrono::milliseconds timeout); + + SendResult send(const MQMessage& message, const std::string& message_group); /** * Send message in synchronous manner. @@ -64,7 +67,7 @@ class DefaultMQProducer { void send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); /** - * send message in Oneway(The implementation is simply ignore the result of send message in synchronous) + * send message in Oneway(The implementation is simply ignore the result of send message in synchronous). * @param message Message to send. * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. */ @@ -84,9 +87,15 @@ class DefaultMQProducer { bool isTracingEnabled(); - int getRetryTimes() const; + /** + * Number of attempts before claiming a send action as failure. By default, 3 attempts will be performed for sync and + * async send methods. + * + * @return + */ + int getMaxAttemptTimes() const; - void setRetryTimes(int times); + void setMaxAttemptTimes(int max_attempt_times); std::vector getTopicMessageQueueInfo(const std::string& topic); diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt index 87645df61..0f9b7d02e 100644 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources(rocketmq_obj DefaultMQPushConsumerImpl.cpp DefaultMQPullConsumer.cpp DefaultMQPullConsumerImpl.cpp + MessageGroupQueueSelector.cpp ProcessQueue.cpp TransactionImpl.cpp) target_include_directories(rocketmq_obj diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index 4f45cb7ee..465eb9060 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -13,12 +13,16 @@ void DefaultMQProducer::start() { impl_->start(); } void DefaultMQProducer::shutdown() { impl_->shutdown(); } -int DefaultMQProducer::getSendMsgTimeout() const { - return static_cast(absl::ToInt64Milliseconds(impl_->getIoTimeout())); +std::chrono::milliseconds DefaultMQProducer::getSendMsgTimeout() const { + return absl::ToChronoMilliseconds(impl_->getIoTimeout()); } -void DefaultMQProducer::setSendMsgTimeout(int timeout_millis) { - impl_->setIoTimeout(absl::Milliseconds(timeout_millis)); +SendResult DefaultMQProducer::send(const MQMessage& message, const std::string& message_group) { + return impl_->send(message, message_group); +} + +void DefaultMQProducer::setSendMsgTimeout(std::chrono::milliseconds timeout) { + impl_->setIoTimeout(absl::FromChrono(timeout)); } void DefaultMQProducer::setNamesrvAddr(const std::string& name_server_address_list) { @@ -77,9 +81,9 @@ void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateChe impl_->setLocalTransactionStateChecker(std::move(checker)); } -void DefaultMQProducer::setRetryTimes(int retry_times) { impl_->maxAttemptTimes(retry_times); } +void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { impl_->maxAttemptTimes(times); } -int DefaultMQProducer::getRetryTimes() const { return impl_->maxAttemptTimes(); } +int DefaultMQProducer::getMaxAttemptTimes() const { return impl_->maxAttemptTimes(); } std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { return impl_->getTopicMessageQueueInfo(topic); diff --git a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp index 0483b23ce..2c34e9970 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp @@ -1,14 +1,15 @@ #include "DefaultMQProducerImpl.h" #include "MessageAccessor.h" +#include "MessageGroupQueueSelector.h" #include "Metadata.h" #include "MixAll.h" -#include "UtilAll.h" #include "Protocol.h" #include "SendCallbacks.h" #include "SendMessageContext.h" #include "Signature.h" #include "TransactionImpl.h" #include "UniqueIdGenerator.h" +#include "UtilAll.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" @@ -125,6 +126,11 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message) { THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); } +SendResult DefaultMQProducerImpl::send(const MQMessage& message, const std::string& message_group) { + MessageGroupQueueSelector selector(message_group); + return send(message, &selector, nullptr); +} + SendResult DefaultMQProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue) { ensureRunning(); std::vector message_queue_list{message_queue}; diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp new file mode 100644 index 000000000..36a2b3817 --- /dev/null +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -0,0 +1,18 @@ +#include "MessageGroupQueueSelector.h" + +#include + +ROCKETMQ_NAMESPACE_BEGIN + +MessageGroupQueueSelector::MessageGroupQueueSelector(std::string message_group) + : message_group_(std::move(message_group)) {} + +MQMessageQueue MessageGroupQueueSelector::select(const std::vector& mqs, const MQMessage& msg, + void* arg) { + std::size_t hash_code = std::hash{}(message_group_); + assert(!mqs.empty()); + std::size_t len = mqs.size(); + return mqs[hash_code % len]; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h index 6b1d4d463..47dbd4d27 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h @@ -33,6 +33,7 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void shutdown() override; SendResult send(const MQMessage& message); + SendResult send(const MQMessage& message, const std::string& message_group); SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); diff --git a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h new file mode 100644 index 000000000..1553cc978 --- /dev/null +++ b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h @@ -0,0 +1,17 @@ +#pragma once + +#include "rocketmq/MQSelector.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageGroupQueueSelector : public MessageQueueSelector { +public: + explicit MessageGroupQueueSelector(std::string message_group); + + MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) override; + +private: + std::string message_group_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 597d312a49fbfeaf4e1dceacae4589d96874e9f9 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 22 Jul 2021 21:00:46 +0800 Subject: [PATCH 018/133] Add STS credentials provider --- .bazelrc | 4 - api/rocketmq/Credentials.h | 5 + api/rocketmq/CredentialsProvider.h | 28 +++-- src/main/cpp/base/BUILD.bazel | 38 +++++-- src/main/cpp/base/GHttpClient.cpp | 13 ++- src/main/cpp/base/include/GHttpClient.h | 21 ++-- src/main/cpp/base/include/HttpClient.h | 27 +++++ .../cpp/base/mocks/include/HttpClientMock.h | 19 ++++ src/main/cpp/rocketmq/CredentialsProvider.cpp | 103 ++++++++++++++++-- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 2 +- .../rocketmq/MessageGroupQueueSelector.cpp | 1 + .../include/StsCredentialsProviderImpl.h | 49 +++++++++ src/test/cpp/ut/admin/BUILD.bazel | 1 - src/test/cpp/ut/base/GHttpClientTest.cpp | 24 ++++ src/test/cpp/ut/logger/BUILD.bazel | 1 - src/test/cpp/ut/rocketmq/BUILD.bazel | 23 ++++ .../MessageGroupQueueSelectorTest.cpp | 25 +++++ .../StsCredentialsProviderImplTest.cpp | 61 +++++++++++ src/test/cpp/ut/scheduler/BUILD.bazel | 1 - 19 files changed, 394 insertions(+), 52 deletions(-) create mode 100644 src/main/cpp/base/include/HttpClient.h create mode 100644 src/main/cpp/base/mocks/include/HttpClientMock.h create mode 100644 src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h create mode 100644 src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp diff --git a/.bazelrc b/.bazelrc index 23c73b746..528f4c1f5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -115,10 +115,6 @@ build:ubsan --linkopt -lubsan build:remote_cache --remote_cache=http://11.122.50.85:9090 test:remote_cache --remote_cache=http://11.122.50.85:9090 -# Remote cache for CI. --config=ci_remote_cache -build:ci_remote_cache --remote_cache=http://47.253.54.230:9090 -test:ci_remote_cache --remote_cache=http://47.253.54.230:9090 - # Coverage options coverage --config=coverage # 1.5x original timeout + 300s for trace merger in all categories diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index 35c9229a0..a72bdb677 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -15,6 +15,11 @@ class Credentials { : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), expiration_instant_(std::chrono::system_clock::time_point::max()) {} + Credentials(std::string access_key, std::string access_secret, std::string token, + std::chrono::system_clock::time_point expiration) + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), session_token_(std::move(token)), + expiration_instant_(expiration) {} + bool operator==(const Credentials& rhs) const { return access_key_ == rhs.access_key_ && access_secret_ == rhs.access_secret_ && session_token_ == rhs.session_token_ && expiration_instant_ == rhs.expiration_instant_; diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index ddce476f1..69d48b010 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -1,10 +1,12 @@ #pragma once +#include "Credentials.h" + +#include +#include #include #include -#include "Credentials.h" - ROCKETMQ_NAMESPACE_BEGIN class CredentialsProvider { @@ -12,8 +14,6 @@ class CredentialsProvider { virtual ~CredentialsProvider() = default; virtual Credentials getCredentials() = 0; - - virtual std::chrono::system_clock::duration refreshInterval() = 0; }; using CredentialsProviderPtr = std::shared_ptr; @@ -26,8 +26,6 @@ class StaticCredentialsProvider : public CredentialsProvider { Credentials getCredentials() override; - std::chrono::system_clock::duration refreshInterval() override; - private: std::string access_key_; std::string access_secret_; @@ -44,8 +42,6 @@ class EnvironmentVariablesCredentialsProvider : public CredentialsProvider { Credentials getCredentials() override; - std::chrono::system_clock::duration refreshInterval() override { return std::chrono::system_clock::duration::zero(); } - private: std::string access_key_; std::string access_secret_; @@ -62,9 +58,9 @@ class ConfigFileCredentialsProvider : public CredentialsProvider { public: ConfigFileCredentialsProvider(); - Credentials getCredentials() override; + ConfigFileCredentialsProvider(std::string config_file, std::chrono::milliseconds refresh_interval); - std::chrono::system_clock::duration refreshInterval() override; + Credentials getCredentials() override; /** * For test purpose only. @@ -82,4 +78,16 @@ class ConfigFileCredentialsProvider : public CredentialsProvider { static const char* ACCESS_SECRET_FIELD_NAME; }; +class StsCredentialsProviderImpl; + +class StsCredentialsProvider : public CredentialsProvider { +public: + explicit StsCredentialsProvider(std::string ram_role_name); + + Credentials getCredentials() override; + +private: + std::unique_ptr impl_; +}; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 1c5cecfcd..9cb7198ca 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -1,5 +1,33 @@ load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "http_client_interface", + hdrs = [ + "include/HttpClient.h", + ], + strip_include_prefix = "//src/main/cpp/base/include", + deps = [ + "//api:rocketmq_interface", + "@com_google_absl//absl/container:flat_hash_map", + ], +) + + +cc_library( + name = "http_client_mocks", + hdrs = [ + "mocks/include/HttpClientMock.h", + ], + strip_include_prefix = "//src/main/cpp/base/mocks/include", + deps = [ + ":http_client_interface", + "//external:gtest", + ], +) + + cc_library( name = "ghttp_client", hdrs = [ @@ -9,14 +37,12 @@ cc_library( "GHttpClient.cpp", ], deps = [ + ":http_client_interface", "//src/main/cpp/log:log_library", "@com_github_fmtlib_fmt//:fmtlib", "@com_github_grpc_grpc//:grpc", ], strip_include_prefix = "//src/main/cpp/base/include", - visibility = [ - "//visibility:public", - ], ) cc_library( @@ -33,9 +59,6 @@ cc_library( deps = [ ":ghttp_client", ], - visibility = [ - "//visibility:public", - ], ) cc_library( @@ -55,7 +78,4 @@ cc_library( "@boringssl//:crypto", "@boringssl//:ssl", ], - visibility = [ - "//visibility:public", - ], ) \ No newline at end of file diff --git a/src/main/cpp/base/GHttpClient.cpp b/src/main/cpp/base/GHttpClient.cpp index c8335348f..e4e1c871c 100644 --- a/src/main/cpp/base/GHttpClient.cpp +++ b/src/main/cpp/base/GHttpClient.cpp @@ -24,6 +24,7 @@ GHttpClient::GHttpClient() : shutdown_(false) { GHttpClient::~GHttpClient() { SPDLOG_INFO("GHttpClient::~GHttpClient() starts"); + shutdown(); grpc_core::ExecCtx exec_ctx; grpc_httpcli_context_destroy(&http_context_); grpc_pollset_shutdown(grpc_polling_entity_pollset(&http_polling_entity_), &destroy_); @@ -60,11 +61,13 @@ void GHttpClient::start() { } void GHttpClient::shutdown() { - SPDLOG_INFO("GHttpClient::shutdown()"); - shutdown_.store(true, std::memory_order_relaxed); - if (loop_.joinable()) { - loop_.join(); - SPDLOG_INFO("GHttpClient#loop thread quit OK"); + bool expected = false; + if (shutdown_.compare_exchange_strong(expected, true, std::memory_order_relaxed)) { + SPDLOG_INFO("GHttpClient::shutdown()"); + if (loop_.joinable()) { + loop_.join(); + SPDLOG_INFO("GHttpClient#loop thread quit OK"); + } } } diff --git a/src/main/cpp/base/include/GHttpClient.h b/src/main/cpp/base/include/GHttpClient.h index 53dc7c2d7..0cfcbc10a 100644 --- a/src/main/cpp/base/include/GHttpClient.h +++ b/src/main/cpp/base/include/GHttpClient.h @@ -1,7 +1,7 @@ #include "rocketmq/RocketMQ.h" +#include "HttpClient.h" #include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "src/core/lib/http/httpcli.h" #include "src/core/lib/iomgr/iomgr.h" @@ -15,11 +15,6 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class HttpProtocol : int8_t { - HTTP = 1, - HTTPS = 2, -}; - struct HttpInvocationContext { HttpInvocationContext() { memset(&request, 0, sizeof(request)); } std::string host; @@ -29,19 +24,19 @@ struct HttpInvocationContext { std::function&, const std::string&)> callback; }; -class GHttpClient { +class GHttpClient : public HttpClient { public: GHttpClient(); - ~GHttpClient(); + ~GHttpClient() override; - void start(); + void start() override; - void shutdown(); + void shutdown() override; - void - get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb); + void get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) + override; static const int STATUS_OK; diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h new file mode 100644 index 000000000..84b35e747 --- /dev/null +++ b/src/main/cpp/base/include/HttpClient.h @@ -0,0 +1,27 @@ +#pragma once +#include "absl/container/flat_hash_map.h" +#include "rocketmq/RocketMQ.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class HttpProtocol : int8_t { + HTTP = 1, + HTTPS = 2, +}; + +class HttpClient { +public: + virtual ~HttpClient() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void + get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/mocks/include/HttpClientMock.h b/src/main/cpp/base/mocks/include/HttpClientMock.h new file mode 100644 index 000000000..392756ada --- /dev/null +++ b/src/main/cpp/base/mocks/include/HttpClientMock.h @@ -0,0 +1,19 @@ +#pragma once + +#include "gmock/gmock.h" +#include "HttpClient.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientMock : public HttpClient { +public: + ~HttpClientMock() override = default; + + MOCK_METHOD(void, start, (), (override)); + MOCK_METHOD(void, shutdown, (), (override)); + MOCK_METHOD(void, get, (HttpProtocol, const std::string&, std::uint16_t, const std::string&, + const std::function&, const std::string&)>&), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index 29e70e726..83b2d6a02 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -1,14 +1,18 @@ -#include "rocketmq/CredentialsProvider.h" #include "MixAll.h" +#include "StsCredentialsProviderImpl.h" +#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "ghc/filesystem.hpp" #include "google/protobuf/struct.pb.h" #include "google/protobuf/util/json_util.h" #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" +#include "GHttpClient.h" #include #include #include +#include +#include "fmt/format.h" ROCKETMQ_NAMESPACE_BEGIN @@ -17,10 +21,6 @@ StaticCredentialsProvider::StaticCredentialsProvider(std::string access_key, std Credentials StaticCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } -std::chrono::system_clock::duration StaticCredentialsProvider::refreshInterval() { - return std::chrono::system_clock::duration::zero(); -} - const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY = "ROCKETMQ_ACCESS_KEY"; const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET = "ROCKETMQ_ACCESS_SECRET"; @@ -88,10 +88,99 @@ ConfigFileCredentialsProvider::ConfigFileCredentialsProvider() { } } +ConfigFileCredentialsProvider::ConfigFileCredentialsProvider(std::string config_file, + std::chrono::milliseconds refresh_interval) {} + Credentials ConfigFileCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } -std::chrono::system_clock::duration ConfigFileCredentialsProvider::refreshInterval() { - return std::chrono::seconds(10); +StsCredentialsProvider::StsCredentialsProvider(std::string ram_role_name) + : impl_(absl::make_unique(std::move(ram_role_name))) {} + +Credentials StsCredentialsProvider::getCredentials() { return impl_->getCredentials(); } + +StsCredentialsProviderImpl::StsCredentialsProviderImpl(std::string ram_role_name) + : ram_role_name_(std::move(ram_role_name)), http_client_(absl::make_unique()) { + http_client_->start(); +} + +StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { + http_client_->shutdown(); +} + +Credentials StsCredentialsProviderImpl::getCredentials() { + if (std::chrono::system_clock::now() >= expiration_) { + refresh(); + } + + { + absl::MutexLock lk(&mtx_); + return Credentials(access_key_, access_secret_, security_token_, expiration_); + } +} + +void StsCredentialsProviderImpl::refresh() { + std::string path = fmt::format("{}{}", RAM_ROLE_URL_PREFIX, ram_role_name_); + absl::Mutex sync_mtx; + absl::CondVar sync_cv; + bool completed = false; + auto callback = [&, this](int code, const absl::flat_hash_map& headers, + const std::string body) { + SPDLOG_DEBUG("Received STS reponse. Code: {}", code); + if (GHttpClient::STATUS_OK == code) { + google::protobuf::Struct doc; + google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(body, &doc); + if (status.ok()) { + const auto& fields = doc.fields(); + assert(fields.contains(FIELD_ACCESS_KEY)); + std::string access_key = fields.at(FIELD_ACCESS_KEY).string_value(); + assert(fields.contains(FIELD_ACCESS_SECRET)); + std::string access_secret = fields.at(FIELD_ACCESS_SECRET).string_value(); + assert(fields.contains(FIELD_SECURITY_TOKEN)); + std::string security_token = fields.at(FIELD_SECURITY_TOKEN).string_value(); + assert(fields.contains(FIELD_EXPIRATION)); + std::string expiration_string = fields.at(FIELD_EXPIRATION).string_value(); + absl::Time expiration_instant; + std::string parse_error; + if (absl::ParseTime(EXPIRATION_DATE_TIME_FORMAT, expiration_string, absl::UTCTimeZone(), &expiration_instant, + &parse_error)) { + absl::MutexLock lk(&mtx_); + access_key_ = std::move(access_key); + access_secret_ = std::move(access_secret); + security_token_ = std::move(security_token); + expiration_ = absl::ToChronoTime(expiration_instant); + } else { + SPDLOG_WARN("Failed to parse expiration time. Message: {}", parse_error); + } + + } else { + SPDLOG_WARN("Failed to parse STS response. Message: {}", status.message().as_string()); + } + } else { + SPDLOG_WARN("STS response code is not OK. Code: {}", code); + } + + { + absl::MutexLock lk(&sync_mtx); + completed = true; + sync_cv.Signal(); + } + }; + + http_client_->get(HttpProtocol::HTTP, RAM_ROLE_HOST, 80, path, callback); + + while (!completed) { + absl::MutexLock lk(&sync_mtx); + sync_cv.Wait(&sync_mtx); + } } +const char* StsCredentialsProviderImpl::RAM_ROLE_HOST = "100.100.100.200"; +const char* StsCredentialsProviderImpl::RAM_ROLE_URL_PREFIX = "/latest/meta-data/Ram/security-credentials/"; +const char* StsCredentialsProviderImpl::FIELD_ACCESS_KEY = "AccessKeyId"; +const char* StsCredentialsProviderImpl::FIELD_ACCESS_SECRET = "AccessKeySecret"; +const char* StsCredentialsProviderImpl::FIELD_SECURITY_TOKEN = "SecurityToken"; +const char* StsCredentialsProviderImpl::FIELD_EXPIRATION = "Expiration"; +const char* StsCredentialsProviderImpl::EXPIRATION_DATE_TIME_FORMAT = "%Y-%m-%d%ET%H:%H:%S%Ez"; + + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index 465eb9060..afee3ffea 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -81,7 +81,7 @@ void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateChe impl_->setLocalTransactionStateChecker(std::move(checker)); } -void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { impl_->maxAttemptTimes(times); } +void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { impl_->maxAttemptTimes(max_attempt_times); } int DefaultMQProducer::getMaxAttemptTimes() const { return impl_->maxAttemptTimes(); } diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp index 36a2b3817..f7e1c2223 100644 --- a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -1,6 +1,7 @@ #include "MessageGroupQueueSelector.h" #include +#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h new file mode 100644 index 000000000..0567e9dd2 --- /dev/null +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -0,0 +1,49 @@ +#pragma once + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/CredentialsProvider.h" +#include "HttpClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StsCredentialsProviderImpl : public CredentialsProvider { +public: + explicit StsCredentialsProviderImpl(std::string ram_role_name); + + ~StsCredentialsProviderImpl() override; + + Credentials getCredentials() override; + + /** + * @brief Expose http_client for test purpose only. + * + * @param http_client + */ + void setHttpClient(std::unique_ptr http_client) { + http_client_ = std::move(http_client); + } + +private: + static const char* RAM_ROLE_HOST; + static const char* RAM_ROLE_URL_PREFIX; + static const char* FIELD_ACCESS_KEY; + static const char* FIELD_ACCESS_SECRET; + static const char* FIELD_SECURITY_TOKEN; + static const char* FIELD_EXPIRATION; + static const char* EXPIRATION_DATE_TIME_FORMAT; + + std::string ram_role_name_; + + std::string access_key_ GUARDED_BY(mtx_); + std::string access_secret_ GUARDED_BY(mtx_); + std::string security_token_ GUARDED_BY(mtx_); + std::chrono::system_clock::time_point expiration_; + + absl::Mutex mtx_; + void refresh() LOCKS_EXCLUDED(mtx_); + + std::unique_ptr http_client_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/admin/BUILD.bazel b/src/test/cpp/ut/admin/BUILD.bazel index 506e27e34..8a2cc7f86 100644 --- a/src/test/cpp/ut/admin/BUILD.bazel +++ b/src/test/cpp/ut/admin/BUILD.bazel @@ -9,5 +9,4 @@ cc_test( "//src/main/cpp/admin:admin_server_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/GHttpClientTest.cpp b/src/test/cpp/ut/base/GHttpClientTest.cpp index 87407d96b..eebbf26ba 100644 --- a/src/test/cpp/ut/base/GHttpClientTest.cpp +++ b/src/test/cpp/ut/base/GHttpClientTest.cpp @@ -48,4 +48,28 @@ TEST_F(GHttpClientTest, testCtor) { http_client.shutdown(); } +TEST_F(GHttpClientTest, testDestructWithoutShutdown) { + spdlog::set_level(spdlog::level::debug); + GHttpClient http_client; + absl::Mutex mtx; + absl::CondVar cv; + bool completed = false; + auto callback = [&](int status, const absl::flat_hash_map& metadata, + const std::string& body) { + absl::MutexLock lk(&mtx); + completed = true; + SPDLOG_DEBUG("HTTP status-code: {}, response body: {}", status, body); + cv.SignalAll(); + }; + + std::string host("www.aliyun.com"); + http_client.start(); + http_client.get(HttpProtocol::HTTP, host, 8080, "/", callback); + if (!completed) { + absl::MutexLock lk(&mtx); + cv.WaitWithTimeout(&mtx, absl::Seconds(5)); + } + ASSERT_TRUE(completed); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/test/cpp/ut/logger/BUILD.bazel index fe03dc8f1..5fb6ac1d0 100644 --- a/src/test/cpp/ut/logger/BUILD.bazel +++ b/src/test/cpp/ut/logger/BUILD.bazel @@ -13,5 +13,4 @@ cc_test( defines = [ "BAZEL", ], - linkstatic = True, ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 1cb7e4760..073bb7c6b 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -1,5 +1,16 @@ load("@rules_cc//cc:defs.bzl", "cc_test", "cc_library") +cc_test( + name = "message_group_queue_selector_test", + srcs = [ + "MessageGroupQueueSelectorTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + cc_test( name = "executor_test", srcs = [ @@ -117,4 +128,16 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "sts_credentials_provider_test", + srcs = [ + "StsCredentialsProviderImplTest.cpp", + ], + deps = [ + "//src/main/cpp/base:http_client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp new file mode 100644 index 000000000..67fc5ab9d --- /dev/null +++ b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp @@ -0,0 +1,25 @@ +#include "gtest/gtest.h" +#include "MessageGroupQueueSelector.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(MessageGroupQueueSelectorTest, testSelect) { + std::string message_group("sample"); + std::size_t hash_code = std::hash{}(message_group); + MessageGroupQueueSelector selector(message_group); + std::size_t len = 8; + std::vector mqs; + mqs.resize(len); + for (std::size_t i = 0; i < len; i++) { + MQMessageQueue queue("topic", "broker-a", static_cast(i)); + mqs.emplace_back(queue); + } + + MQMessage message; + MQMessageQueue selected = selector.select(mqs, message, nullptr); + EXPECT_EQ(selected, mqs[hash_code % len]); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp new file mode 100644 index 000000000..f9b85fc99 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -0,0 +1,61 @@ +#include "StsCredentialsProviderImpl.h" +#include "HttpClientMock.h" +#include "absl/memory/memory.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include +#include "grpc/grpc.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StsCredentialsProviderImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + sts_credentials_provider = std::make_shared("test"); + auto http_client_ = absl::make_unique>(); + auto http_get_action = + [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& + cb) { + absl::flat_hash_map header; + std::string body = R"( + { + "AccessKeyId": "key", + "AccessKeySecret": "secret", + "SecurityToken": "token", + "Expiration" : "2017-11-01T05:20:01Z", + "LastUpdated" : "2017-10-31T23:20:01Z", + "Code" : "Success" + } + )"; + cb(200, header, body); + }; + + EXPECT_CALL(*http_client_, get).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(http_get_action)); + sts_credentials_provider->setHttpClient(std::move(http_client_)); + } + + void TearDown() override { + grpc_shutdown(); + } + +protected: + std::shared_ptr sts_credentials_provider; +}; + +TEST_F(StsCredentialsProviderImplTest, testGetCredentials) { + auto credentials = sts_credentials_provider->getCredentials(); + EXPECT_EQ(credentials.accessKey(), "key"); + EXPECT_EQ(credentials.accessSecret(), "secret"); + absl::Time time; + std::string input = "2017-11-01 05:20:01"; + std::string format = "%Y-%m-%d %H:%H:%S"; + std::string error; + EXPECT_TRUE(absl::ParseTime(format, input, &time, &error)); + EXPECT_EQ(credentials.expirationInstant(), absl::ToChronoTime(time)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/BUILD.bazel b/src/test/cpp/ut/scheduler/BUILD.bazel index f9e32e05d..d1e0c56ea 100644 --- a/src/test/cpp/ut/scheduler/BUILD.bazel +++ b/src/test/cpp/ut/scheduler/BUILD.bazel @@ -11,5 +11,4 @@ cc_test( "//src/main/cpp/scheduler:scheduler_library", "@com_google_googletest//:gtest_main", ], - linkstatic = True, ) \ No newline at end of file From 6a04948e150626c4ba578a23c99bd76ecb382d45 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 2 Aug 2021 14:05:19 +0800 Subject: [PATCH 019/133] Support FIFO message pub/sub (#5) Support FIFO and class hierarchy re-structure. --- CMakeLists.txt | 2 +- README.md | 115 +++--- api/rocketmq/AsyncCallback.h | 4 +- .../{ConsumerType.h => ConsumeType.h} | 7 - api/rocketmq/CredentialsProvider.h | 5 +- api/rocketmq/DefaultMQPullConsumer.h | 6 +- api/rocketmq/DefaultMQPushConsumer.h | 16 +- api/rocketmq/Executor.h | 10 + api/rocketmq/MQMessageExt.h | 2 +- api/rocketmq/MQMessageListener.h | 35 -- api/rocketmq/MessageListener.h | 33 ++ api/rocketmq/MessageModel.h | 13 + api/rocketmq/SendResult.h | 2 +- docs/assets/BasicMode.png | Bin 0 -> 42779 bytes docs/assets/class_diagram.png | Bin 0 -> 364206 bytes example/rocketmq/BUILD.bazel | 22 ++ example/rocketmq/BenchmarkPushConsumer.cpp | 9 +- example/rocketmq/ExampleAsyncProducer.cpp | 2 +- .../rocketmq/ExampleBroadcastPushConsumer.cpp | 8 +- example/rocketmq/ExamplePushConsumer.cpp | 10 +- .../PushConsumerWithCustomExecutor.cpp | 21 +- example/rocketmq/PushConsumerWithThrottle.cpp | 15 +- example/rocketmq/SqlConsumer.cpp | 9 +- proto/apache/rocketmq/v1/definition.proto | 18 +- proto/apache/rocketmq/v1/service.proto | 27 +- src/main/cpp/base/BUILD.bazel | 12 + src/main/cpp/base/CMakeLists.txt | 8 +- src/main/cpp/base/MQMessage.cpp | 125 +------ src/main/cpp/base/MQMessageExt.cpp | 51 +++ src/main/cpp/base/MessageAccessor.cpp | 70 ++++ src/main/cpp/base/Metadata.cpp | 19 - src/main/cpp/base/MetadataConstants.cpp | 19 + src/main/cpp/base/MixAll.cpp | 14 +- src/main/cpp/base/PermissionHelper.cpp | 15 - src/main/cpp/base/include/InvocationContext.h | 4 +- src/main/cpp/base/include/MessageAccessor.h | 2 +- src/main/cpp/base/include/MessageImpl.h | 18 + .../{Metadata.h => MetadataConstants.h} | 2 +- src/main/cpp/base/include/MixAll.h | 11 +- src/main/cpp/base/include/PermissionHelper.h | 18 - src/main/cpp/base/include/Protocol.h | 2 +- src/main/cpp/base/mocks/CMakeLists.txt | 6 + .../base/mocks/include/MessageListenerMock.h | 18 + src/main/cpp/client/BUILD.bazel | 16 +- src/main/cpp/client/CMakeLists.txt | 7 +- src/main/cpp/client/ClientApi.cpp | 15 - src/main/cpp/client/ClientConfig.cpp | 56 --- src/main/cpp/client/ClientConfigImpl.cpp | 56 +++ src/main/cpp/client/ClientManager.cpp | 40 --- src/main/cpp/client/ClientManagerFactory.cpp | 41 +++ ...ientInstance.cpp => ClientManagerImpl.cpp} | 247 +++++++------ src/main/cpp/client/RpcClientImpl.cpp | 12 +- src/main/cpp/client/Signature.cpp | 29 +- src/main/cpp/client/include/Assignment.h | 11 +- .../include/{ClientCallback.h => Client.h} | 12 +- src/main/cpp/client/include/ClientApi.h | 21 -- src/main/cpp/client/include/ClientConfig.h | 90 +---- .../cpp/client/include/ClientConfigImpl.h | 99 ++++++ src/main/cpp/client/include/ClientManager.h | 95 +++-- .../cpp/client/include/ClientManagerFactory.h | 40 +++ .../{ClientInstance.h => ClientManagerImpl.h} | 150 ++++---- .../cpp/client/include/ConsumeMessageType.h | 14 + src/main/cpp/client/include/Identifiable.h | 20 -- src/main/cpp/client/include/Partition.h | 1 + .../cpp/client/include/ReceiveMessageResult.h | 5 +- src/main/cpp/client/include/RpcClient.h | 7 + src/main/cpp/client/include/RpcClientImpl.h | 4 + .../cpp/client/include/TopicPublishInfo.h | 8 +- src/main/cpp/client/mocks/CMakeLists.txt | 20 +- src/main/cpp/client/mocks/RpcClientMock.cpp | 2 +- .../client/mocks/include/ClientConfigMock.h | 22 ++ .../client/mocks/include/ClientManagerMock.h | 90 +++++ .../cpp/client/mocks/include/ClientMock.h | 22 ++ .../client/mocks/include/IdentifiableMock.h | 15 - .../include/ReceiveMessageCallbackMock.h | 14 + .../cpp/client/mocks/include/RpcClientMock.h | 9 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 145 ++++++++ src/main/cpp/rocketmq/BUILD.bazel | 14 +- src/main/cpp/rocketmq/CMakeLists.txt | 12 +- .../rocketmq/{BaseImpl.cpp => ClientImpl.cpp} | 69 ++-- .../rocketmq/ConsumeFifoMessageService.cpp | 243 +++++++++++++ .../ConsumeMessageConcurrentlyService.cpp | 284 --------------- .../rocketmq/ConsumeMessageOrderlyService.cpp | 38 -- .../cpp/rocketmq/ConsumeMessageService.cpp | 60 +++- .../ConsumeStandardMessageService.cpp | 197 ++++++++++ .../cpp/rocketmq/DefaultMQProducerImpl.cpp | 9 +- .../rocketmq/DefaultMQPullConsumerImpl.cpp | 14 +- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 6 +- .../rocketmq/DefaultMQPushConsumerImpl.cpp | 256 ++++--------- src/main/cpp/rocketmq/FilterExpression.cpp | 26 ++ src/main/cpp/rocketmq/ProcessQueue.cpp | 200 ----------- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 317 +++++++++++++++++ src/main/cpp/rocketmq/SendCallbacks.cpp | 2 +- .../include/AsyncReceiveMessageCallback.h | 37 ++ .../include/{BaseImpl.h => ClientImpl.h} | 19 +- .../rocketmq/include/ConsumeMessageService.h | 119 ++++--- src/main/cpp/rocketmq/include/Consumer.h | 22 ++ .../rocketmq/include/DefaultMQProducerImpl.h | 10 +- .../include/DefaultMQPullConsumerImpl.h | 15 +- .../include/DefaultMQPushConsumerImpl.h | 138 ++++--- .../cpp/rocketmq/include/FilterExpression.h | 10 +- src/main/cpp/rocketmq/include/ProcessQueue.h | 133 ++----- .../cpp/rocketmq/include/ProcessQueueImpl.h | 202 +++++++++++ src/main/cpp/rocketmq/include/PushConsumer.h | 44 +++ src/main/cpp/rocketmq/include/SendCallbacks.h | 12 +- src/main/cpp/rocketmq/mocks/CMakeLists.txt | 8 + .../cpp/rocketmq/mocks/include/ConsumerMock.h | 19 + .../rocketmq/mocks/include/ProcessQueueMock.h | 49 +++ .../rocketmq/mocks/include/PushConsumerMock.h | 33 ++ src/test/cpp/it/BUILD.bazel | 4 +- src/test/cpp/it/ClientInstanceTest.cpp | 4 +- .../DefaultMQPushConsumerIntegrationTest.cpp | 4 +- src/test/cpp/it/RpcClientIntegrationTest.cpp | 2 +- src/test/cpp/it/RpcClientTest.cpp | 11 +- src/test/cpp/it/TopicPublishInfoTest.cpp | 16 +- src/test/cpp/ut/AbseilTimeTest.cpp | 11 - src/test/cpp/ut/AsanDemo.cpp | 7 - src/test/cpp/ut/BUILD | 72 +--- src/test/cpp/ut/CMakeLists.txt | 33 -- src/test/cpp/ut/EventTest.cpp | 37 -- src/test/cpp/ut/GTestExample.cpp | 4 - src/test/cpp/ut/HexTest.cpp | 17 - src/test/cpp/ut/LogTest.cpp | 29 -- src/test/cpp/ut/MacTest.cpp | 84 ----- src/test/cpp/ut/ZlibTest.cpp | 81 ----- src/test/cpp/ut/base/CMakeLists.txt | 8 +- src/test/cpp/ut/base/MixAllTest.cpp | 33 +- src/test/cpp/ut/client/BUILD.bazel | 24 +- src/test/cpp/ut/client/CMakeLists.txt | 10 +- src/test/cpp/ut/client/ClientManagerTest.cpp | 73 ++++ src/test/cpp/ut/client/ClientTest.cpp | 27 ++ src/test/cpp/ut/rocketmq/BUILD.bazel | 66 ++-- src/test/cpp/ut/rocketmq/CMakeLists.txt | 61 ++-- .../ConsumeStandardMessageServiceTest.cpp | 88 +++++ src/test/cpp/ut/rocketmq/ConsumerTest.cpp | 11 + .../ut/rocketmq/CredentialsProviderTest.cpp | 14 + .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 17 +- .../cpp/ut/rocketmq/FilterExpressionTest.cpp | 28 ++ src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 336 +++++++++++++++++- src/test/cpp/ut/rocketmq/Semaphore.cpp | 25 -- src/test/cpp/ut/rocketmq/SemaphoreTest.cpp | 27 -- .../cpp/ut/rocketmq/include/MQClientTest.h | 10 +- src/test/cpp/ut/rocketmq/include/Semaphore.h | 20 -- 143 files changed, 3717 insertions(+), 2434 deletions(-) rename api/rocketmq/{ConsumerType.h => ConsumeType.h} (88%) create mode 100644 api/rocketmq/Executor.h delete mode 100644 api/rocketmq/MQMessageListener.h create mode 100644 api/rocketmq/MessageListener.h create mode 100644 api/rocketmq/MessageModel.h create mode 100644 docs/assets/BasicMode.png create mode 100644 docs/assets/class_diagram.png create mode 100644 src/main/cpp/base/MQMessageExt.cpp create mode 100644 src/main/cpp/base/MessageAccessor.cpp delete mode 100644 src/main/cpp/base/Metadata.cpp create mode 100644 src/main/cpp/base/MetadataConstants.cpp delete mode 100644 src/main/cpp/base/PermissionHelper.cpp create mode 100644 src/main/cpp/base/include/MessageImpl.h rename src/main/cpp/base/include/{Metadata.h => MetadataConstants.h} (95%) delete mode 100644 src/main/cpp/base/include/PermissionHelper.h create mode 100644 src/main/cpp/base/mocks/CMakeLists.txt create mode 100644 src/main/cpp/base/mocks/include/MessageListenerMock.h delete mode 100644 src/main/cpp/client/ClientApi.cpp delete mode 100644 src/main/cpp/client/ClientConfig.cpp create mode 100644 src/main/cpp/client/ClientConfigImpl.cpp delete mode 100644 src/main/cpp/client/ClientManager.cpp create mode 100644 src/main/cpp/client/ClientManagerFactory.cpp rename src/main/cpp/client/{ClientInstance.cpp => ClientManagerImpl.cpp} (83%) rename src/main/cpp/client/include/{ClientCallback.h => Client.h} (64%) delete mode 100644 src/main/cpp/client/include/ClientApi.h create mode 100644 src/main/cpp/client/include/ClientConfigImpl.h create mode 100644 src/main/cpp/client/include/ClientManagerFactory.h rename src/main/cpp/client/include/{ClientInstance.h => ClientManagerImpl.h} (55%) create mode 100644 src/main/cpp/client/include/ConsumeMessageType.h delete mode 100644 src/main/cpp/client/include/Identifiable.h create mode 100644 src/main/cpp/client/mocks/include/ClientConfigMock.h create mode 100644 src/main/cpp/client/mocks/include/ClientManagerMock.h create mode 100644 src/main/cpp/client/mocks/include/ClientMock.h delete mode 100644 src/main/cpp/client/mocks/include/IdentifiableMock.h create mode 100644 src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h create mode 100644 src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp rename src/main/cpp/rocketmq/{BaseImpl.cpp => ClientImpl.cpp} (86%) create mode 100644 src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp delete mode 100644 src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp delete mode 100644 src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp create mode 100644 src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp create mode 100644 src/main/cpp/rocketmq/FilterExpression.cpp delete mode 100644 src/main/cpp/rocketmq/ProcessQueue.cpp create mode 100644 src/main/cpp/rocketmq/ProcessQueueImpl.cpp create mode 100644 src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h rename src/main/cpp/rocketmq/include/{BaseImpl.h => ClientImpl.h} (90%) create mode 100644 src/main/cpp/rocketmq/include/Consumer.h create mode 100644 src/main/cpp/rocketmq/include/ProcessQueueImpl.h create mode 100644 src/main/cpp/rocketmq/include/PushConsumer.h create mode 100644 src/main/cpp/rocketmq/mocks/CMakeLists.txt create mode 100644 src/main/cpp/rocketmq/mocks/include/ConsumerMock.h create mode 100644 src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h create mode 100644 src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h delete mode 100644 src/test/cpp/ut/AbseilTimeTest.cpp delete mode 100644 src/test/cpp/ut/AsanDemo.cpp delete mode 100644 src/test/cpp/ut/EventTest.cpp delete mode 100644 src/test/cpp/ut/GTestExample.cpp delete mode 100644 src/test/cpp/ut/HexTest.cpp delete mode 100644 src/test/cpp/ut/LogTest.cpp delete mode 100644 src/test/cpp/ut/MacTest.cpp delete mode 100644 src/test/cpp/ut/ZlibTest.cpp create mode 100644 src/test/cpp/ut/client/ClientManagerTest.cpp create mode 100644 src/test/cpp/ut/client/ClientTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/ConsumerTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/Semaphore.cpp delete mode 100644 src/test/cpp/ut/rocketmq/SemaphoreTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/include/Semaphore.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eacb2760..e79e0ff07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ if (BUILD_TESTS) FetchContent_MakeAvailable(googletest) endif () -FetchContent_MakeAvailable(fmt gRPC spdlog uv ghc) +FetchContent_MakeAvailable(fmt gRPC spdlog ghc) option(ENABLE_TRACING OFF) if (ENABLE_TRACING) diff --git a/README.md b/README.md index af3ecf672..c80c6a0ef 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,89 @@ [![CI](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml/badge.svg)](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml) ### Introduction -MQ products in the market supports two styles of API to acknowledge success of message consumption. +Apache RocketMQ supports two styles of APIs to acknowledge messages once they are successfully processed. -1. Rich client SDK manages individually and syncs to broker in batch manner; -2. On consumption of each message, thin client SDK acknowledge it to broker instantly. +1. Selective Acknowledgement + For each logical message queue(aka, topic partition), SDK manages offsets locally and periodically syncs committed offset to brokers in charge. +2. Per Message Acknowledgement + On consumption of each message, SDK acknowledge it to the broker instantly. Broker is responsible of managing consuming progress. -Both of which have famous adopters, Kafka, for example prefers 1 over 2 to achieve best performance and accept SDK -complexities. SQS/IBM MQ, etc choose choice 2. RocketMQ support both of them. +Either of them is widely adopted by products. Per message acknowledgement simplifies SDK implementation while selective approach is more performant considering that fewer RPCs are required. -### Pop SDK -This SDK implements choice of message consumption and acknowledgement. Aka, SDK leaves most of the complexities to -brokers to enjoy client simplicity and robustness. +### Transport Layer -### API -To make the transition as smooth as possible, API compatibility is maintained. Namespace for now is changed to rocketmq -from metaq during proof of concept phase. +This SDK is built on top of [gRPC](https://grpc.io/). [Protocol Buffers](https://developers.google.com/protocol-buffers) is used to serialize application messages. -### How to build -We use [Modern CMake](https://cliutils.gitlab.io/modern-cmake/) to manage and compile this project, therefore, fairly -easy to compile. -1. Clone this repo `git clone git@gitlab.alibaba-inc.com:shutian.lzh/rocketmq-cpp.git` -2. Build `cd rocketmq-cpp; mkdir _build; cd _build && cmake ..; make` -3. Enjoy +### Type Hierarchy +Classes of this project are designed to be interface oriented. +![Basic class hierarchy](docs/assets/BasicMode.png) +This paradigm makes dependency injection possible. DI is especially helpful when writing unit tests. -[Google Bazel](https://bazel.build/) is another build tool we supported, you can use `bazel build //...` to build the whole project. +### Core Concepts +![Class Diagram](docs/assets/class_diagram.png) -### IDE -[Clangd](https://clangd.llvm.org/) is a really cool code completion tool. Use the following command to generate compile_command.json if you prefer to use it. -``` -cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. -``` +### Code Style +Generally, we follow [Google C++ Code Style](https://google.github.io/styleguide/cppguide.html). A few exceptions are made to maintain API compatibility. +1. C++ exception is only allowed in the outer wrapper classes, for example, DefaultMQProducer, DefaultMQConsumer. +2. C++ --std=c++11 is preferred. We intend to maintain the same compiler compatibility matrix to [those of gRPC](https://github.com/grpc/grpc/blob/master/BUILDING.md) +3. Smart pointers are preferred where it makes sense. Use raw pointers only when it is really necessary. -### Sanitizer -Clang/GCC has developed cool collection of sanitizer tools: Address, Undefined, Thread... +### Dependency Management +Considering SDK built on top of gRPC, ensure it is really necessary before introducing a third-party library. Check [gRPC deps](https://github.com/grpc/grpc/blob/master/bazel/grpc_deps.bzl) and [gRPC extra deps](https://github.com/grpc/grpc/blob/master/bazel/grpc_extra_deps.bzl) first! -To run with sanitizer on, +When introducing a third-party dependency or raising version of a dependency, make sure it is back-off friendly. For example, ``` -bazel run -c dbg --config=asan //src/test:asan_demo +if "com_google_googletest" not in native.existing_rules(): + http_archive( + name = "com_google_googletest", + sha256 = "b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5", + strip_prefix = "googletest-release-1.11.0", + urls = [ + "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz", + ], + ) ``` -### Example -Talk is cheap, show me the code!!! +### How To Build +[Google Bazel](https://bazel.build/) is the primary build tool we supported, Please follow [bazel installation guide](https://docs.bazel.build/versions/main/install.html). + + +1. Build + From the repository root, + ``` + bazel build :all + ``` +2. Run Unit Tests + From the repository root, + ``` + bazel test //src/test/cpp/ut/... + ``` + +### IDE +[Visual Studio Code](https://code.visualstudio.com/) + [Clangd](https://clangd.llvm.org/) is the recommended development toolset. +1. VSCode + Clangd + [Clangd](https://clangd.llvm.org/) is a really nice code completion tool. Clangd requires compile_commands.json to work properly. + To generate the file, we need clone another repository along with the current one. -Here you go: + ``` + git clone git@github.com:grailbio/bazel-compilation-database.git + ``` + From current repository root, -1. [ExampleProducer.cpp](example/rocketmq/ExampleProducer.cpp) + ``` + ../bazel-compilation-database/generate.sh + ``` + Once the script completes, you should have compile_commands.json file in the repository root directory. -1. [ExamplePushConsumer.cpp](example/rocketmq/ExamplePushConsumer.cpp) + LLVM project has an extension for [clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd). Please install it from the extension market. -### FAQ -1. Q: Can this pop SDK deploy along with existing Pull SDK? - - A: We are currently working on this issue and this feature will be supported soon. Once the development is completed - and verified in production, we will get this item updated. - -1. Q: Is this SDK production ready? - - A: To be honest, this SDK is just built and many features are still under active development. However, we take quality - seriously and various measures are employed to achieve this goal, code review, unit test coverage, compiler - sanitizers, thread-annotations-to-avoid-concurrent-issues, ... - \ No newline at end of file + The following configuration entries should be appended to your VSC settings file. + ```text + "C_Cpp.intelliSenseEngine": "Disabled", + "C_Cpp.autocomplete": "Disabled", // So you don't get autocomplete from both extensions. + "C_Cpp.errorSquiggles": "Disabled", // So you don't get error squiggles from both extensions (clangd's seem to be more reliable anyway). + "clangd.path": "/Users/lizhanhui/usr/clangd_12.0.0/bin/clangd", + "clangd.arguments": ["-log=verbose", "-pretty", "--background-index"], + "clangd.onConfigChanged": "restart", + ``` +2. CLion + Bazel Plugin + Bazel also has a plugin for CLion. \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index c0a30f474..6bf4a56a0 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -8,7 +8,7 @@ ROCKETMQ_NAMESPACE_BEGIN struct AsyncCallback {}; -enum SendCallbackType { noAutoDeleteSendCallback = 0, autoDeleteSendCallback = 1 }; +enum class SendCallbackType : int8_t { noAutoDeleteSendCallback = 0, autoDeleteSendCallback = 1 }; using sendCallbackType = SendCallbackType; @@ -17,7 +17,7 @@ class SendCallback : public AsyncCallback { virtual ~SendCallback() = default; virtual void onSuccess(const SendResult& send_result) = 0; virtual void onException(const MQException& e) = 0; - virtual SendCallbackType getSendCallbackType() { return noAutoDeleteSendCallback; } + virtual SendCallbackType getSendCallbackType() { return SendCallbackType::noAutoDeleteSendCallback; } }; class PullCallback : public AsyncCallback { diff --git a/api/rocketmq/ConsumerType.h b/api/rocketmq/ConsumeType.h similarity index 88% rename from api/rocketmq/ConsumerType.h rename to api/rocketmq/ConsumeType.h index 65b2f4870..fb8514865 100644 --- a/api/rocketmq/ConsumerType.h +++ b/api/rocketmq/ConsumeType.h @@ -27,11 +27,6 @@ enum ConsumeInitialMode { MAX, }; -enum MessageModel { - BROADCASTING, - CLUSTERING, -}; - enum QueryOffsetPolicy : uint8_t { BEGINNING = 0, END = 1, @@ -52,6 +47,4 @@ struct PullMessageQuery { std::chrono::system_clock::duration timeout; }; -using Executor = std::function&)>; - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index 69d48b010..567220a16 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -42,12 +42,11 @@ class EnvironmentVariablesCredentialsProvider : public CredentialsProvider { Credentials getCredentials() override; + static const char* ENVIRONMENT_ACCESS_KEY; + static const char* ENVIRONMENT_ACCESS_SECRET; private: std::string access_key_; std::string access_secret_; - - static const char* ENVIRONMENT_ACCESS_KEY; - static const char* ENVIRONMENT_ACCESS_SECRET; }; /** diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index 8a5de93fe..0374b55f7 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -5,11 +5,11 @@ #include #include -#include "ConsumerType.h" -#include "MQMessageQueue.h" -#include "MQMessageExt.h" #include "AsyncCallback.h" +#include "ConsumeType.h" #include "CredentialsProvider.h" +#include "MQMessageExt.h" +#include "MQMessageQueue.h" #include ROCKETMQ_NAMESPACE_BEGIN diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index b3b5e9cb8..ae57b2c16 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -4,12 +4,14 @@ #include #include "AsyncCallback.h" -#include "ConsumerType.h" +#include "ConsumeType.h" +#include "CredentialsProvider.h" #include "ExpressionType.h" #include "Logger.h" -#include "MQMessageListener.h" #include "MQMessageQueue.h" -#include "CredentialsProvider.h" +#include "MessageListener.h" +#include "rocketmq/MessageModel.h" +#include "rocketmq/Executor.h" ROCKETMQ_NAMESPACE_BEGIN @@ -30,7 +32,7 @@ class DefaultMQPushConsumer { void setConsumeFromWhere(ConsumeFromWhere policy); - void registerMessageListener(MQMessageListener* listener); + void registerMessageListener(MessageListener* listener); void setNamesrvAddr(const std::string& name_srv); @@ -56,12 +58,6 @@ class DefaultMQPushConsumer { */ void setAsyncPull(bool); - /** - * By default maxCacheMsgSize is 1024. Valid range is: 1~65535. Should not change this value unless you know what - * you are doing. - */ - void setMaxCacheMsgSizePerQueue(int max_cache_size); - /** * Maximum number of messages passed to each callback. * @param batch_size Batch size diff --git a/api/rocketmq/Executor.h b/api/rocketmq/Executor.h new file mode 100644 index 000000000..1d78e8252 --- /dev/null +++ b/api/rocketmq/Executor.h @@ -0,0 +1,10 @@ +#pragma once + +#include "RocketMQ.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +using Executor = std::function&)>; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h index ae5f3ef2b..e1328a699 100644 --- a/api/rocketmq/MQMessageExt.h +++ b/api/rocketmq/MQMessageExt.h @@ -35,7 +35,7 @@ class MQMessageExt : public MQMessage { int64_t getQueueOffset() const; - int32_t getReconsumeTimes() const; + int32_t getDeliveryAttempt() const; const std::string& receiptHandle() const; diff --git a/api/rocketmq/MQMessageListener.h b/api/rocketmq/MQMessageListener.h deleted file mode 100644 index 07be63c71..000000000 --- a/api/rocketmq/MQMessageListener.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "MQMessageExt.h" -#include "MQMessageQueue.h" -#include -#include "iostream" - -ROCKETMQ_NAMESPACE_BEGIN - -enum ConsumeStatus { CONSUME_SUCCESS, RECONSUME_LATER }; - -enum MessageListenerType { messageListenerDefaultly = 0, messageListenerOrderly = 1, messageListenerConcurrently = 2 }; - -class MQMessageListener { -public: - virtual ~MQMessageListener() {} - virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerDefaultly; } -}; - -class MessageListenerOrderly : public MQMessageListener { -public: - virtual ~MessageListenerOrderly() {} - virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerOrderly; } -}; - -class MessageListenerConcurrently : public MQMessageListener { -public: - virtual ~MessageListenerConcurrently() {} - virtual ConsumeStatus consumeMessage(const std::vector& msgs) = 0; - virtual MessageListenerType getMessageListenerType() { return messageListenerConcurrently; } -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h new file mode 100644 index 000000000..d8c388c54 --- /dev/null +++ b/api/rocketmq/MessageListener.h @@ -0,0 +1,33 @@ +#pragma once + +#include "MQMessageExt.h" +#include "MQMessageQueue.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ConsumeMessageResult : uint8_t { SUCCESS = 0, FAILURE = 1 }; + +enum class MessageListenerType : uint8_t { STANDARD = 0, FIFO = 1 }; + +class MessageListener { +public: + virtual ~MessageListener() = default; + + virtual MessageListenerType listenerType() = 0; +}; + +class StandardMessageListener : public MessageListener { +public: + virtual ConsumeMessageResult consumeMessage(const std::vector& msgs) = 0; + + MessageListenerType listenerType() override { return MessageListenerType::STANDARD; } +}; + +class FifoMessageListener : public MessageListener { +public: + MessageListenerType listenerType() override { return MessageListenerType::FIFO; } + + virtual ConsumeMessageResult consumeMessage(const MQMessageExt& msgs) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h new file mode 100644 index 000000000..6f9f5a7e8 --- /dev/null +++ b/api/rocketmq/MessageModel.h @@ -0,0 +1,13 @@ +#pragma once +#include "RocketMQ.h" + +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class MessageModel : int8_t { + BROADCASTING, + CLUSTERING, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 1bd924dfe..549d8a7f3 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -6,7 +6,7 @@ ROCKETMQ_NAMESPACE_BEGIN -enum SendStatus { SEND_OK, SEND_FLUSH_DISK_TIMEOUT, SEND_FLUSH_SLAVE_TIMEOUT, SEND_SLAVE_NOT_AVAILABLE }; +enum class SendStatus : int8_t { SEND_OK, SEND_FLUSH_DISK_TIMEOUT, SEND_FLUSH_SLAVE_TIMEOUT, SEND_SLAVE_NOT_AVAILABLE }; class SendResult { public: diff --git a/docs/assets/BasicMode.png b/docs/assets/BasicMode.png new file mode 100644 index 0000000000000000000000000000000000000000..f042585150eaf15b0b43bb41db1abe408c60dc9d GIT binary patch literal 42779 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 SJo60vyDO(Gnm__-XV=d0l^t{?4x1>1sfLn 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

)Cb|SjxfARLY3&HA6IM+=)L2-N= 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%0DqhCKZf&69W8>I%Yie)oJ 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


?(cn$=#H||;TLn`*FdDE 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%0AG#udMcO2>Psc_akyP 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 L20Ktxxu%d^3U>s%Q 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 literal 0 HcmV?d00001 diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel index 01684fba5..6e81e5088 100644 --- a/example/rocketmq/BUILD.bazel +++ b/example/rocketmq/BUILD.bazel @@ -86,4 +86,26 @@ cc_binary( "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], +) + +cc_binary( + name = "benchmark_push_consumer", + srcs = [ + "BenchmarkPushConsumer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + +cc_binary( + name = "example_broadcast_push_consumer", + srcs = [ + "ExampleBroadcastPushConsumer.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/rocketmq:rocketmq_library", + ], ) \ No newline at end of file diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp index dddb8dad0..fae094268 100644 --- a/example/rocketmq/BenchmarkPushConsumer.cpp +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -4,16 +4,17 @@ #include #include #include +#include using namespace rocketmq; -class CounterMessageListener : public MessageListenerConcurrently { +class CounterMessageListener : public StandardMessageListener { public: explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { counter_.fetch_add(msgs.size()); - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } private: @@ -29,7 +30,7 @@ int main(int argc, char* argv[]) { std::atomic_long counter(0); DefaultMQPushConsumer push_consumer("CID_sample"); - MQMessageListener* listener = new CounterMessageListener(counter); + MessageListener* listener = new CounterMessageListener(counter); push_consumer.setGroupName("CID_sample"); push_consumer.setInstanceName("CID_sample_member_0"); diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index b37bc740d..40c84ff30 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -2,10 +2,10 @@ #include #include #include +#include #include #include #include -#include using namespace rocketmq; diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 5b5c53687..c14245fbc 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -8,14 +8,14 @@ using namespace rocketmq; -class SampleMQMessageListener : public MessageListenerConcurrently { +class SampleMQMessageListener : public StandardMessageListener { public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } }; @@ -34,7 +34,7 @@ int main(int argc, char* argv[]) { push_consumer.setArn(arn); push_consumer.setCredentialsProvider(std::make_shared()); push_consumer.setNamesrvAddr("11.165.223.199:9876"); - MQMessageListener* listener = new SampleMQMessageListener; + MessageListener* listener = new SampleMQMessageListener; push_consumer.setGroupName(cid); push_consumer.setInstanceName("instance_0"); push_consumer.subscribe(topic, "*"); diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 70858ecc6..d7832f968 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -8,14 +8,14 @@ using namespace rocketmq; -class SampleMQMessageListener : public MessageListenerConcurrently { +class SampleMQMessageListener : public StandardMessageListener { public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } }; @@ -26,14 +26,14 @@ int main(int argc, char* argv[]) { logger.init(); const char* cid = "GID_group003"; - const char *topic = "yc001"; + const char* topic = "yc001"; const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; DefaultMQPushConsumer push_consumer(cid); push_consumer.setArn(arn); push_consumer.setCredentialsProvider(std::make_shared()); push_consumer.setNamesrvAddr("11.165.223.199:9876"); - MQMessageListener* listener = new SampleMQMessageListener; + MessageListener* listener = new SampleMQMessageListener; push_consumer.setGroupName(cid); push_consumer.setInstanceName("instance_0"); push_consumer.subscribe(topic, "*"); diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp index e913c34c3..60244d551 100644 --- a/example/rocketmq/PushConsumerWithCustomExecutor.cpp +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -3,12 +3,11 @@ #include #include #include +#include #include #include -using namespace rocketmq; - -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class ExecutorImpl { public: @@ -86,31 +85,31 @@ class ExecutorImpl { std::thread worker_; }; -} // namespace rocketmq - -class SampleMQMessageListener : public MessageListenerConcurrently { +class SampleMQMessageListener : public StandardMessageListener { public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { std::lock_guard lk(console_mtx_); for (const MQMessageExt& msg : msgs) { std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Body=" << msg.getBody() << std::endl; } - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } private: std::mutex console_mtx_; }; -int main(int argc, char* argv[]) { +ROCKETMQ_NAMESPACE_END +int main(int argc, char* argv[]) { + using namespace ROCKETMQ_NAMESPACE; Logger& logger = getLogger(); logger.setLevel(Level::Debug); logger.init(); DefaultMQPushConsumer push_consumer("TestGroup"); - MQMessageListener* listener = new SampleMQMessageListener; + MessageListener* listener = new SampleMQMessageListener; auto pool = new ExecutorImpl; pool->start(); @@ -118,7 +117,7 @@ int main(int argc, char* argv[]) { push_consumer.setGroupName("TestGroup"); push_consumer.setInstanceName("CID_sample_member_0"); push_consumer.subscribe("TestTopic", "*"); - push_consumer.setNamesrvAddr("11.167.164.105:9876"); + push_consumer.setNamesrvAddr("11.167.164.105:9876"); push_consumer.registerMessageListener(listener); push_consumer.start(); diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp index dc9e977f5..af8926580 100644 --- a/example/rocketmq/PushConsumerWithThrottle.cpp +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -2,26 +2,31 @@ #include #include +#include #include #include -using namespace rocketmq; +ROCKETMQ_NAMESPACE_BEGIN -class CounterMessageListener : public MessageListenerConcurrently { +class CounterMessageListener : public StandardMessageListener { public: explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { counter_.fetch_add(msgs.size()); - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } private: std::atomic_long& counter_; }; +ROCKETMQ_NAMESPACE_END + int main(int argc, char* argv[]) { + using namespace ROCKETMQ_NAMESPACE; + Logger& logger = getLogger(); logger.setLevel(Level::Debug); logger.init(); @@ -29,7 +34,7 @@ int main(int argc, char* argv[]) { std::atomic_long counter(0); DefaultMQPushConsumer push_consumer("TestGroup"); - MQMessageListener* listener = new CounterMessageListener(counter); + MessageListener* listener = new CounterMessageListener(counter); push_consumer.setGroupName("TestGroup"); push_consumer.setInstanceName("CID_sample_member_0"); diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp index d2eddd819..4a690bdad 100644 --- a/example/rocketmq/SqlConsumer.cpp +++ b/example/rocketmq/SqlConsumer.cpp @@ -3,18 +3,19 @@ #include #include #include +#include using namespace rocketmq; -class SampleMQMessageListener : public MessageListenerConcurrently { +class SampleMQMessageListener : public StandardMessageListener { public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { std::lock_guard lk(console_mtx_); for (const MQMessageExt& msg : msgs) { std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Tag=" << msg.getTags() << ", a=" << msg.getProperty("a") << ", Body=" << msg.getBody() << std::endl; } - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } private: @@ -27,7 +28,7 @@ int main(int argc, char* argv[]) { logger.init(); DefaultMQPushConsumer push_consumer("TestGroup"); - MQMessageListener* listener = new SampleMQMessageListener; + MessageListener* listener = new SampleMQMessageListener; push_consumer.setGroupName("TestGroup"); push_consumer.setInstanceName("CID_sample_member_0"); diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index 6a82ae38a..04af66f1d 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -94,6 +94,7 @@ message HeartbeatEntry { ProducerGroup producer_group = 2; ConsumerGroup consumer_group = 3; } + bool need_rebalance = 4; } enum AddressScheme { @@ -242,16 +243,22 @@ message SystemAttribute { google.protobuf.Duration invisible_period = 16; // Business code may failed to process messages for the moment. Hence, clients may request servers to deliver them - // again using certain back-off strategy. - int32 delivery_count = 17; + // again using certain back-off strategy, the attempt is 1 not 0 if message is delivered first time. + int32 delivery_attempt = 17; + + // Message's max delivery attempts count, if message's delivery count exceed this value, it will be sent to dlq topic. + int32 max_delivery_attempts = 18; // Message producer load-balance group if applicable. - Resource producer_group = 18; + Resource producer_group = 19; - string message_group = 19; + string message_group = 20; // Trace context. - string trace_context = 20; + string trace_context = 21; + + // Correlated transaction + string transaction_id = 22; } message Message { @@ -274,6 +281,7 @@ enum ConsumeMessageType { message Assignment { Partition Partition = 1; + // to delete. ConsumeMessageType mode = 2; } diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 8a1ef2468..13351e1d7 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -39,6 +39,7 @@ message QueryRouteResponse { message SendMessageRequest { Message message = 1; + Partition partition = 2; } message SendMessageResponse { @@ -68,6 +69,7 @@ message ReceiveMessageRequest { int32 batch_size = 7; google.protobuf.Duration invisible_duration = 8; google.protobuf.Duration await_time = 9; + bool fifo_flag = 10; } message ReceiveMessageResponse { @@ -98,8 +100,9 @@ message NackMessageRequest { string client_id = 3; string receipt_handle = 4; string message_id = 5; - int32 reconsume_times = 6; - int32 max_reconsume_times = 7; + int32 delivery_attempt = 6; + int32 max_delivery_attempts = 7; + // to delete. ConsumeModel consume_model = 8; } @@ -107,6 +110,20 @@ message NackMessageResponse { ResponseCommon common = 1; } +message ForwardMessageToDeadLetterQueueRequest { + Resource group = 1; + Resource topic = 2; + string client_id = 3; + string receipt_handle = 4; + string message_id = 5; + int32 delivery_attempt = 6; + int32 max_delivery_attempts = 7; +} + +message ForwardMessageToDeadLetterQueueResponse { + ResponseCommon common = 1; +} + message HeartbeatRequest { repeated HeartbeatEntry heartbeats = 1; } @@ -199,7 +216,8 @@ message PrintThreadStackResponse { } message VerifyMessageConsumptionRequest { - Message message = 1; + Partition Partition = 1; + Message message = 2; } message VerifyMessageConsumptionResponse { @@ -253,6 +271,9 @@ service MessagingService { rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { } + rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) returns (ForwardMessageToDeadLetterQueueResponse) { + } + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { } diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 9cb7198ca..f50a0c4ff 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -78,4 +78,16 @@ cc_library( "@boringssl//:crypto", "@boringssl//:ssl", ], +) + +cc_library( + name = "message_listener_mocks", + hdrs = [ + "mocks/include/MessageListenerMock.h", + ], + strip_include_prefix = "//src/main/cpp/base/mocks/include", + deps = [ + "//api:rocketmq_interface", + "//external:gtest", + ], ) \ No newline at end of file diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt index f4e44406f..3c4504c04 100644 --- a/src/main/cpp/base/CMakeLists.txt +++ b/src/main/cpp/base/CMakeLists.txt @@ -3,10 +3,11 @@ target_sources(base_obj PRIVATE GHttpClient.cpp HostInfo.cpp - Metadata.cpp + MessageAccessor.cpp + MetadataConstants.cpp MixAll.cpp MQMessage.cpp - PermissionHelper.cpp + MQMessageExt.cpp Protocol.cpp RateLimiter.cpp TopAddressing.cpp @@ -54,4 +55,5 @@ target_link_libraries(base_library rocketmq_interface spdlog::spdlog_header_only uv_a - zlib) \ No newline at end of file + zlib) +add_subdirectory(mocks) \ No newline at end of file diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 6a25d05d8..63a905ca3 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -1,23 +1,10 @@ #include "rocketmq/MQMessage.h" +#include "MessageImpl.h" #include "MessageAccessor.h" -#include "Protocol.h" #include "rocketmq/MQMessageExt.h" -#include -#include ROCKETMQ_NAMESPACE_BEGIN -class MessageImpl { -private: - Resource topic_; - std::map user_attribute_map_; - SystemAttribute system_attribute_; - std::string body_; - friend class MQMessage; - friend class MQMessageExt; - friend class MessageAccessor; -}; - MQMessage::MQMessage() : MQMessage("", "", "", "") {} MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) {} @@ -99,114 +86,4 @@ void MQMessage::setProperties(const std::map& properti impl_->user_attribute_map_.insert({it.first, it.second}); } } - -MQMessageExt::MQMessageExt() : MQMessage() {} - -MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) {} - -MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { - if (this == &other) { - return *this; - } - - *impl_ = *(other.impl_); - return *this; -} - -int32_t MQMessageExt::getQueueId() const { return impl_->system_attribute_.partition_id; } - -std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { - return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); -} - -int64_t MQMessageExt::getBornTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); } - -std::string MQMessageExt::getBornHost() const { return impl_->system_attribute_.born_host; } - -std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { - return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); -} - -int64_t MQMessageExt::getStoreTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); } - -std::string MQMessageExt::getStoreHost() const { return impl_->system_attribute_.store_host; } - -const std::string& MQMessageExt::getMsgId() const { return impl_->system_attribute_.message_id; } - -int64_t MQMessageExt::getQueueOffset() const { return impl_->system_attribute_.partition_offset; } - -int32_t MQMessageExt::getReconsumeTimes() const { return impl_->system_attribute_.delivery_count; } - -const std::string& MQMessageExt::receiptHandle() const { return impl_->system_attribute_.receipt_handle; } - -const std::string& MQMessageExt::traceContext() const { return impl_->system_attribute_.trace_context; } - -bool MQMessageExt::operator==(const MQMessageExt& other) { - return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; -} - -void MessageAccessor::setMessageId(MQMessageExt& message, std::string message_id) { - message.impl_->system_attribute_.message_id = std::move(message_id); -} - -void MessageAccessor::setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp) { - message.impl_->system_attribute_.born_timestamp = born_timestamp; -} - -void MessageAccessor::setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp) { - message.impl_->system_attribute_.store_timestamp = store_timestamp; -} - -void MessageAccessor::setQueueId(MQMessageExt& message, int32_t queue_id) { - message.impl_->system_attribute_.partition_id = queue_id; -} - -void MessageAccessor::setQueueOffset(MQMessageExt& message, int64_t queue_offset) { - message.impl_->system_attribute_.partition_offset = queue_offset; -} - -void MessageAccessor::setBornHost(MQMessageExt& message, std::string born_host) { - message.impl_->system_attribute_.born_host = std::move(born_host); -} - -void MessageAccessor::setStoreHost(MQMessageExt& message, std::string store_host) { - message.impl_->system_attribute_.store_host = std::move(store_host); -} - -void MessageAccessor::setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp) { - message.impl_->system_attribute_.delivery_timestamp = delivery_timestamp; -} - -void MessageAccessor::setDeliveryCount(MQMessageExt& message, int32_t delivery_count) { - message.impl_->system_attribute_.delivery_count = delivery_count; -} - -void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp) { - message.impl_->system_attribute_.decode_timestamp = decode_timestamp; -} - -void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { - message.impl_->system_attribute_.invisible_period = invisible_period; -} - -void MessageAccessor::setReceiptHandle(MQMessageExt& message, std::string receipt_handle) { - message.impl_->system_attribute_.receipt_handle = std::move(receipt_handle); -} - -void MessageAccessor::setTraceContext(MQMessageExt& message, std::string trace_context) { - message.impl_->system_attribute_.trace_context = std::move(trace_context); -} - -void MessageAccessor::setMessageType(MQMessage& message, MessageType message_type) { - message.impl_->system_attribute_.message_type = message_type; -} - -void MessageAccessor::setTargetEndpoint(MQMessage& message, const std::string& target_endpoint) { - message.impl_->system_attribute_.target_endpoint = target_endpoint; -} - -const std::string& MessageAccessor::targetEndpoint(const MQMessage& message) { - return message.impl_->system_attribute_.target_endpoint; -} - ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp new file mode 100644 index 000000000..84a1aca3f --- /dev/null +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -0,0 +1,51 @@ +#include "rocketmq/MQMessageExt.h" +#include "MessageImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +MQMessageExt::MQMessageExt() : MQMessage() {} + +MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) {} + +MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { + if (this == &other) { + return *this; + } + + *impl_ = *(other.impl_); + return *this; +} + +int32_t MQMessageExt::getQueueId() const { return impl_->system_attribute_.partition_id; } + +std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); +} + +int64_t MQMessageExt::getBornTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); } + +std::string MQMessageExt::getBornHost() const { return impl_->system_attribute_.born_host; } + +std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { + return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); +} + +int64_t MQMessageExt::getStoreTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); } + +std::string MQMessageExt::getStoreHost() const { return impl_->system_attribute_.store_host; } + +const std::string& MQMessageExt::getMsgId() const { return impl_->system_attribute_.message_id; } + +int64_t MQMessageExt::getQueueOffset() const { return impl_->system_attribute_.partition_offset; } + +int32_t MQMessageExt::getDeliveryAttempt() const { return impl_->system_attribute_.attempt_times; } + +const std::string& MQMessageExt::receiptHandle() const { return impl_->system_attribute_.receipt_handle; } + +const std::string& MQMessageExt::traceContext() const { return impl_->system_attribute_.trace_context; } + +bool MQMessageExt::operator==(const MQMessageExt& other) { + return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp new file mode 100644 index 000000000..ba4307731 --- /dev/null +++ b/src/main/cpp/base/MessageAccessor.cpp @@ -0,0 +1,70 @@ +#include "MessageAccessor.h" +#include "MessageImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void MessageAccessor::setMessageId(MQMessageExt& message, std::string message_id) { + message.impl_->system_attribute_.message_id = std::move(message_id); +} + +void MessageAccessor::setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp) { + message.impl_->system_attribute_.born_timestamp = born_timestamp; +} + +void MessageAccessor::setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp) { + message.impl_->system_attribute_.store_timestamp = store_timestamp; +} + +void MessageAccessor::setQueueId(MQMessageExt& message, int32_t queue_id) { + message.impl_->system_attribute_.partition_id = queue_id; +} + +void MessageAccessor::setQueueOffset(MQMessageExt& message, int64_t queue_offset) { + message.impl_->system_attribute_.partition_offset = queue_offset; +} + +void MessageAccessor::setBornHost(MQMessageExt& message, std::string born_host) { + message.impl_->system_attribute_.born_host = std::move(born_host); +} + +void MessageAccessor::setStoreHost(MQMessageExt& message, std::string store_host) { + message.impl_->system_attribute_.store_host = std::move(store_host); +} + +void MessageAccessor::setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp) { + message.impl_->system_attribute_.delivery_timestamp = delivery_timestamp; +} + +void MessageAccessor::setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times) { + message.impl_->system_attribute_.attempt_times = attempt_times; +} + +void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp) { + message.impl_->system_attribute_.decode_timestamp = decode_timestamp; +} + +void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { + message.impl_->system_attribute_.invisible_period = invisible_period; +} + +void MessageAccessor::setReceiptHandle(MQMessageExt& message, std::string receipt_handle) { + message.impl_->system_attribute_.receipt_handle = std::move(receipt_handle); +} + +void MessageAccessor::setTraceContext(MQMessageExt& message, std::string trace_context) { + message.impl_->system_attribute_.trace_context = std::move(trace_context); +} + +void MessageAccessor::setMessageType(MQMessage& message, MessageType message_type) { + message.impl_->system_attribute_.message_type = message_type; +} + +void MessageAccessor::setTargetEndpoint(MQMessage& message, const std::string& target_endpoint) { + message.impl_->system_attribute_.target_endpoint = target_endpoint; +} + +const std::string& MessageAccessor::targetEndpoint(const MQMessage& message) { + return message.impl_->system_attribute_.target_endpoint; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/Metadata.cpp b/src/main/cpp/base/Metadata.cpp deleted file mode 100644 index 2f97cef9f..000000000 --- a/src/main/cpp/base/Metadata.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "Metadata.h" - -ROCKETMQ_NAMESPACE_BEGIN - -const char* Metadata::TENANT_ID_KEY = "x-mq-tenant-id"; -const char* Metadata::ARN_KEY = "x-mq-arn"; -const char* Metadata::AUTHORIZATION = "authorization"; -const char* Metadata::DATE_TIME_KEY = "x-mq-date-time"; -const char* Metadata::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; -const char* Metadata::CREDENTIAL_KEY = "Credential"; -const char* Metadata::SIGNED_HEADERS_KEY = "SignedHeaders"; -const char* Metadata::SIGNATURE_KEY = "Signature"; -const char* Metadata::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; -const char* Metadata::LANGUAGE_KEY = "x-mq-language"; -const char* Metadata::CLIENT_VERSION_KEY = "x-mq-client-version"; -const char* Metadata::PROTOCOL_VERSION_KEY = "x-mq-protocol-version"; -const char* Metadata::REQUEST_ID_KEY = "x-mq-request-id"; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp new file mode 100644 index 000000000..5f29a06a2 --- /dev/null +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -0,0 +1,19 @@ +#include "MetadataConstants.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; +const char* MetadataConstants::ARN_KEY = "x-mq-arn"; +const char* MetadataConstants::AUTHORIZATION = "authorization"; +const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; +const char* MetadataConstants::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; +const char* MetadataConstants::CREDENTIAL_KEY = "Credential"; +const char* MetadataConstants::SIGNED_HEADERS_KEY = "SignedHeaders"; +const char* MetadataConstants::SIGNATURE_KEY = "Signature"; +const char* MetadataConstants::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; +const char* MetadataConstants::LANGUAGE_KEY = "x-mq-language"; +const char* MetadataConstants::CLIENT_VERSION_KEY = "x-mq-client-version"; +const char* MetadataConstants::PROTOCOL_VERSION_KEY = "x-mq-protocol-version"; +const char* MetadataConstants::REQUEST_ID_KEY = "x-mq-request-id"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index aacf2c8d0..524a0b0b5 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -17,17 +17,21 @@ ROCKETMQ_NAMESPACE_BEGIN const int32_t MixAll::MASTER_BROKER_ID = 0; -const int32_t MixAll::MAX_MESSAGE_NUMBER_PER_BATCH = 32; +const int32_t MixAll::DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE = 32; const uint32_t MixAll::MAX_MESSAGE_BODY_SIZE = 1024 * 1024 * 4; - -const char* MixAll::RETRY_GROUP_TOPIC_PREFIX = "%RETRY%"; +const uint32_t MixAll::MAX_CACHED_MESSAGE_COUNT = 65535; +const uint32_t MixAll::DEFAULT_CACHED_MESSAGE_COUNT = 1024; +const uint64_t MixAll::DEFAULT_CACHED_MESSAGE_MEMORY = 128L * 1024 * 1024; +const uint32_t MixAll::DEFAULT_CONSUME_THREAD_POOL_SIZE = 20; +const uint32_t MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE = 1; +const int32_t MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS = 16; const RE2 MixAll::TOPIC_REGEX("[a-zA-Z0-9\\-_]{3,64}"); const std::chrono::duration MixAll::DEFAULT_INVISIBLE_TIME_ = std::chrono::seconds(30); -const std::chrono::duration MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ = std::chrono::seconds(20); +const std::chrono::duration MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ = std::chrono::seconds(120); const int32_t MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_ = 3; @@ -87,7 +91,7 @@ bool MixAll::sha1(const std::string& data, std::string& digest) { SHA1_Update(&ctx, data.data(), data.length()); SHA1_Final(out, &ctx); digest.clear(); - digest.append(reinterpret_cast(out), SHA_DIGEST_LENGTH); + digest.append(hex(reinterpret_cast(out), SHA_DIGEST_LENGTH)); return true; } diff --git a/src/main/cpp/base/PermissionHelper.cpp b/src/main/cpp/base/PermissionHelper.cpp deleted file mode 100644 index 70328d19b..000000000 --- a/src/main/cpp/base/PermissionHelper.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "PermissionHelper.h" - -using namespace rocketmq; - -const unsigned int PermissionHelper::READ_ = 0x1u << 2u; - -const unsigned int PermissionHelper::WRITE_ = 0x1u << 1u; - -const unsigned int PermissionHelper::INHERIT_ = 0x1u << 0u; - -bool PermissionHelper::canRead(unsigned int mode) { return (mode & READ_) == READ_; } - -bool PermissionHelper::canWrite(unsigned int mode) { return (mode & WRITE_) == WRITE_; } - -bool PermissionHelper::canInherit(unsigned int mode) { return (mode & INHERIT_) == INHERIT_; } \ No newline at end of file diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 8fb2614f4..32ef5a7c2 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -1,7 +1,7 @@ #pragma once #include "LoggerImpl.h" -#include "Metadata.h" +#include "MetadataConstants.h" #include "UniqueIdGenerator.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -23,7 +23,7 @@ ROCKETMQ_NAMESPACE_BEGIN */ struct BaseInvocationContext { BaseInvocationContext() : request_id_(UniqueIdGenerator::instance().next()) { - context.AddMetadata(Metadata::REQUEST_ID_KEY, request_id_); + context.AddMetadata(MetadataConstants::REQUEST_ID_KEY, request_id_); } virtual ~BaseInvocationContext() = default; diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h index a88114857..c97dd13f7 100644 --- a/src/main/cpp/base/include/MessageAccessor.h +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -26,7 +26,7 @@ class MessageAccessor { static void setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp); - static void setDeliveryCount(MQMessageExt& message, int32_t delivery_count); + static void setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times); static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h new file mode 100644 index 000000000..36d3d74ed --- /dev/null +++ b/src/main/cpp/base/include/MessageImpl.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageImpl { +protected: + Resource topic_; + std::map user_attribute_map_; + SystemAttribute system_attribute_; + std::string body_; + friend class MQMessage; + friend class MQMessageExt; + friend class MessageAccessor; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Metadata.h b/src/main/cpp/base/include/MetadataConstants.h similarity index 95% rename from src/main/cpp/base/include/Metadata.h rename to src/main/cpp/base/include/MetadataConstants.h index 202ff1b68..fdccf183c 100644 --- a/src/main/cpp/base/include/Metadata.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -3,7 +3,7 @@ ROCKETMQ_NAMESPACE_BEGIN -class Metadata { +class MetadataConstants { public: static const char* REQUEST_ID_KEY; static const char* TENANT_ID_KEY; diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 42d8203d9..36d348bd1 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -5,18 +5,21 @@ #include #include #include - #include "rocketmq/MQMessage.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN class MixAll { public: static const int32_t MASTER_BROKER_ID; - static const int32_t MAX_MESSAGE_NUMBER_PER_BATCH; + static const int32_t DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE; static const uint32_t MAX_MESSAGE_BODY_SIZE; - static const char* RETRY_GROUP_TOPIC_PREFIX; + static const uint32_t MAX_CACHED_MESSAGE_COUNT; + static const uint32_t DEFAULT_CACHED_MESSAGE_COUNT; + static const uint64_t DEFAULT_CACHED_MESSAGE_MEMORY; + static const uint32_t DEFAULT_CONSUME_THREAD_POOL_SIZE; + static const uint32_t DEFAULT_CONSUME_MESSAGE_BATCH_SIZE; + static const int32_t DEFAULT_MAX_DELIVERY_ATTEMPTS; static const RE2 TOPIC_REGEX; diff --git a/src/main/cpp/base/include/PermissionHelper.h b/src/main/cpp/base/include/PermissionHelper.h deleted file mode 100644 index 1756a7e5d..000000000 --- a/src/main/cpp/base/include/PermissionHelper.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace rocketmq { -class PermissionHelper { -public: - static bool canRead(unsigned int mode); - - static bool canWrite(unsigned int mode); - - static bool canInherit(unsigned int mode); - -private: - static const unsigned int READ_; - static const unsigned int WRITE_; - static const unsigned int INHERIT_; -}; - -} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 65dfb2c70..8101bd991 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -69,7 +69,7 @@ struct SystemAttribute { int32_t partition_id{0}; int64_t partition_offset{0}; absl::Duration invisible_period; - int32_t delivery_count{0}; + int32_t attempt_times{0}; Resource publisher_group; std::string trace_context; std::string target_endpoint; diff --git a/src/main/cpp/base/mocks/CMakeLists.txt b/src/main/cpp/base/mocks/CMakeLists.txt new file mode 100644 index 000000000..8ca2461df --- /dev/null +++ b/src/main/cpp/base/mocks/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(base_mocks INTERFACE) +target_include_directories(base_mocks INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(base_mocks + INTERFACE + base_library + gmock) \ No newline at end of file diff --git a/src/main/cpp/base/mocks/include/MessageListenerMock.h b/src/main/cpp/base/mocks/include/MessageListenerMock.h new file mode 100644 index 000000000..81da855c9 --- /dev/null +++ b/src/main/cpp/base/mocks/include/MessageListenerMock.h @@ -0,0 +1,18 @@ +#pragma once + +#include "rocketmq/MessageListener.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StandardMessageListenerMock : public StandardMessageListener { +public: + MOCK_METHOD(ConsumeMessageResult, consumeMessage, (const std::vector&), (override)); +}; + +class FifoMessageListenerMock : public FifoMessageListener { +public: + MOCK_METHOD(ConsumeMessageResult, consumeMessage, (const MQMessageExt&), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index 6ec90b8de..d3a88db61 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -1,4 +1,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + cc_library( name = "client_library", hdrs = glob(["include/*.h"]), @@ -20,24 +23,17 @@ cc_library( "//external:gtest", "//third_party/crc:crc_interface", ], - visibility = ["//visibility:public"], defines = [ ], ) cc_library( - name = "rpc_client_mock", - hdrs = [ - "mocks/include/RpcClientMock.h", - "mocks/include/IdentifiableMock.h", - ], - srcs = [ - "mocks/RpcClientMock.cpp", - ], + name = "client_mocks", + hdrs = glob(["mocks/include/*.h"]), + srcs = glob(["mocks/*.cpp"]), strip_include_prefix = "//src/main/cpp/client/mocks/include", deps = [ ":client_library", "//external:gtest", ], - visibility = ["//visibility:public"], ) \ No newline at end of file diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt index e39390c83..923818dc4 100644 --- a/src/main/cpp/client/CMakeLists.txt +++ b/src/main/cpp/client/CMakeLists.txt @@ -1,10 +1,9 @@ add_library(client_obj OBJECT) target_sources(client_obj PRIVATE - ClientApi.cpp - ClientConfig.cpp - ClientInstance.cpp - ClientManager.cpp + ClientConfigImpl.cpp + ClientManagerImpl.cpp + ClientManagerFactory.cpp LogInterceptorFactory.cpp LogInterceptor.cpp TopicAssignmentInfo.cpp diff --git a/src/main/cpp/client/ClientApi.cpp b/src/main/cpp/client/ClientApi.cpp deleted file mode 100644 index 300a6cbfd..000000000 --- a/src/main/cpp/client/ClientApi.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "ClientApi.h" - -using namespace rocketmq; - -void ClientApi::update_name_server_list(const std::vector& list) { - if (list.empty()) { - return; - } - - { - absl::MutexLock lock(&name_server_list_mtx_); - name_server_list_.clear(); - name_server_list_.insert(name_server_list_.begin(), list.begin(), list.end()); - } -} \ No newline at end of file diff --git a/src/main/cpp/client/ClientConfig.cpp b/src/main/cpp/client/ClientConfig.cpp deleted file mode 100644 index e754e0e27..000000000 --- a/src/main/cpp/client/ClientConfig.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "ClientConfig.h" -#include "UtilAll.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -#ifndef CLIENT_VERSION_MAJOR -#define CLIENT_VERSION_MAJOR "1" -#endif - -#ifndef CLIENT_VERSION_MINOR -#define CLIENT_VERSION_MINOR "0" -#endif - -#ifndef CLIENT_VERSION_PATCH -#define CLIENT_VERSION_PATCH "0" -#endif - -const char* ClientConfig::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; - -ClientConfig::ClientConfig() : ClientConfig(std::string()) {} - -ClientConfig::ClientConfig(std::string group_name) - : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)), long_polling_timeout_(absl::Seconds(30)) { - instance_name_ = "DEFAULT"; -} - -std::string ClientConfig::clientId() const { - std::stringstream ss; - ss << UtilAll::hostname(); - ss << "@"; - std::string processID = std::to_string(getpid()); - ss << (instance_name_ == "DEFAULT" ? processID : instance_name_); - return ss.str(); -} - -const std::string& ClientConfig::getInstanceName() const { return instance_name_; } - -void ClientConfig::setInstanceName(std::string instance_name) { instance_name_ = std::move(instance_name); } - -const std::string& ClientConfig::getGroupName() const { return group_name_; } - -void ClientConfig::setGroupName(std::string group_name) { group_name_ = std::move(group_name); } - -void ClientConfig::setIoTimeout(absl::Duration timeout) { io_timeout_ = timeout; } - -void ClientConfig::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { - credentials_provider_ = std::move(credentials_provider); -} - -CredentialsProviderPtr ClientConfig::credentialsProvider() { return credentials_provider_; } - -absl::Duration ClientConfig::getIoTimeout() const { return io_timeout_; } - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp new file mode 100644 index 000000000..9c31ddd95 --- /dev/null +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -0,0 +1,56 @@ +#include "ClientConfigImpl.h" +#include "UtilAll.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +#ifndef CLIENT_VERSION_MAJOR +#define CLIENT_VERSION_MAJOR "1" +#endif + +#ifndef CLIENT_VERSION_MINOR +#define CLIENT_VERSION_MINOR "0" +#endif + +#ifndef CLIENT_VERSION_PATCH +#define CLIENT_VERSION_PATCH "0" +#endif + +const char* ClientConfigImpl::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; + +ClientConfigImpl::ClientConfigImpl() : ClientConfigImpl(std::string()) {} + +ClientConfigImpl::ClientConfigImpl(std::string group_name) + : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)), long_polling_timeout_(absl::Seconds(30)) { + instance_name_ = "DEFAULT"; +} + +std::string ClientConfigImpl::clientId() const { + std::stringstream ss; + ss << UtilAll::hostname(); + ss << "@"; + std::string processID = std::to_string(getpid()); + ss << (instance_name_ == "DEFAULT" ? processID : instance_name_); + return ss.str(); +} + +const std::string& ClientConfigImpl::getInstanceName() const { return instance_name_; } + +void ClientConfigImpl::setInstanceName(std::string instance_name) { instance_name_ = std::move(instance_name); } + +const std::string& ClientConfigImpl::getGroupName() const { return group_name_; } + +void ClientConfigImpl::setGroupName(std::string group_name) { group_name_ = std::move(group_name); } + +void ClientConfigImpl::setIoTimeout(absl::Duration timeout) { io_timeout_ = timeout; } + +void ClientConfigImpl::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { + credentials_provider_ = std::move(credentials_provider); +} + +CredentialsProviderPtr ClientConfigImpl::credentialsProvider() { return credentials_provider_; } + +absl::Duration ClientConfigImpl::getIoTimeout() const { return io_timeout_; } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManager.cpp b/src/main/cpp/client/ClientManager.cpp deleted file mode 100644 index 5ff9c7e9a..000000000 --- a/src/main/cpp/client/ClientManager.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "ClientManager.h" - -ROCKETMQ_NAMESPACE_BEGIN - -ClientManager& ClientManager::getInstance() { - static ClientManager instance; - return instance; -} - -ClientInstancePtr ClientManager::getClientInstance(const ClientConfig& client_config) { - { - absl::MutexLock lock(&client_instance_table_mtx_); - auto search = client_instance_table_.find(client_config.arn()); - if (search != client_instance_table_.end()) { - ClientInstancePtr client_instance = search->second.lock(); - if (client_instance) { - SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.arn()); - return client_instance; - } else { - client_instance_table_.erase(client_config.arn()); - } - } - ClientInstancePtr client_instance = std::make_shared(client_config.arn()); - std::weak_ptr client_instance_weak_ptr(client_instance); - client_instance_table_.insert_or_assign(client_config.arn(), client_instance_weak_ptr); - SPDLOG_INFO("Created new client_instance[ARN={}]", client_config.arn()); - return client_instance; - } -} - -void ClientManager::addClientInstance(const std::string& arn, const ClientInstancePtr& client_instance) { - absl::MutexLock lk(&client_instance_table_mtx_); - client_instance_table_.insert_or_assign(arn, std::weak_ptr(client_instance)); -} - -ClientManager::ClientManager() : admin_server_(admin::AdminFacade::getServer()) { admin_server_.start(); } - -ClientManager::~ClientManager() { admin_server_.stop(); } - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp new file mode 100644 index 000000000..05515df28 --- /dev/null +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -0,0 +1,41 @@ +#include "ClientManagerFactory.h" +#include "ClientManagerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ClientManagerFactory& ClientManagerFactory::getInstance() { + static ClientManagerFactory instance; + return instance; +} + +ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& client_config) { + { + absl::MutexLock lock(&client_manager_table_mtx_); + auto search = client_manager_table_.find(client_config.arn()); + if (search != client_manager_table_.end()) { + ClientManagerPtr client_instance = search->second.lock(); + if (client_instance) { + SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.arn()); + return client_instance; + } else { + client_manager_table_.erase(client_config.arn()); + } + } + ClientManagerPtr client_instance = std::make_shared(client_config.arn()); + std::weak_ptr client_instance_weak_ptr(client_instance); + client_manager_table_.insert_or_assign(client_config.arn(), client_instance_weak_ptr); + SPDLOG_INFO("Created new client_instance[ARN={}]", client_config.arn()); + return client_instance; + } +} + +void ClientManagerFactory::addClientInstance(const std::string& arn, const ClientManagerPtr& client_manager) { + absl::MutexLock lk(&client_manager_table_mtx_); + client_manager_table_.insert_or_assign(arn, std::weak_ptr(client_manager)); +} + +ClientManagerFactory::ClientManagerFactory() : admin_server_(admin::AdminFacade::getServer()) { admin_server_.start(); } + +ClientManagerFactory::~ClientManagerFactory() { admin_server_.stop(); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientInstance.cpp b/src/main/cpp/client/ClientManagerImpl.cpp similarity index 83% rename from src/main/cpp/client/ClientInstance.cpp rename to src/main/cpp/client/ClientManagerImpl.cpp index ef4293c4b..1339fe8d6 100644 --- a/src/main/cpp/client/ClientInstance.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1,4 +1,4 @@ -#include "ClientInstance.h" +#include "ClientManagerImpl.h" #include #include @@ -11,7 +11,7 @@ #include "LogInterceptorFactory.h" #include "LoggerImpl.h" #include "MessageAccessor.h" -#include "Metadata.h" +#include "MetadataConstants.h" #include "MixAll.h" #include "Partition.h" #include "Protocol.h" @@ -28,7 +28,7 @@ ROCKETMQ_NAMESPACE_BEGIN -ClientInstance::ClientInstance(std::string arn) +ClientManagerImpl::ClientManagerImpl(std::string arn) : arn_(std::move(arn)), state_(State::CREATED), completion_queue_(std::make_shared()), callback_thread_pool_(grpc::CreateDefaultThreadPool()), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); @@ -52,19 +52,19 @@ ClientInstance::ClientInstance(std::string arn) SPDLOG_INFO("ClientInstance[ARN={}] created", arn_); } -ClientInstance::~ClientInstance() { +ClientManagerImpl::~ClientManagerImpl() { shutdown(); SPDLOG_INFO("ClientInstance[ARN={}] destructed", arn_); } -void ClientInstance::start() { +void ClientManagerImpl::start() { if (State::CREATED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); return; } state_.store(State::STARTING, std::memory_order_relaxed); - std::weak_ptr client_instance_weak_ptr = shared_from_this(); + std::weak_ptr client_instance_weak_ptr = shared_from_this(); auto health_check_functor = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); @@ -83,7 +83,7 @@ void ClientInstance::start() { heartbeat_handle_ = scheduler_.schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); - completion_queue_thread_ = std::thread(std::bind(&ClientInstance::pollCompletionQueue, this)); + completion_queue_thread_ = std::thread(std::bind(&ClientManagerImpl::pollCompletionQueue, this)); auto stats_functor_ = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); @@ -96,7 +96,7 @@ void ClientInstance::start() { state_.store(State::STARTED, std::memory_order_relaxed); } -void ClientInstance::shutdown() { +void ClientManagerImpl::shutdown() { SPDLOG_DEBUG("Client instance shutdown"); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); @@ -132,7 +132,7 @@ void ClientInstance::shutdown() { SPDLOG_DEBUG("Client instance stopped"); } -void ClientInstance::assignLabels(Histogram& histogram) { +void ClientManagerImpl::assignLabels(Histogram& histogram) { histogram.labels().emplace_back("[000ms~020ms): "); histogram.labels().emplace_back("[020ms~040ms): "); histogram.labels().emplace_back("[040ms~060ms): "); @@ -146,9 +146,9 @@ void ClientInstance::assignLabels(Histogram& histogram) { histogram.labels().emplace_back("[200ms~inf): "); } -void ClientInstance::healthCheck( - const std::string& target_host, const absl::flat_hash_map& metadata, - const HealthCheckRequest& request, std::chrono::milliseconds timeout, +void ClientManagerImpl::healthCheck( + const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, const std::function*)>& cb) { { absl::MutexLock lk(&rpc_clients_mtx_); @@ -176,7 +176,7 @@ void ClientInstance::healthCheck( client->asyncHealthCheck(request, invocation_context); } -void ClientInstance::doHealthCheck() { +void ClientManagerImpl::doHealthCheck() { SPDLOG_DEBUG("Start to perform health check for inactive clients"); if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { @@ -186,7 +186,7 @@ void ClientInstance::doHealthCheck() { cleanOfflineRpcClients(); - std::vector> clients; + std::vector> clients; { absl::MutexLock lk(&clients_mtx_); for (auto& item : clients_) { @@ -276,12 +276,12 @@ nostd::shared_ptr ClientInstance::getTracer() { #endif -void ClientInstance::cleanOfflineRpcClients() { +void ClientManagerImpl::cleanOfflineRpcClients() { absl::flat_hash_set hosts; { absl::MutexLock lk(&clients_mtx_); for (const auto& item : clients_) { - std::shared_ptr client = item.lock(); + std::shared_ptr client = item.lock(); if (!client) { continue; } @@ -303,10 +303,9 @@ void ClientInstance::cleanOfflineRpcClients() { } } -void ClientInstance::heartbeat(const std::string& target_host, - const absl::flat_hash_map& metadata, - const HeartbeatRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata& metadata, + const HeartbeatRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { auto client = getRpcClient(target_host, true); if (!client) { return; @@ -340,19 +339,19 @@ void ClientInstance::heartbeat(const std::string& target_host, client->asyncHeartbeat(request, invocation_context); } -bool ClientInstance::active() { +bool ClientManagerImpl::active() { State state = state_.load(std::memory_order_relaxed); return State::STARTED == state || State::STARTING == state; } -void ClientInstance::doHeartbeat() { +void ClientManagerImpl::doHeartbeat() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); return; } - std::vector> clients; + std::vector> clients; { absl::MutexLock lk(&clients_mtx_); for (const auto& item : clients_) { @@ -368,7 +367,7 @@ void ClientInstance::doHeartbeat() { } } -void ClientInstance::pollCompletionQueue() { +void ClientManagerImpl::pollCompletionQueue() { while (State::STARTED == state_.load(std::memory_order_relaxed) || State::STARTING == state_.load(std::memory_order_relaxed)) { @@ -388,8 +387,8 @@ void ClientInstance::pollCompletionQueue() { SPDLOG_INFO("pollCompletionQueue completed and quit"); } -bool ClientInstance::send(const std::string& target_host, const absl::flat_hash_map& metadata, - SendMessageRequest& request, SendCallback* cb) { +bool ClientManagerImpl::send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { assert(cb); SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); RpcClientSharedPtr client = getRpcClient(target_host); @@ -482,7 +481,7 @@ bool ClientInstance::send(const std::string& target_host, const absl::flat_hash_ return true; } -RpcClientSharedPtr ClientInstance::getRpcClient(const std::string& target_host, bool need_heartbeat) { +RpcClientSharedPtr ClientManagerImpl::getRpcClient(const std::string& target_host, bool need_heartbeat) { std::shared_ptr client; { absl::MutexLock lock(&rpc_clients_mtx_); @@ -511,20 +510,20 @@ RpcClientSharedPtr ClientInstance::getRpcClient(const std::string& target_host, return client; } -void ClientInstance::addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) { +void ClientManagerImpl::addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) { { absl::MutexLock lock(&rpc_clients_mtx_); rpc_clients_.insert_or_assign(target_host, client); } } -void ClientInstance::cleanRpcClients() { +void ClientManagerImpl::cleanRpcClients() { absl::MutexLock lk(&rpc_clients_mtx_); rpc_clients_.clear(); } -SendResult ClientInstance::processSendResponse(const MQMessageQueue& message_queue, - const SendMessageResponse& response) { +SendResult ClientManagerImpl::processSendResponse(const MQMessageQueue& message_queue, + const SendMessageResponse& response) { if (google::rpc::Code::OK != response.common().status().code()) { THROW_MQ_EXCEPTION(MQClientException, response.common().DebugString(), response.common().status().code()); } @@ -537,15 +536,14 @@ SendResult ClientInstance::processSendResponse(const MQMessageQueue& message_que return send_result; } -void ClientInstance::addClientObserver(std::weak_ptr client) { +void ClientManagerImpl::addClientObserver(std::weak_ptr client) { absl::MutexLock lk(&clients_mtx_); clients_.emplace_back(std::move(client)); } -void ClientInstance::resolveRoute(const std::string& target_host, - const absl::flat_hash_map& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { RpcClientSharedPtr client = getRpcClient(target_host, false); if (!client) { @@ -632,10 +630,9 @@ void ClientInstance::resolveRoute(const std::string& target_host, client->asyncQueryRoute(request, invocation_context); } -void ClientInstance::queryAssignment(const std::string& target, - const absl::flat_hash_map& metadata, - const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::queryAssignment(const std::string& target, const Metadata& metadata, + const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { SPDLOG_DEBUG("Prepare to send query assignment request to broker[address={}]", target); std::shared_ptr client = getRpcClient(target); @@ -666,10 +663,9 @@ void ClientInstance::queryAssignment(const std::string& target, client->asyncQueryAssignment(request, invocation_context); } -void ClientInstance::receiveMessage(const std::string& target_host, - const absl::flat_hash_map& metadata, - const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - std::shared_ptr& cb) { +void ClientManagerImpl::receiveMessage(const std::string& target_host, const Metadata& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + std::shared_ptr& cb) { SPDLOG_DEBUG("Prepare to pop message from {} asynchronously. Request: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); @@ -702,41 +698,9 @@ void ClientInstance::receiveMessage(const std::string& target_host, client->asyncReceive(request, invocation_context); } -void ClientInstance::pullMessage(const std::string& target_host, - absl::flat_hash_map& metadata, - const PullMessageRequest& request, std::shared_ptr& cb) { - SPDLOG_DEBUG("Prepare to pull message from {} asynchronously", target_host); - RpcClientSharedPtr client = getRpcClient(target_host); - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = target_host; - if (!metadata.empty()) { - for (const auto& entry : metadata) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - } - - auto callback = [this, cb](const InvocationContext* invocation_context) { - if (invocation_context->status.ok()) { - SPDLOG_DEBUG("Received message response through gRPC from brokerAddress={}", invocation_context->remote_address); - ReceiveMessageResult receive_result; - this->processPullResult(invocation_context->context, invocation_context->response, receive_result, - invocation_context->remote_address); - cb->onSuccess(receive_result); - } else { - SPDLOG_WARN("Failed to pull messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", - invocation_context->remote_address, invocation_context->status.error_code(), - invocation_context->status.error_message()); - MQException e(invocation_context->status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, - __LINE__); - cb->onException(e); - } - }; - invocation_context->callback = callback; - client->asyncPull(request, invocation_context); -} - -void ClientInstance::processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host) { +void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_context, + const ReceiveMessageResponse& response, ReceiveMessageResult& result, + const std::string& target_host) { // process response to result ReceiveMessageStatus status; switch (response.common().status().code()) { @@ -788,8 +752,9 @@ void ClientInstance::processPopResult(const grpc::ClientContext& client_context, result.setMsgFoundList(msg_found_list); } -void ClientInstance::processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host) { +void ClientManagerImpl::processPullResult(const grpc::ClientContext& client_context, + const PullMessageResponse& response, ReceiveMessageResult& result, + const std::string& target_host) { if (google::rpc::Code::OK != response.common().status().code()) { result.status_ = ReceiveMessageStatus::INTERNAL; return; @@ -824,7 +789,7 @@ void ClientInstance::processPullResult(const grpc::ClientContext& client_context } } -bool ClientInstance::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { +bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { assert(item.topic().arn() == arn_); // base @@ -1006,8 +971,8 @@ bool ClientInstance::wrapMessage(const rmq::Message& item, MQMessageExt& message MessageAccessor::setInvisiblePeriod(message_ext, invisible_period); } - // Delivery Count - MessageAccessor::setDeliveryCount(message_ext, system_attributes.delivery_count()); + // Delivery attempt + MessageAccessor::setDeliveryAttempt(message_ext, system_attributes.delivery_attempt()); // Trace-context MessageAccessor::setTraceContext(message_ext, system_attributes.trace_context()); @@ -1032,13 +997,12 @@ bool ClientInstance::wrapMessage(const rmq::Message& item, MQMessageExt& message return true; } -Scheduler& ClientInstance::getScheduler() { return scheduler_; } +Scheduler& ClientManagerImpl::getScheduler() { return scheduler_; } -TopAddressing& ClientInstance::topAddressing() { return top_addressing_; } +TopAddressing& ClientManagerImpl::topAddressing() { return top_addressing_; } -void ClientInstance::ack(const std::string& target, const absl::flat_hash_map& metadata, - const AckMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { std::string target_host(target.data(), target.length()); SPDLOG_DEBUG("Prepare to ack message against {} asynchronously. AckMessageRequest: {}", target_host, request.DebugString()); @@ -1065,9 +1029,9 @@ void ClientInstance::ack(const std::string& target, const absl::flat_hash_mapasyncAck(request, invocation_context); } -void ClientInstance::nack(const std::string& target_host, const absl::flat_hash_map& metadata, - const NackMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& completion_callback) { +void ClientManagerImpl::nack(const std::string& target_host, const Metadata& metadata, + const NackMessageRequest& request, std::chrono::milliseconds timeout, + const std::function& completion_callback) { RpcClientSharedPtr client = getRpcClient(target_host); assert(client); auto invocation_context = new InvocationContext(); @@ -1090,10 +1054,9 @@ void ClientInstance::nack(const std::string& target_host, const absl::flat_hash_ client->asyncNack(request, invocation_context); } -void ClientInstance::endTransaction(const std::string& target_host, - const absl::flat_hash_map& metadata, - const EndTransactionRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::endTransaction(const std::string& target_host, const Metadata& metadata, + const EndTransactionRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { RpcClientSharedPtr client = getRpcClient(target_host); if (!client) { SPDLOG_WARN("No RPC client for {}", target_host); @@ -1130,10 +1093,9 @@ void ClientInstance::endTransaction(const std::string& target_host, client->asyncEndTransaction(request, invocation_context); } -void ClientInstance::multiplexingCall(const std::string& target_host, - const absl::flat_hash_map& metadata, - const MultiplexingRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { +void ClientManagerImpl::multiplexingCall( + const std::string& target_host, const Metadata& metadata, const MultiplexingRequest& request, + std::chrono::milliseconds timeout, const std::function*)>& cb) { RpcClientSharedPtr client = getRpcClient(target_host); if (!client) { SPDLOG_WARN("No RPC client for {}", target_host); @@ -1169,47 +1131,98 @@ void ClientInstance::multiplexingCall(const std::string& target_host, client->asyncMultiplexingCall(request, invocation_context); } -void ClientInstance::pullMessage(const std::string& target_host, - const absl::flat_hash_map& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::queryOffset(const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { auto client = getRpcClient(target_host); if (!client) { - PullMessageResponse response; - cb(false, response); + SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + auto callback = [cb](const InvocationContext* invocation_context) { + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", invocation_context->remote_address, + invocation_context->status.error_message()); + cb(false, invocation_context->response); + return; + } + + if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { + SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", + invocation_context->remote_address, invocation_context->response.common().DebugString()); + cb(false, invocation_context->response); + } + + SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); + cb(true, invocation_context->response); + }; + invocation_context->callback = callback; + client->asyncQueryOffset(request, invocation_context); +} + +void ClientManagerImpl::pullMessage(const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + auto client = getRpcClient(target_host); + if (!client) { + cb(nullptr); return; } auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + invocation_context->context.AddMetadata(item.first, item.second); + } + invocation_context->callback = cb; + client->asyncPull(request, invocation_context); +} + +void ClientManagerImpl::forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) { + auto client = getRpcClient(target_host); + if (!client) { + cb(nullptr); + return; + } + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target_host; + invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [cb](const InvocationContext* invocation_context) { + auto callback = [cb](const InvocationContext* invocation_context) { if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to send pullMessage request to {}", invocation_context->remote_address); - cb(false, invocation_context->response); + SPDLOG_WARN("Failed to transmit SendMessageToDeadLetterQueueRequest to {}", invocation_context->remote_address); + cb(invocation_context); return; } - SPDLOG_DEBUG("Received pullMessage response from server[host={}]", invocation_context->remote_address); - cb(true, invocation_context->response); + SPDLOG_DEBUG("Received forwardToDeadLetterQueue response from server[host={}]", invocation_context->remote_address); + cb(invocation_context); }; invocation_context->callback = callback; - - client->asyncPull(request, invocation_context); + client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); } -void ClientInstance::logStats() { +void ClientManagerImpl::logStats() { std::string stats; latency_histogram_.reportAndReset(stats); SPDLOG_INFO("{}", stats); } -const char* ClientInstance::HEARTBEAT_TASK_NAME = "heartbeat-task"; -const char* ClientInstance::STATS_TASK_NAME = "stats-task"; -const char* ClientInstance::HEALTH_CHECK_TASK_NAME = "health-check-task"; +const char* ClientManagerImpl::HEARTBEAT_TASK_NAME = "heartbeat-task"; +const char* ClientManagerImpl::STATS_TASK_NAME = "stats-task"; +const char* ClientManagerImpl::HEALTH_CHECK_TASK_NAME = "health-check-task"; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 70a7ad959..7cb89a489 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -2,7 +2,7 @@ #include -#include "ClientConfig.h" +#include "ClientConfigImpl.h" #include "TlsHelper.h" #include "absl/time/time.h" @@ -142,4 +142,14 @@ void RpcClientImpl::asyncPull(const PullMessageRequest& request, invocation_context); } +void RpcClientImpl::asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) { + invocation_context->response_reader = stub_->PrepareAsyncForwardMessageToDeadLetterQueue( + &invocation_context->context, request, completion_queue_.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index 805d7627a..250692d2c 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -1,5 +1,6 @@ #include "Signature.h" -#include "Metadata.h" +#include "ClientConfigImpl.h" +#include "MetadataConstants.h" #include "Protocol.h" #include "TlsHelper.h" #include "spdlog/spdlog.h" @@ -9,23 +10,23 @@ ROCKETMQ_NAMESPACE_BEGIN void Signature::sign(ClientConfig* client, absl::flat_hash_map& metadata) { assert(client); - metadata.insert({Metadata::LANGUAGE_KEY, "CPP"}); + metadata.insert({MetadataConstants::LANGUAGE_KEY, "CPP"}); // Add common headers - metadata.insert({Metadata::CLIENT_VERSION_KEY, ClientConfig::CLIENT_VERSION}); - metadata.insert({Metadata::PROTOCOL_VERSION_KEY, Protocol::PROTOCOL_VERSION}); + metadata.insert({MetadataConstants::CLIENT_VERSION_KEY, ClientConfigImpl::CLIENT_VERSION}); + metadata.insert({MetadataConstants::PROTOCOL_VERSION_KEY, Protocol::PROTOCOL_VERSION}); if (!client->tenantId().empty()) { - metadata.insert({Metadata::TENANT_ID_KEY, client->tenantId()}); + metadata.insert({MetadataConstants::TENANT_ID_KEY, client->tenantId()}); } if (!client->arn().empty()) { - metadata.insert({Metadata::ARN_KEY, client->arn()}); + metadata.insert({MetadataConstants::ARN_KEY, client->arn()}); } absl::Time now = absl::Now(); absl::TimeZone utc_time_zone = absl::UTCTimeZone(); - const std::string request_date_time = absl::FormatTime(Metadata::DATE_TIME_FORMAT, now, utc_time_zone); - metadata.insert({Metadata::DATE_TIME_KEY, request_date_time}); + const std::string request_date_time = absl::FormatTime(MetadataConstants::DATE_TIME_FORMAT, now, utc_time_zone); + metadata.insert({MetadataConstants::DATE_TIME_KEY, request_date_time}); if (client->credentialsProvider()) { Credentials&& credentials = client->credentialsProvider()->getCredentials(); @@ -35,9 +36,9 @@ void Signature::sign(ClientConfig* client, absl::flat_hash_mapserviceName()) .append(", ") - .append(Metadata::SIGNED_HEADERS_KEY) + .append(MetadataConstants::SIGNED_HEADERS_KEY) .append("=") - .append(Metadata::DATE_TIME_KEY) + .append(MetadataConstants::DATE_TIME_KEY) .append(", ") - .append(Metadata::SIGNATURE_KEY) + .append(MetadataConstants::SIGNATURE_KEY) .append("=") .append(TlsHelper::sign(credentials.accessSecret(), request_date_time)); SPDLOG_DEBUG("Add authorization header: {}", authorization); - metadata.insert({Metadata::AUTHORIZATION, authorization}); + metadata.insert({MetadataConstants::AUTHORIZATION, authorization}); } } diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index 2dd624f33..ddc67f7d1 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -1,15 +1,11 @@ #pragma once #include "rocketmq/MQMessageQueue.h" +#include "ConsumeMessageType.h" #include #include -namespace rocketmq { - -enum ConsumeMessageType { - PULL = 0, - POP = 1, -}; +ROCKETMQ_NAMESPACE_BEGIN class Assignment { public: @@ -44,5 +40,4 @@ class Assignment { MQMessageQueue message_queue_; ConsumeMessageType consume_type_; }; - -} // namespace rocketmq \ No newline at end of file +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientCallback.h b/src/main/cpp/client/include/Client.h similarity index 64% rename from src/main/cpp/client/include/ClientCallback.h rename to src/main/cpp/client/include/Client.h index 7cd0a4a62..847dac9e9 100644 --- a/src/main/cpp/client/include/ClientCallback.h +++ b/src/main/cpp/client/include/Client.h @@ -1,11 +1,14 @@ +#pragma once + +#include "ClientConfig.h" #include "absl/container/flat_hash_set.h" -#include "rocketmq/RocketMQ.h" +#include ROCKETMQ_NAMESPACE_BEGIN -class ClientCallback { +class Client : virtual public ClientConfig { public: - virtual ~ClientCallback() = default; + ~Client() override = default; virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; @@ -19,6 +22,9 @@ class ClientCallback { * workload. */ virtual void healthCheck() = 0; + + virtual void schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientApi.h b/src/main/cpp/client/include/ClientApi.h deleted file mode 100644 index d39402315..000000000 --- a/src/main/cpp/client/include/ClientApi.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -#include "RpcClient.h" -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" - -namespace rocketmq { -class ClientApi { -public: - ClientApi() = default; - - void update_name_server_list(const std::vector& list) LOCKS_EXCLUDED(name_server_list_mtx_); - -private: - std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); - absl::Mutex name_server_list_mtx_; // Protects name_server_list_ -}; -} // namespace rocketmq \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 028ad90cd..0e71cb271 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -1,99 +1,33 @@ #pragma once -#include "Identifiable.h" -#include "absl/strings/string_view.h" +#include "rocketmq/CredentialsProvider.h" #include "absl/time/time.h" #include "rocketmq/RocketMQ.h" -#include -#include -#include #include -#include ROCKETMQ_NAMESPACE_BEGIN -class ClientConfig : public Identifiable { +class ClientConfig { public: - ClientConfig(); + virtual ~ClientConfig() = default; - explicit ClientConfig(std::string group_name); + virtual const std::string& region() const = 0; - ~ClientConfig() override = default; + virtual const std::string& serviceName() const = 0; - const std::string& arn() const { return arn_; } + virtual const std::string& arn() const = 0; - void arn(absl::string_view arn) { arn_ = std::string(arn.data(), arn.length()); } + virtual CredentialsProviderPtr credentialsProvider() = 0; - std::string clientId() const; + virtual const std::string& tenantId() const = 0; - const std::string& getInstanceName() const; + virtual absl::Duration getIoTimeout() const = 0; - void setInstanceName(std::string instance_name); + virtual absl::Duration getLongPollingTimeout() const = 0; - const std::string& getGroupName() const; - void setGroupName(std::string group_name); + virtual const std::string& getGroupName() const = 0; - const std::string& getUnitName() const { return unit_name_; } - void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } - - absl::Duration getIoTimeout() const; - void setIoTimeout(absl::Duration timeout); - - absl::Duration getLongPollingTimeout() const { return long_polling_timeout_; } - - void setLongPollingTimeout(absl::Duration timeout) { long_polling_timeout_ = timeout; } - - bool isTracingEnabled() { return enable_tracing_.load(); } - void enableTracing(bool enabled) { enable_tracing_.store(enabled); } - - CredentialsProviderPtr credentialsProvider() override; - void setCredentialsProvider(CredentialsProviderPtr credentials_provider); - - void serviceName(std::string service_name) { service_name_ = std::move(service_name); } - const std::string& serviceName() const { return service_name_; } - - void region(std::string region) { region_ = std::move(region); } - const std::string& region() const { return region_; } - - void tenantId(std::string tenant_id) { tenant_id_ = std::move(tenant_id); } - const std::string& tenantId() const override { return tenant_id_; } - - static const char* CLIENT_VERSION; - -protected: - /** - * Name of the service. - */ - std::string service_name_{"ONS"}; - - /** - * Region of the service to connect to. - */ - std::string region_; - - /** - * Abstract Resource Namespace, in which topic/group_name remain unique. - */ - std::string arn_; - - /** - * Tenant identifier. - */ - std::string tenant_id_; - - std::string instance_name_; - - std::string group_name_; - - std::string unit_name_; - - CredentialsProviderPtr credentials_provider_; - - absl::Duration io_timeout_; - - absl::Duration long_polling_timeout_; - - std::atomic enable_tracing_{false}; + virtual std::string clientId() const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h new file mode 100644 index 000000000..a15444cea --- /dev/null +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -0,0 +1,99 @@ +#pragma once + +#include "ClientConfig.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientConfigImpl : virtual public ClientConfig { +public: + ClientConfigImpl(); + + explicit ClientConfigImpl(std::string group_name); + + ~ClientConfigImpl() override = default; + + const std::string& arn() const override { return arn_; } + + void arn(absl::string_view arn) { arn_ = std::string(arn.data(), arn.length()); } + + std::string clientId() const override; + + const std::string& getInstanceName() const; + + void setInstanceName(std::string instance_name); + + const std::string& getGroupName() const override; + void setGroupName(std::string group_name); + + const std::string& getUnitName() const { return unit_name_; } + void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } + + absl::Duration getIoTimeout() const override; + void setIoTimeout(absl::Duration timeout); + + absl::Duration getLongPollingTimeout() const override { return long_polling_timeout_; } + + void setLongPollingTimeout(absl::Duration timeout) { long_polling_timeout_ = timeout; } + + bool isTracingEnabled() { return enable_tracing_.load(); } + void enableTracing(bool enabled) { enable_tracing_.store(enabled); } + + CredentialsProviderPtr credentialsProvider() override; + void setCredentialsProvider(CredentialsProviderPtr credentials_provider); + + void serviceName(std::string service_name) { service_name_ = std::move(service_name); } + const std::string& serviceName() const override { return service_name_; } + + void region(std::string region) { region_ = std::move(region); } + const std::string& region() const override { return region_; } + + void tenantId(std::string tenant_id) { tenant_id_ = std::move(tenant_id); } + const std::string& tenantId() const override { return tenant_id_; } + + static const char* CLIENT_VERSION; + +protected: + /** + * Name of the service. + */ + std::string service_name_{"ONS"}; + + /** + * Region of the service to connect to. + */ + std::string region_; + + /** + * Abstract Resource Namespace, in which topic/group_name remain unique. + */ + std::string arn_; + + /** + * Tenant identifier. + */ + std::string tenant_id_; + + std::string instance_name_; + + std::string group_name_; + + std::string unit_name_; + + CredentialsProviderPtr credentials_provider_; + + absl::Duration io_timeout_; + + absl::Duration long_polling_timeout_; + + std::atomic enable_tracing_{false}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index b3021debb..513054543 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -1,40 +1,87 @@ #pragma once -#include -#include - -#include "ClientConfig.h" -#include "ClientInstance.h" -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/synchronization/mutex.h" -#include "rocketmq/AdminServer.h" +#include "Client.h" +#include "ReceiveMessageCallback.h" +#include "Scheduler.h" +#include "TopAddressing.h" +#include "TopicRouteData.h" +#include "rocketmq/MQMessageExt.h" +#include ROCKETMQ_NAMESPACE_BEGIN +using Metadata = absl::flat_hash_map; + class ClientManager { public: - static ClientManager& getInstance(); + virtual ~ClientManager() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual Scheduler& getScheduler() = 0; + + virtual TopAddressing& topAddressing() = 0; + + virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; - ClientInstancePtr getClientInstance(const ClientConfig& client_config) LOCKS_EXCLUDED(client_instance_table_mtx_); + virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; - // For test purpose only - void addClientInstance(const std::string& arn, const ClientInstancePtr& client_instance) - LOCKS_EXCLUDED(client_instance_table_mtx_); + virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) = 0; -private: - ClientManager(); + virtual void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& callback) = 0; - virtual ~ClientManager(); + virtual void forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; - /** - * Client Id --> Client Instance - */ - absl::flat_hash_map> - client_instance_table_ GUARDED_BY(client_instance_table_mtx_); - absl::Mutex client_instance_table_mtx_; // protects client_instance_table_ + virtual void endTransaction(const std::string& target_host, const Metadata& metadata, + const EndTransactionRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) = 0; - rocketmq::admin::AdminServer& admin_server_; + virtual void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void + healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; + + virtual void addClientObserver(std::weak_ptr client) = 0; + + virtual void queryAssignment(const std::string& target, const Metadata& metadata, + const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) = 0; + + virtual void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, + std::chrono::milliseconds timeout, std::shared_ptr& cb) = 0; + + virtual bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) = 0; + + virtual void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, + ReceiveMessageResult& result, const std::string& target_host) = 0; + + virtual void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; }; +using ClientManagerPtr = std::shared_ptr; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h new file mode 100644 index 000000000..dfd746643 --- /dev/null +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "ClientConfig.h" +#include "ClientManager.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/AdminServer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerFactory { +public: + static ClientManagerFactory& getInstance(); + + ClientManagerPtr getClientManager(const ClientConfig& client_config) LOCKS_EXCLUDED(client_manager_table_mtx_); + + // For test purpose only + void addClientInstance(const std::string& arn, const ClientManagerPtr& client_manager) + LOCKS_EXCLUDED(client_manager_table_mtx_); + +private: + ClientManagerFactory(); + + virtual ~ClientManagerFactory(); + + /** + * Client Id --> Client Instance + */ + absl::flat_hash_map> + client_manager_table_ GUARDED_BY(client_manager_table_mtx_); + absl::Mutex client_manager_table_mtx_; // protects client_manager_table_ + + rocketmq::admin::AdminServer& admin_server_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientInstance.h b/src/main/cpp/client/include/ClientManagerImpl.h similarity index 55% rename from src/main/cpp/client/include/ClientInstance.h rename to src/main/cpp/client/include/ClientManagerImpl.h index 48ec920f2..8b9ae3c9f 100644 --- a/src/main/cpp/client/include/ClientInstance.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -4,15 +4,13 @@ #include #include #include -#include #include #include -#include "ClientCallback.h" -#include "ClientConfig.h" +#include "Client.h" +#include "ClientManager.h" #include "HeartbeatDataCallback.h" #include "Histogram.h" -#include "Identifiable.h" #include "InvocationContext.h" #include "OrphanTransactionCallback.h" #include "ReceiveMessageCallback.h" @@ -42,15 +40,20 @@ ROCKETMQ_NAMESPACE_BEGIN -class ClientInstance : public std::enable_shared_from_this { +class ClientManagerImpl : virtual public ClientManager, public std::enable_shared_from_this { public: - explicit ClientInstance(std::string arn); + /** + * @brief Construct a new Client Manager Impl object + * TODO: Make it protected such that instantiating it through ClientManagerFactory only, achieving Singleton effectively. + * @param arn Abstract resource namespace, in which this client manager lives. + */ + explicit ClientManagerImpl(std::string arn); - ~ClientInstance(); + ~ClientManagerImpl() override; - void start(); + void start() override; - void shutdown() LOCKS_EXCLUDED(rpc_clients_mtx_); + void shutdown() override LOCKS_EXCLUDED(rpc_clients_mtx_); static void assignLabels(Histogram& histogram); @@ -63,9 +66,10 @@ class ClientInstance : public std::enable_shared_from_this { * @param timeout RPC timeout. * @param cb Callback to execute once the request/response completes. */ - void resolveRoute(const std::string& target_host, const absl::flat_hash_map& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override + LOCKS_EXCLUDED(rpc_clients_mtx_); void doHealthCheck() LOCKS_EXCLUDED(clients_mtx_); @@ -78,13 +82,14 @@ class ClientInstance : public std::enable_shared_from_this { /** * Execute health-check on behalf of the client. */ - void healthCheck(const std::string& target_host, const absl::flat_hash_map& metadata, - const HealthCheckRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) + void + healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); - bool send(const std::string& target_host, const absl::flat_hash_map& metadata, - SendMessageRequest& request, SendCallback* cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); /** * Get a RpcClient according to the given target hosts, which follows scheme specified @@ -108,18 +113,14 @@ class ClientInstance : public std::enable_shared_from_this { // Test purpose only void cleanRpcClients() LOCKS_EXCLUDED(rpc_clients_mtx_); - void addClientObserver(std::weak_ptr client); - - void queryAssignment(const std::string& target, const absl::flat_hash_map& metadata, - const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, - const std::function& cb); + void addClientObserver(std::weak_ptr client) override; - void receiveMessage(const std::string& target, const absl::flat_hash_map& metadata, - const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - std::shared_ptr& cb) LOCKS_EXCLUDED(rpc_clients_mtx_); + void queryAssignment(const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; - void pullMessage(const std::string& target, absl::flat_hash_map& metadata, - const PullMessageRequest& request, std::shared_ptr& cb) + void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, + std::chrono::milliseconds timeout, std::shared_ptr& cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); /** @@ -129,23 +130,27 @@ class ClientInstance : public std::enable_shared_from_this { * @param message_ext * @return true if the translation succeeded; false if something wrong happens, including checksum verification, etc. */ - bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext); + bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) override; - Scheduler& getScheduler(); + Scheduler& getScheduler() override; - TopAddressing& topAddressing(); + TopAddressing& topAddressing() override; /** * Ack message asynchronously. * @param target_host Target broker host address. * @param request Ack message request. */ - void ack(const std::string& target_host, const absl::flat_hash_map& metadata, - const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb); + void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) override; - void nack(const std::string& target_host, const absl::flat_hash_map& metadata, - const NackMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& callback); + void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& callback) override; + + void forwardMessageToDeadLetterQueue( + const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; /** * End a transaction asynchronously. @@ -161,50 +166,21 @@ class ClientInstance : public std::enable_shared_from_this { * @param timeout * @param cb */ - void endTransaction(const std::string& target_host, const absl::flat_hash_map& metadata, - const EndTransactionRequest& request, std::chrono::milliseconds timeout, - const std::function& cb); - - void multiplexingCall(const std::string& target, const absl::flat_hash_map& metadata, - const MultiplexingRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb); - - template - void queryOffset(const std::string& target_host, const absl::flat_hash_map& metadata, - const QueryOffsetRequest& request, std::chrono::milliseconds timeout, const Callable& cb) { - auto client = getRpcClient(target_host); - if (!client) { - SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); - return; - } - - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = target_host; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - auto callback = [cb](const InvocationContext* invocation_context) { - if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", invocation_context->remote_address, - invocation_context->status.error_message()); - cb(false, invocation_context->response); - return; - } - - if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", - invocation_context->remote_address, invocation_context->response.common().DebugString()); - cb(false, invocation_context->response); - } - - SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); - cb(true, invocation_context->response); - }; - invocation_context->callback = callback; - client->asyncQueryOffset(request, invocation_context); - } - - void pullMessage(const std::string& target_host, const absl::flat_hash_map& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb); + void endTransaction(const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; + + void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; #ifdef ENABLE_TRACING nostd::shared_ptr getTracer(); @@ -213,17 +189,17 @@ class ClientInstance : public std::enable_shared_from_this { void trace(bool trace) { trace_ = trace; } - void heartbeat(const std::string& target_host, const absl::flat_hash_map& metadata, - const HeartbeatRequest& request, std::chrono::milliseconds timeout, - const std::function& cb); + void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; + + void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, + ReceiveMessageResult& result, const std::string& target_host) override; private: void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, ReceiveMessageResult& result, const std::string& target_host); - void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host); - bool active(); void doHeartbeat(); @@ -246,7 +222,7 @@ class ClientInstance : public std::enable_shared_from_this { std::atomic state_; - std::vector> clients_ GUARDED_BY(clients_mtx_); + std::vector> clients_ GUARDED_BY(clients_mtx_); absl::Mutex clients_mtx_; absl::flat_hash_map> rpc_clients_ GUARDED_BY(rpc_clients_mtx_); @@ -287,6 +263,4 @@ class ClientInstance : public std::enable_shared_from_this { TopAddressing top_addressing_; }; -using ClientInstancePtr = std::shared_ptr; - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h new file mode 100644 index 000000000..b27c347c2 --- /dev/null +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -0,0 +1,14 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" + +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ConsumeMessageType : int8_t { + PULL = 0, + POP = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Identifiable.h b/src/main/cpp/client/include/Identifiable.h deleted file mode 100644 index 965a40f8b..000000000 --- a/src/main/cpp/client/include/Identifiable.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "rocketmq/RocketMQ.h" -#include "rocketmq/CredentialsProvider.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class Identifiable { -public: - virtual ~Identifiable() = default; - - virtual CredentialsProviderPtr credentialsProvider() = 0; - - virtual const std::string& tenantId() const = 0; -}; - -using IdentifiablePtr = std::shared_ptr; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h index 9a21d62d3..51753a710 100644 --- a/src/main/cpp/client/include/Partition.h +++ b/src/main/cpp/client/include/Partition.h @@ -3,6 +3,7 @@ #include "Broker.h" #include "Topic.h" #include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index a7200d67c..026f5fd3d 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -8,7 +8,7 @@ #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN enum class ReceiveMessageStatus : int32_t { /** @@ -124,4 +124,5 @@ struct ReceiveMessageResult { int64_t next_offset_{0}; }; -} // namespace rocketmq + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index b1c24bbe2..cb289b6e2 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "InvocationContext.h" @@ -52,6 +53,8 @@ using QueryOffsetRequest = rmq::QueryOffsetRequest; using QueryOffsetResponse = rmq::QueryOffsetResponse; using PullMessageRequest = rmq::PullMessageRequest; using PullMessageResponse = rmq::PullMessageResponse; +using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; +using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; class RpcClient { public: @@ -97,6 +100,10 @@ class RpcClient { virtual void asyncPull(const PullMessageRequest& request, InvocationContext* invocation_context) = 0; + virtual void + asyncForwardMessageToDeadLetterQueue(const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) = 0; + /** * Indicate if heartbeat is required. * @return true if periodic heartbeat is required; false otherwise. diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index 5008a461e..aa79930b9 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -57,6 +57,10 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; + void asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) override; + bool needHeartbeat() override; void needHeartbeat(bool need_heartbeat) override; diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h index 6e3a585e9..d07e49551 100644 --- a/src/main/cpp/client/include/TopicPublishInfo.h +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -2,13 +2,14 @@ #include -#include "ClientInstance.h" +#include "TopicRouteData.h" +#include "absl/container/flat_hash_set.h" #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "rocketmq/MQMessageQueue.h" -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class TopicPublishInfo { public: @@ -48,4 +49,5 @@ class TopicPublishInfo { }; using TopicPublishInfoPtr = std::shared_ptr; -} // namespace rocketmq + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/CMakeLists.txt b/src/main/cpp/client/mocks/CMakeLists.txt index c6fbe407f..02113513a 100644 --- a/src/main/cpp/client/mocks/CMakeLists.txt +++ b/src/main/cpp/client/mocks/CMakeLists.txt @@ -1,17 +1,9 @@ -add_library(rpc_client_mock RpcClientMock.cpp) -target_include_directories(rpc_client_mock +add_library(client_mocks RpcClientMock.cpp) +target_include_directories(client_mocks INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(rpc_client_mock - gmock - client_library - mq_proto_library) - -add_library(credentials_observable_mock INTERFACE) -target_include_directories(credentials_observable_mock - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(credentials_observable_mock - INTERFACE +target_link_libraries(client_mocks + PUBLIC gmock - client_library) \ No newline at end of file + client_library + mq_proto_library) \ No newline at end of file diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp index ed7b07661..7d85dc92e 100644 --- a/src/main/cpp/client/mocks/RpcClientMock.cpp +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -11,4 +11,4 @@ RpcClientMock::RpcClientMock() : completion_queue_(std::make_shared&)), + (override)); + + MOCK_METHOD(void, heartbeat, + (const std::string&, const Metadata&, const HeartbeatRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, multiplexingCall, + (const std::string&, const Metadata&, const MultiplexingRequest&, std::chrono::milliseconds, + (const std::function*)>&)), + (override)); + + MOCK_METHOD(bool, wrapMessage, (const rmq::Message&, MQMessageExt&), (override)); + + MOCK_METHOD(void, ack, + (const std::string&, const Metadata&, const AckMessageRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, nack, + (const std::string&, const Metadata&, const NackMessageRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, forwardMessageToDeadLetterQueue, + (const std::string&, const Metadata&, const ForwardMessageToDeadLetterQueueRequest&, + std::chrono::milliseconds, + (const std::function*)>&)), + (override)); + + MOCK_METHOD(void, endTransaction, + (const std::string&, const Metadata&, const EndTransactionRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, queryOffset, + (const std::string&, const Metadata&, const QueryOffsetRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, healthCheck, + (const std::string&, const Metadata&, const HealthCheckRequest&, std::chrono::milliseconds, + (const std::function*)>&)), + (override)); + + MOCK_METHOD(void, addClientObserver, (std::weak_ptr), (override)); + + MOCK_METHOD(void, queryAssignment, + (const std::string& target, const Metadata&, const QueryAssignmentRequest&, std::chrono::milliseconds, + (const std::function&)), + (override)); + + MOCK_METHOD(void, receiveMessage, + (const std::string&, const Metadata&, const ReceiveMessageRequest&, std::chrono::milliseconds, + (std::shared_ptr&)), + (override)); + + MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback*), (override)); + + MOCK_METHOD(void, processPullResult, + (const grpc::ClientContext&, const PullMessageResponse&, ReceiveMessageResult&, const std::string&), + (override)); + + MOCK_METHOD(void, pullMessage, + (const std::string&, const Metadata&, const PullMessageRequest&, std::chrono::milliseconds, + (const std::function*)>&)), + (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h new file mode 100644 index 000000000..7ad69b0ae --- /dev/null +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Client.h" +#include "ClientConfigMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientMock : virtual public Client, virtual public ClientConfigMock { +public: + MOCK_METHOD(void, endpointsInUse, (absl::flat_hash_set&), (override)); + + MOCK_METHOD(void, heartbeat, (), (override)); + + MOCK_METHOD(bool, active, (), (override)); + + MOCK_METHOD(void, healthCheck, (), (override)); + + MOCK_METHOD(void, schedule, (const std::string&, const std::function&, std::chrono::milliseconds), + (override)); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/mocks/include/IdentifiableMock.h b/src/main/cpp/client/mocks/include/IdentifiableMock.h deleted file mode 100644 index 473277fae..000000000 --- a/src/main/cpp/client/mocks/include/IdentifiableMock.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Identifiable.h" -#include "gmock/gmock.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class IdentifiableMock : public Identifiable { -public: - ~IdentifiableMock() override = default; - MOCK_METHOD(CredentialsProviderPtr, credentialsProvider, (), (override)); - MOCK_METHOD(const std::string&, tenantId, (), (const override)); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h new file mode 100644 index 000000000..cb7016cd6 --- /dev/null +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -0,0 +1,14 @@ +#pragma once +#include "ReceiveMessageCallback.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ReceiveMessageCallbackMock : public ReceiveMessageCallback { +public: + MOCK_METHOD(void, onSuccess, (ReceiveMessageResult&), (override)); + + MOCK_METHOD(void, onException, (MQException&), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index b7e8b069b..1e80802bd 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -1,5 +1,6 @@ #pragma once +#include "InvocationContext.h" #include "RpcClient.h" #include "gmock/gmock.h" #include @@ -10,9 +11,7 @@ class RpcClientMock : public RpcClient { public: RpcClientMock(); - ~RpcClientMock() override { - std::cout << "~RpcClientMock()" << std::endl; - } + ~RpcClientMock() override { std::cout << "~RpcClientMock()" << std::endl; } MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); @@ -44,6 +43,10 @@ class RpcClientMock : public RpcClient { MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); + MOCK_METHOD(void, asyncForwardMessageToDeadLetterQueue, + (const ForwardMessageToDeadLetterQueueRequest&, InvocationContext*), + (override)); + MOCK_METHOD(bool, needHeartbeat, (), (override)); MOCK_METHOD(void, needHeartbeat, (bool), (override)); diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp new file mode 100644 index 000000000..9309f8dcc --- /dev/null +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -0,0 +1,145 @@ +#include "AsyncReceiveMessageCallback.h" +#include "ClientManagerImpl.h" +#include "ConsumeMessageType.h" +#include "LoggerImpl.h" +#include "PushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) + : process_queue_(std::move(process_queue)) { + receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); +} + +void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + if (!process_queue_shared_ptr) { + SPDLOG_WARN("Process queue has been released. Drop PopResult: {}", result.toString()); + return; + } + + std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); + if (!impl->active()) { + return; + } + + switch (result.status()) { + case ReceiveMessageStatus::OK: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", + result.sourceHost(), result.getMsgFoundList().size(), process_queue_shared_ptr->simpleName()); + process_queue_shared_ptr->cacheMessages(result.getMsgFoundList()); + impl->getConsumeMessageService()->signalDispatcher(); + + if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::PULL) { + process_queue_shared_ptr->nextOffset(result.next_offset_); + } + checkThrottleThenReceive(); + break; + case ReceiveMessageStatus::DATA_CORRUPTED: + if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::POP) { + process_queue_shared_ptr->cacheMessages(result.messages_); + impl->getConsumeMessageService()->signalDispatcher(); + } + checkThrottleThenReceive(); + break; + + case ReceiveMessageStatus::OUT_OF_RANGE: + assert(ConsumeMessageType::PULL == process_queue_shared_ptr->consumeType()); + process_queue_shared_ptr->nextOffset(result.next_offset_); + checkThrottleThenReceive(); + break; + case ReceiveMessageStatus::DEADLINE_EXCEEDED: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=DEADLINE_EXCEEDED, queue={}", + result.sourceHost(), process_queue_shared_ptr->simpleName()); + checkThrottleThenReceive(); + break; + case ReceiveMessageStatus::INTERNAL: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=UNKNOWN, queue={}", result.sourceHost(), + process_queue_shared_ptr->simpleName()); + receiveMessageLater(); + break; + case ReceiveMessageStatus::RESOURCE_EXHAUSTED: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=RESOURCE_EXHAUSTED, queue={}", + result.sourceHost(), process_queue_shared_ptr->simpleName()); + receiveMessageLater(); + break; + case ReceiveMessageStatus::NOT_FOUND: + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=NOT_FOUND, queue={}", result.sourceHost(), + process_queue_shared_ptr->simpleName()); + receiveMessageLater(); + break; + default: + SPDLOG_WARN("Unknown receive message status: {} from broker[host={}], queue={}", result.status(), + result.sourceHost(), process_queue_shared_ptr->simpleName()); + receiveMessageLater(); + break; + } +} + +const char* AsyncReceiveMessageCallback::RECEIVE_LATER_TASK_NAME = "receive-later-task"; + +void AsyncReceiveMessageCallback::checkThrottleThenReceive() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + SPDLOG_WARN("Process queue should have been destructed"); + return; + } + + if (process_queue->shouldThrottle()) { + SPDLOG_INFO("Number of messages in {} exceeds throttle threshold. Receive messages later.", + process_queue->simpleName()); + process_queue->syncIdleState(); + receiveMessageLater(); + } else { + // Receive message immediately + receiveMessageImmediately(); + } +} + +void AsyncReceiveMessageCallback::onException(MQException& e) { + auto process_queue_ptr = process_queue_.lock(); + if (process_queue_ptr) { + SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", e.what(), process_queue_ptr->simpleName()); + // pop message later + receiveMessageLater(); + } +} + +void AsyncReceiveMessageCallback::receiveMessageLater() { + auto process_queue = process_queue_.lock(); + if (!process_queue) { + return; + } + + auto client_instance = process_queue->getClientManager(); + std::weak_ptr receive_callback_weak_ptr(shared_from_this()); + + auto task = [receive_callback_weak_ptr]() { + auto async_receive_ptr = receive_callback_weak_ptr.lock(); + if (async_receive_ptr) { + async_receive_ptr->checkThrottleThenReceive(); + } + }; + + client_instance->getScheduler().schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), + std::chrono::seconds(0)); +} + +void AsyncReceiveMessageCallback::receiveMessageImmediately() { + ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + if (!process_queue_shared_ptr) { + SPDLOG_INFO("ProcessQueue has been released. Ignore further receive message request-response cycles"); + return; + } + + std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); + if (!impl) { + SPDLOG_INFO("Owner of ProcessQueue[{}] has been released. Ignore further receive message request-response cycles", + process_queue_shared_ptr->simpleName()); + return; + } + impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression(), + process_queue_shared_ptr->consumeType()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index 9a92c3942..cdca0a8dc 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -1,4 +1,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + cc_library( name = "rocketmq_library", hdrs = glob(["include/*.h"]), @@ -9,5 +12,14 @@ cc_library( "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", ], - visibility = ["//visibility:public"], +) + +cc_library( + name = "rocketmq_mocks", + hdrs = glob(["mocks/include/*.h"]), + strip_include_prefix = "//src/main/cpp/rocketmq/mocks/include", + deps = [ + ":rocketmq_library", + "//src/main/cpp/client:client_mocks", + ], ) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt index 0f9b7d02e..87be4a332 100644 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -1,12 +1,13 @@ add_library(rocketmq_obj OBJECT) target_sources(rocketmq_obj PRIVATE + AsyncReceiveMessageCallback.cpp SendCallbacks.cpp AwaitPullCallback.cpp - BaseImpl.cpp + ClientImpl.cpp ConsumeMessageService.cpp - ConsumeMessageConcurrentlyService.cpp - ConsumeMessageOrderlyService.cpp + ConsumeStandardMessageService.cpp + ConsumeFifoMessageService.cpp CredentialsProvider.cpp DefaultMQProducer.cpp DefaultMQProducerImpl.cpp @@ -14,8 +15,9 @@ target_sources(rocketmq_obj DefaultMQPushConsumerImpl.cpp DefaultMQPullConsumer.cpp DefaultMQPullConsumerImpl.cpp + FilterExpression.cpp MessageGroupQueueSelector.cpp - ProcessQueue.cpp + ProcessQueueImpl.cpp TransactionImpl.cpp) target_include_directories(rocketmq_obj PUBLIC @@ -37,3 +39,5 @@ target_link_libraries(rocketmq_library client_library CURL::libcurl logger_library) + +add_subdirectory(mocks) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BaseImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp similarity index 86% rename from src/main/cpp/rocketmq/BaseImpl.cpp rename to src/main/cpp/rocketmq/ClientImpl.cpp index d97ef9983..14a246314 100644 --- a/src/main/cpp/rocketmq/BaseImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,5 +1,5 @@ -#include "BaseImpl.h" -#include "ClientManager.h" +#include "ClientImpl.h" +#include "ClientManagerFactory.h" #include "InvocationContext.h" #include "LoggerImpl.h" #include "MessageAccessor.h" @@ -13,16 +13,16 @@ ROCKETMQ_NAMESPACE_BEGIN -BaseImpl::BaseImpl(std::string group_name) : ClientConfig(std::move(group_name)), state_(State::CREATED) {} +ClientImpl::ClientImpl(std::string group_name) : ClientConfigImpl(std::move(group_name)), state_(State::CREATED) {} -void BaseImpl::start() { +void ClientImpl::start() { State expected = CREATED; if (!state_.compare_exchange_strong(expected, State::STARTING)) { SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); return; } - client_instance_ = ClientManager::getInstance().getClientInstance(*this); + client_instance_ = ClientManagerFactory::getInstance().getClientManager(*this); client_instance_->start(); bool update_name_server_list = false; { @@ -32,7 +32,7 @@ void BaseImpl::start() { } } - std::weak_ptr ptr(self()); + std::weak_ptr ptr(self()); if (update_name_server_list) { // Acquire name server list immediately @@ -41,7 +41,7 @@ void BaseImpl::start() { // Schedule to renew name server list periodically SPDLOG_INFO("Name server list was empty. Schedule a task to fetch and renew periodically"); auto name_server_update_functor = [ptr]() { - std::shared_ptr base = ptr.lock(); + std::shared_ptr base = ptr.lock(); if (base) { base->renewNameServerList(); } @@ -52,7 +52,7 @@ void BaseImpl::start() { } auto route_update_functor = [ptr]() { - std::shared_ptr base = ptr.lock(); + std::shared_ptr base = ptr.lock(); if (base) { base->updateRouteInfo(); } @@ -63,7 +63,7 @@ void BaseImpl::start() { state_.store(State::STARTED); } -void BaseImpl::shutdown() { +void ClientImpl::shutdown() { state_.store(State::STOPPING, std::memory_order_relaxed); if (name_server_update_handle_) { client_instance_->getScheduler().cancel(name_server_update_handle_); @@ -76,10 +76,10 @@ void BaseImpl::shutdown() { client_instance_.reset(); } -const char* BaseImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; -const char* BaseImpl::UPDATE_NAME_SERVER_LIST_TASK_NAME = "name_server_list_updater"; +const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; +const char* ClientImpl::UPDATE_NAME_SERVER_LIST_TASK_NAME = "name_server_list_updater"; -void BaseImpl::endpointsInUse(absl::flat_hash_set& endpoints) { +void ClientImpl::endpointsInUse(absl::flat_hash_set& endpoints) { absl::MutexLock lk(&topic_route_table_mtx_); for (const auto& item : topic_route_table_) { for (const auto& partition : item.second->partitions()) { @@ -91,7 +91,7 @@ void BaseImpl::endpointsInUse(absl::flat_hash_set& endpoints) { } } -void BaseImpl::getRouteFor(const std::string& topic, const std::function& cb) { +void ClientImpl::getRouteFor(const std::string& topic, const std::function& cb) { TopicRouteDataPtr route = nullptr; { absl::MutexLock lock(&topic_route_table_mtx_); @@ -132,11 +132,11 @@ void BaseImpl::getRouteFor(const std::string& topic, const std::function& list) { +void ClientImpl::debugNameServerChanges(const std::vector& list) { std::string previous; bool changed = false; { @@ -158,7 +158,7 @@ void BaseImpl::debugNameServerChanges(const std::vector& list) { } } -void BaseImpl::renewNameServerList() { +void ClientImpl::renewNameServerList() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); @@ -190,7 +190,7 @@ void BaseImpl::renewNameServerList() { client_instance_->topAddressing().fetchNameServerAddresses(callback); } -bool BaseImpl::selectNameServer(std::string& selected, bool change) { +bool ClientImpl::selectNameServer(std::string& selected, bool change) { static uint32_t index = 0; if (change) { index++; @@ -206,7 +206,7 @@ bool BaseImpl::selectNameServer(std::string& selected, bool change) { return true; } -void BaseImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { +void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { std::string name_server; if (!selectNameServer(name_server)) { SPDLOG_WARN("No name server available"); @@ -237,7 +237,7 @@ void BaseImpl::fetchRouteFor(const std::string& topic, const std::functionresolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } -void BaseImpl::updateRouteInfo() { +void ClientImpl::updateRouteInfo() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); @@ -254,7 +254,7 @@ void BaseImpl::updateRouteInfo() { if (!topics.empty()) { for (const auto& topic : topics) { - fetchRouteFor(topic, std::bind(&BaseImpl::updateRouteCache, this, topic, std::placeholders::_1)); + fetchRouteFor(topic, std::bind(&ClientImpl::updateRouteCache, this, topic, std::placeholders::_1)); } } @@ -265,7 +265,7 @@ void BaseImpl::updateRouteInfo() { SPDLOG_DEBUG("Topic route info updated"); } -void BaseImpl::heartbeat() { +void ClientImpl::heartbeat() { absl::flat_hash_set hosts; endpointsInUse(hosts); if (hosts.empty()) { @@ -295,7 +295,7 @@ void BaseImpl::heartbeat() { } } -void BaseImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) { +void ClientImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) { updateRouteCache(topic, route); // Take all pending callbacks @@ -314,7 +314,7 @@ void BaseImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataP } } -void BaseImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) { +void ClientImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) { if (!route || route->partitions().empty()) { SPDLOG_WARN("Yuck! route for {} is invalid", topic); return; @@ -337,14 +337,14 @@ void BaseImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPt } } -void BaseImpl::multiplexing(const std::string& target, const MultiplexingRequest& request) { +void ClientImpl::multiplexing(const std::string& target, const MultiplexingRequest& request) { absl::flat_hash_map metadata; Signature::sign(this, metadata); client_instance_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), - std::bind(&BaseImpl::onMultiplexingResponse, this, std::placeholders::_1)); + std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); } -void BaseImpl::onMultiplexingResponse(const InvocationContext* ctx) { +void ClientImpl::onMultiplexingResponse(const InvocationContext* ctx) { if (!ctx->status.ok()) { std::string remote_address = ctx->remote_address; auto multiplexingLater = [this, remote_address]() { @@ -368,7 +368,7 @@ void BaseImpl::onMultiplexingResponse(const InvocationContext metadata; Signature::sign(this, metadata); client_instance_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - std::bind(&BaseImpl::onMultiplexingResponse, this, std::placeholders::_1)); + std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); break; } @@ -427,7 +427,7 @@ void BaseImpl::onMultiplexingResponse(const InvocationContext endpoints; { absl::MutexLock lk(&isolated_endpoints_mtx_); @@ -436,10 +436,10 @@ void BaseImpl::healthCheck() { } } - std::weak_ptr base(self()); + std::weak_ptr base(self()); auto callback = [base](const std::string& endpoint, const InvocationContext* invocation_context) { - std::shared_ptr ptr = base.lock(); + std::shared_ptr ptr = base.lock(); if (ptr) { ptr->onHealthCheckResponse(endpoint, invocation_context); } else { @@ -455,7 +455,12 @@ void BaseImpl::healthCheck() { } } -void BaseImpl::onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) { +void ClientImpl::schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) { + client_instance_->getScheduler().schedule(task, task_name, delay, std::chrono::milliseconds(0)); +} + +void ClientImpl::onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) { if (!ctx) { SPDLOG_WARN("ClientInstance does not have RPC client for {}. It might have been offline and thus cleaned", endpoint); @@ -483,7 +488,7 @@ void BaseImpl::onHealthCheckResponse(const std::string& endpoint, const Invocati } } -void BaseImpl::fillGenericPollingRequest(MultiplexingRequest& request) { +void ClientImpl::fillGenericPollingRequest(MultiplexingRequest& request) { auto&& resource_bundle = resourceBundle(); auto protocol_bundle = request.mutable_polling_request()->mutable_client_resource_bundle(); protocol_bundle->set_client_id(resource_bundle.client_id); diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp new file mode 100644 index 000000000..cdad3e23e --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -0,0 +1,243 @@ +#include "ConsumeMessageService.h" +#include "DefaultMQPushConsumerImpl.h" +#include "MessageAccessor.h" +#include "ProcessQueue.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeFifoMessageService ::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener) + : ConsumeMessageService(std::move(consumer), thread_count, message_listener) {} + +void ConsumeFifoMessageService::start() { + ConsumeMessageService::start(); + State expected = State::STARTING; + if (state_.compare_exchange_strong(expected, State::STARTED)) { + SPDLOG_DEBUG("ConsumeMessageOrderlyService started"); + } +} + +void ConsumeFifoMessageService::shutdown() { + // Wait till consume-message-orderly-service has fully started; otherwise, we may potentially miss closing resources + // in concurrent scenario. + while (State::STARTING == state_.load(std::memory_order_relaxed)) { + absl::SleepFor(absl::Milliseconds(10)); + } + + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, STOPPING)) { + ConsumeMessageService::shutdown(); + SPDLOG_INFO("ConsumeMessageOrderlyService shut down"); + } +} + +void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr& consumer, + const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + // In case custom executor is used. + const Executor& custom_executor = consumer->customExecutor(); + if (custom_executor) { + std::function consume_task = + std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); + custom_executor(consume_task); + SPDLOG_DEBUG("Submit FIFO consume task to custom executor"); + return; + } + + // submit batch message + std::function consume_task = + std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); + SPDLOG_DEBUG("Submit FIFO consume task to thread pool"); + pool_->Add(consume_task); +} + +void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_INFO("Process queue has destructed"); + return; + } + + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_INFO("Consumer has destructed"); + return; + } + + assert(1 == consumer->consumeBatchSize()); + + if (process_queue_ptr->bindFifoConsumeTask()) { + std::vector messages; + if (process_queue_ptr->take(consumer->consumeBatchSize(), messages)) { + assert(1 == messages.size()); + submitConsumeTask0(consumer, process_queue, *messages.begin()); + } + } +} + +MessageListenerType ConsumeFifoMessageService::messageListenerType() { return MessageListenerType::FIFO; } + +void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + const std::string& topic = message.getTopic(); + ConsumeMessageResult result; + std::shared_ptr consumer = consumer_.lock(); + // consumer might have been destructed. + if (!consumer) { + return; + } + + std::shared_ptr> rate_limiter = rateLimiter(topic); + if (rate_limiter) { + rate_limiter->acquire(); + SPDLOG_DEBUG("Rate-limit permit acquired"); + } + + auto steady_start = std::chrono::steady_clock::now(); + + try { + assert(message_listener_); + auto message_listener = dynamic_cast(message_listener_); + assert(message_listener); + result = message_listener->consumeMessage(message); + } catch (...) { + result = ConsumeMessageResult::FAILURE; + SPDLOG_ERROR("Business FIFO callback raised an exception when consumeMessage"); + } + + auto duration = std::chrono::steady_clock::now() - steady_start; + + // Log client consume-time costs + SPDLOG_DEBUG("Business callback spent {}ms processing message[Topic={}, MessageId={}].", + MixAll::millisecondsOf(duration), message.getTopic(), message.getMsgId()); + + if (MessageModel::CLUSTERING == consumer->messageModel()) { + if (result == ConsumeMessageResult::SUCCESS) { + // Release message number and memory quota + process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); + + // Ensure current message is acked before moving to the next message. + auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); + consumer->ack(message, callback); + } else { + MessageAccessor::setDeliveryAttempt(message, message.getDeliveryAttempt() + 1); + if (message.getDeliveryAttempt() < consumer->maxDeliveryAttempts()) { + auto task = std::bind(&ConsumeFifoMessageService::scheduleConsumeTask, this, process_queue, message); + consumer->schedule("Scheduled-Consume-FIFO-Message-Task", task, std::chrono::seconds(1)); + } else { + auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, + std::placeholders::_1); + consumer->forwardToDeadLetterQueue(message, callback); + } + } + } else if (MessageModel::BROADCASTING == consumer->messageModel()) { + process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); + int64_t committed_offset; + if (process_queue_ptr->committedOffset(committed_offset)) { + consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + } + } +} + +void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_WARN("ProcessQueue has destructed."); + return; + } + if (ok) { + SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), + message.getMsgId()); + process_queue_ptr->unbindFifoConsumeTask(); + signalDispatcher(); + } else { + SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]", process_queue_ptr->simpleName(), + message.getMsgId()); + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_WARN("Consumer instance has destructed"); + return; + } + + auto task = std::bind(&ConsumeFifoMessageService::scheduleAckTask, this, process_queue, message); + int32_t duration = 100; + consumer->schedule("Ack-FIFO-Message-On-Failure", task, std::chrono::milliseconds(duration)); + SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message.getTopic(), message.getMsgId(), + duration); + } +} + +void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message, bool ok) { + if (ok) { + SPDLOG_DEBUG("Forward message[Topic={}, MessagId={}] to DLQ OK", message.getTopic(), message.getMsgId()); + auto process_queue_ptr = process_queue.lock(); + if (process_queue_ptr) { + process_queue_ptr->unbindFifoConsumeTask(); + } + return; + } + + SPDLOG_INFO("Failed to forward message[topic={}, MessageId={}] to DLQ", message.getTopic(), message.getMsgId()); + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_INFO("Abort further attempts considering its process queue has destructed"); + return; + } + + auto consumer = consumer_.lock(); + assert(consumer); + + auto task = std::bind(&ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask, this, process_queue, message); + consumer->schedule("Scheduled-Forward-DLQ-Task", task, std::chrono::milliseconds(100)); +} + +void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + auto consumer = consumer_.lock(); + assert(consumer); + auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, + std::placeholders::_1); + consumer->forwardToDeadLetterQueue(message, callback); +} + +void ConsumeFifoMessageService::scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message) { + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + + auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); + auto consumer = consumer_.lock(); + if (consumer) { + consumer->ack(message, callback); + } +} + +void ConsumeFifoMessageService::scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message) { + auto consumer_ptr = consumer_.lock(); + if (!consumer_ptr) { + return; + } + + auto process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + return; + } + + submitConsumeTask0(consumer_ptr, process_queue_ptr, message); + SPDLOG_INFO("Business callback failed to process FIFO messages. Re-submit consume task back to thread pool"); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp deleted file mode 100644 index 3c7e22e49..000000000 --- a/src/main/cpp/rocketmq/ConsumeMessageConcurrentlyService.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "ConsumeMessageService.h" -#include "DefaultMQPushConsumerImpl.h" -#include "LoggerImpl.h" -#include "Protocol.h" -#include "TracingUtility.h" -#include "absl/strings/str_join.h" -#include "absl/memory/memory.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -ConsumeMessageConcurrentlyService::ConsumeMessageConcurrentlyService(std::weak_ptr consumer, - int thread_count, - MQMessageListener* message_listener_ptr) - : thread_count_(thread_count), message_listener_ptr_(message_listener_ptr), state_(State::CREATED), - pool_(absl::make_unique(thread_count_)), consumer_weak_ptr_(std::move(consumer)) {} - -void ConsumeMessageConcurrentlyService::start() { - // Loop each process queue - // If there is message to dispatch - // Then - // If Not rate limiter configured or has permits - // Submit consume task - // Else - // Continue - // Else - // Continue - // Wait on condition variable - - // Once consume task completes - // If the topic were configured with rate limiter - // Then Wake up dispatch_thread - - state_.store(State::STARTING, std::memory_order_relaxed); - dispatch_thread_ = std::thread([this] { - while (State::STOPPED != state_.load(std::memory_order_relaxed)) { - dispatch0(); - { - absl::MutexLock lk(&dispatch_mtx_); - dispatch_cv_.WaitWithTimeout(&dispatch_mtx_, absl::Milliseconds(100)); - } - } - }); -} - -void ConsumeMessageConcurrentlyService::shutdown() { - state_.store(State::STOPPED, std::memory_order_relaxed); - { - absl::MutexLock lk(&dispatch_mtx_); - dispatch_cv_.SignalAll(); - } - if (dispatch_thread_.joinable()) { - dispatch_thread_.join(); - } - ConsumeMessageService::shutdown(); - pool_.reset(); - SPDLOG_DEBUG("ConsumeMessageConcurrentlyService thread pool stop"); -} - -void ConsumeMessageConcurrentlyService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) { - ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); - return; - } - std::shared_ptr consumer_impl_ptr = process_queue_ptr->getCallbackOwner().lock(); - - if (!consumer_impl_ptr) { - return; - } - - std::string topic = process_queue_ptr->topic(); - std::shared_ptr> rate_limiter = rateLimiter(topic); - bool has_more = true; - while (has_more) { - std::vector messages; - uint32_t batch_size = consumer_impl_ptr->consumeBatchSize(); - uint32_t acquired; - if (rate_limiter) { - uint32_t permits_to_acquire = - std::min({batch_size, rate_limiter->available(), process_queue_ptr->cachedMessagesSize()}); - acquired = rate_limiter->acquire(permits_to_acquire); - if (!acquired) { - SPDLOG_DEBUG("Throttled: failed to acquire permits from rate limiter"); - break; - } - } else if (permits < 0) { - acquired = batch_size; - } else { - SPDLOG_WARN("submitConsumeTask with permits = 0"); - break; - } - - has_more = process_queue_ptr->consume(acquired, messages); - if (messages.empty()) { - assert(!has_more); - break; - } - - // In case custom executor is used. - const Executor& custom_executor = consumer_impl_ptr->customExecutor(); - if (custom_executor) { - std::function consume_task = - std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, process_queue, messages); - custom_executor(consume_task); - SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); - continue; - } - - // submit batch message - std::function consume_task = - std::bind(&ConsumeMessageConcurrentlyService::consumeTask, this, process_queue_ptr, messages); - SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); - pool_->Add(consume_task); - } -} - -MessageListenerType ConsumeMessageConcurrentlyService::getConsumeMsgServiceListenerType() { - return messageListenerConcurrently; -} - -void ConsumeMessageConcurrentlyService::stopThreadPool() { - // do nothing, ThreadPool destructor will be invoked automatically -} - -void ConsumeMessageConcurrentlyService::dispatch() { - absl::MutexLock lk(&dispatch_mtx_); - // Wake up dispatch_thread_ - dispatch_cv_.Signal(); -} - -void ConsumeMessageConcurrentlyService::dispatch0() { - std::shared_ptr consumer = consumer_weak_ptr_.lock(); - if (!consumer) { - SPDLOG_WARN("The consumer was destructed"); - return; - } - - auto lambda = [this](const ProcessQueueSharedPtr& process_queue_ptr) { - // Note: we have already got process_queue_table_mtx_ locked - std::string topic = process_queue_ptr->topic(); - if (hasConsumeRateLimiter(topic)) { - std::shared_ptr> rate_limiter = rateLimiter(topic); - if (rate_limiter) { - uint32_t permits = rate_limiter->available(); - if (permits) { - submitConsumeTask(process_queue_ptr, permits); - } - } else { - submitConsumeTask(process_queue_ptr, -1); - } - } else { - submitConsumeTask(process_queue_ptr, -1); - } - }; - consumer->iterateProcessQueue(lambda); -} - -void ConsumeMessageConcurrentlyService::consumeTask(const ProcessQueueWeakPtr& process_queue, - const std::vector& msgs) { - ProcessQueueSharedPtr process_queue_shared_ptr = process_queue.lock(); - if (!process_queue_shared_ptr || msgs.empty()) { - return; - } - std::string topic = msgs.begin()->getTopic(); - ConsumeStatus status; - std::shared_ptr consumer_shared_ptr = consumer_weak_ptr_.lock(); - // consumer may be destructed here. - if (!consumer_shared_ptr) { - return; - } - // consumer does not start yet. -#ifdef ENABLE_TRACING - nostd::shared_ptr tracer = consumer_shared_ptr->getTracer(); - if (!tracer) { - return; - } - auto system_start = std::chrono::system_clock::now(); -#endif - - auto steady_start = std::chrono::steady_clock::now(); - - try { - assert(nullptr != message_listener_ptr_); - status = message_listener_ptr_->consumeMessage(msgs); - } catch (...) { - status = RECONSUME_LATER; - SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); - } - - auto duration = std::chrono::steady_clock::now() - steady_start; -#ifdef ENABLE_TRACING - std::chrono::microseconds average_duration = - std::chrono::microseconds(MixAll::microsecondsOf(duration) / msgs.size()); -#endif - - std::vector msg_id_list; - msg_id_list.reserve(msgs.size()); - - for (const auto& msg : msgs) { - msg_id_list.emplace_back(msg.getMsgId()); -#ifdef ENABLE_TRACING - nostd::shared_ptr span = nostd::shared_ptr(nullptr); - trace::EndSpanOptions end_options; - if (consumer_shared_ptr->isTracingEnabled()) { - const std::string& serialized_span_context = msg.traceContext(); - trace::SpanContext span_context = TracingUtility::extractContextFromTraceParent(serialized_span_context); - trace::StartSpanOptions start_options; - start_options.start_system_time = - opentelemetry::core::SystemTimestamp(system_start + i * std::chrono::microseconds(average_duration)); - start_options.start_steady_time = - opentelemetry::core::SteadyTimestamp(steady_start + i * std::chrono::microseconds(average_duration)); - start_options.parent = span_context; - end_options.end_steady_time = - opentelemetry::core::SteadyTimestamp(steady_start + (i + 1) * std::chrono::microseconds(average_duration)); - span = tracer->StartSpan("ConsumeMessage", start_options); - } -#endif - - process_queue_shared_ptr->messageCachedNumber().fetch_sub(1, std::memory_order_relaxed); - -#ifdef ENABLE_TRACING - if (span) { - span->SetAttribute(TracingUtility::get().expired_, false); - span->SetAttribute(TracingUtility::get().success_, CONSUME_SUCCESS == status); - span->End(end_options); - } -#endif - - if (MessageModel::CLUSTERING == consumer_shared_ptr->messageModel()) { - if (status == CONSUME_SUCCESS) { - auto ack_callback = [process_queue_shared_ptr, msg](bool ok) { - if (ok) { - SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_shared_ptr->simpleName(), - msg.getMsgId()); - } else { - SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]", - process_queue_shared_ptr->simpleName(), msg.getMsgId()); - } - }; - consumer_shared_ptr->ack(msg, ack_callback); - } else { - auto nack_callback = [process_queue_shared_ptr, msg](bool ok) { - if (ok) { - SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_shared_ptr->simpleName(), - msg.getMsgId()); - } else { - SPDLOG_INFO( - "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Message will be re-consumed " - "after default invisible time", - process_queue_shared_ptr->simpleName(), msg.getMsgId()); - } - }; - consumer_shared_ptr->nack(msg, nack_callback); - } - } - } - - if (MessageModel::BROADCASTING == consumer_shared_ptr->messageModel()) { - if (consumer_shared_ptr->offset_store_) { - consumer_shared_ptr->offset_store_->updateOffset(process_queue_shared_ptr->getMQMessageQueue(), - process_queue_shared_ptr->nextOffset()); - } - } - - // Log client consume-time costs - SPDLOG_DEBUG("Business callback spent {}ms processing {} messages. Message-identifier-list: {}", - MixAll::millisecondsOf(duration), msgs.size(), - absl::StrJoin(msg_id_list.begin(), msg_id_list.end(), ",")); - - bool need_dispatch = false; - { - absl::MutexLock lk(&rate_limiter_table_mtx_); - if (rate_limiter_table_.contains(topic)) { - need_dispatch = true; - } - } - - if (need_dispatch) { - dispatch(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp b/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp deleted file mode 100644 index 5cda5ce97..000000000 --- a/src/main/cpp/rocketmq/ConsumeMessageOrderlyService.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "ConsumeMessageService.h" -#include "DefaultMQPushConsumerImpl.h" - -ROCKETMQ_NAMESPACE_BEGIN - -ConsumeMessageOrderlyService ::ConsumeMessageOrderlyService( - const std::weak_ptr&& consumer_impl_ptr, int thread_count, - MQMessageListener* message_listener_ptr) - : consumer_weak_ptr_(consumer_impl_ptr), message_listener_ptr_(message_listener_ptr), thread_count_(thread_count), - pool_(absl::make_unique(thread_count_)) { - // Suppress field not used warning for now - (void)message_listener_ptr_; -} - -void ConsumeMessageOrderlyService::start() { - // pool_ = std::make_shared(thread_count_); -} - -void ConsumeMessageOrderlyService::shutdown() { ConsumeMessageService::shutdown(); } - -void ConsumeMessageOrderlyService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) {} - -MessageListenerType ConsumeMessageOrderlyService::getConsumeMsgServiceListenerType() { - return MessageListenerType::messageListenerOrderly; -} - -void ConsumeMessageOrderlyService::stopThreadPool() { - // do nothing, ThreadPool destructor will be invoked automatically -} - -void ConsumeMessageOrderlyService::dispatch() { - // Not implemented. -} - -void ConsumeMessageOrderlyService::consumeTask(const ProcessQueueWeakPtr& process_queue, - const std::vector& msgs) {} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageService.cpp b/src/main/cpp/rocketmq/ConsumeMessageService.cpp index 2a5729ffc..2bb7dc5b2 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageService.cpp @@ -1,7 +1,40 @@ #include "ConsumeMessageService.h" +#include "PushConsumer.h" +#include "LoggerImpl.h" ROCKETMQ_NAMESPACE_BEGIN +ConsumeMessageService::ConsumeMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener) + : state_(State::CREATED), thread_count_(thread_count), + pool_(absl::make_unique(thread_count_)), consumer_(std::move(consumer)), + message_listener_(message_listener) {} + +void ConsumeMessageService::start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + dispatch_thread_ = std::thread([this] { + State current_state = state_.load(std::memory_order_relaxed); + while (State::STOPPED != current_state && State::STOPPING != current_state) { + dispatch(); + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.WaitWithTimeout(&dispatch_mtx_, absl::Milliseconds(100)); + } + + // Update current state + current_state = state_.load(std::memory_order_relaxed); + } + }); + } +} + +void ConsumeMessageService::signalDispatcher() { + absl::MutexLock lk(&dispatch_mtx_); + // Wake up dispatch_thread_ + dispatch_cv_.Signal(); +} + void ConsumeMessageService::throttle(const std::string& topic, uint32_t threshold) { absl::MutexLock lk(&rate_limiter_table_mtx_); std::shared_ptr> rate_limiter = std::make_shared>(threshold); @@ -9,7 +42,21 @@ void ConsumeMessageService::throttle(const std::string& topic, uint32_t threshol rate_limiter_observer_.subscribe(rate_limiter); } -void ConsumeMessageService::shutdown() { rate_limiter_observer_.stop(); } +void ConsumeMessageService::shutdown() { + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { + { + absl::MutexLock lk(&dispatch_mtx_); + dispatch_cv_.SignalAll(); + } + + if (dispatch_thread_.joinable()) { + dispatch_thread_.join(); + } + + rate_limiter_observer_.stop(); + } +} bool ConsumeMessageService::hasConsumeRateLimiter(const std::string& topic) const { absl::MutexLock lk(&rate_limiter_table_mtx_); @@ -24,4 +71,15 @@ std::shared_ptr> ConsumeMessageService::rateLimiter(const std::s return rate_limiter_table_[topic]; } +void ConsumeMessageService::dispatch() { + std::shared_ptr consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_WARN("The consumer has already destructed"); + return; + } + + auto callback = [this](const ProcessQueueSharedPtr& process_queue) { submitConsumeTask(process_queue); }; + consumer->iterateProcessQueue(callback); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp new file mode 100644 index 000000000..44739cabe --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -0,0 +1,197 @@ +#include "ConsumeMessageService.h" +#include "LoggerImpl.h" +#include "Protocol.h" +#include "PushConsumer.h" +#include "TracingUtility.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_join.h" +#include "rocketmq/ConsumeType.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, + int thread_count, MessageListener* message_listener_ptr) + : ConsumeMessageService(std::move(consumer), thread_count, message_listener_ptr) {} + +void ConsumeStandardMessageService::start() { + ConsumeMessageService::start(); + State expected = State::STARTING; + if (state_.compare_exchange_strong(expected, State::STARTED)) { + SPDLOG_DEBUG("ConsumeMessageConcurrentlyService started"); + } +} + +void ConsumeStandardMessageService::shutdown() { + while (State::STARTING == state_.load(std::memory_order_relaxed)) { + absl::SleepFor(absl::Milliseconds(10)); + } + + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING)) { + ConsumeMessageService::shutdown(); + SPDLOG_DEBUG("ConsumeMessageConcurrentlyService shut down"); + } +} + +void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr) { + SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); + return; + } + std::shared_ptr consumer = process_queue_ptr->getConsumer().lock(); + + if (!consumer) { + return; + } + + std::string topic = process_queue_ptr->topic(); + bool has_more = true; + while (has_more) { + std::vector messages; + uint32_t batch_size = consumer->consumeBatchSize(); + has_more = process_queue_ptr->take(batch_size, messages); + if (messages.empty()) { + assert(!has_more); + break; + } + + // In case custom executor is used. + const Executor& custom_executor = consumer->customExecutor(); + if (custom_executor) { + std::function consume_task = + std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue, messages); + custom_executor(consume_task); + SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); + continue; + } + + // submit batch message + std::function consume_task = + std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue_ptr, messages); + SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); + pool_->Add(consume_task); + } +} + +MessageListenerType ConsumeStandardMessageService::messageListenerType() { return MessageListenerType::STANDARD; } + +void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, + const std::vector& msgs) { + ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); + if (!process_queue_ptr || msgs.empty()) { + return; + } + std::string topic = msgs.begin()->getTopic(); + ConsumeMessageResult status; + std::shared_ptr consumer = consumer_.lock(); + // consumer might have been destructed. + if (!consumer) { + return; + } + // consumer does not start yet. +#ifdef ENABLE_TRACING + nostd::shared_ptr tracer = consumer_shared_ptr->getTracer(); + if (!tracer) { + return; + } + auto system_start = std::chrono::system_clock::now(); +#endif + + std::shared_ptr> rate_limiter = rateLimiter(topic); + if (rate_limiter) { + // Acquire permits one-by-one to avoid large batch hungry issue. + for (std::size_t i = 0; i < msgs.size(); i++) { + rate_limiter->acquire(); + } + SPDLOG_DEBUG("{} rate-limit permits acquired", msgs.size()); + } + + auto steady_start = std::chrono::steady_clock::now(); + + try { + assert(nullptr != message_listener_); + auto message_listener = dynamic_cast(message_listener_); + assert(message_listener); + status = message_listener->consumeMessage(msgs); + } catch (...) { + status = ConsumeMessageResult::FAILURE; + SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); + } + + auto duration = std::chrono::steady_clock::now() - steady_start; + + // Log client consume-time costs + SPDLOG_DEBUG("Business callback spent {}ms processing {} messages.", MixAll::millisecondsOf(duration), msgs.size()); + +#ifdef ENABLE_TRACING + std::chrono::microseconds average_duration = + std::chrono::microseconds(MixAll::microsecondsOf(duration) / msgs.size()); +#endif + + if (MessageModel::CLUSTERING == consumer->messageModel()) { + for (const auto& msg : msgs) { + const std::string& message_id = msg.getMsgId(); + + // Release message number and memory quota + process_queue_ptr->release(msg.getBody().size(), msg.getQueueOffset()); + +#ifdef ENABLE_TRACING + nostd::shared_ptr span = nostd::shared_ptr(nullptr); + trace::EndSpanOptions end_options; + if (consumer_shared_ptr->isTracingEnabled()) { + const std::string& serialized_span_context = msg.traceContext(); + trace::SpanContext span_context = TracingUtility::extractContextFromTraceParent(serialized_span_context); + trace::StartSpanOptions start_options; + start_options.start_system_time = + opentelemetry::core::SystemTimestamp(system_start + i * std::chrono::microseconds(average_duration)); + start_options.start_steady_time = + opentelemetry::core::SteadyTimestamp(steady_start + i * std::chrono::microseconds(average_duration)); + start_options.parent = span_context; + end_options.end_steady_time = + opentelemetry::core::SteadyTimestamp(steady_start + (i + 1) * std::chrono::microseconds(average_duration)); + span = tracer->StartSpan("ConsumeMessage", start_options); + } + + if (span) { + span->SetAttribute(TracingUtility::get().expired_, false); + span->SetAttribute(TracingUtility::get().success_, CONSUME_SUCCESS == status); + span->End(end_options); + } +#endif + if (status == ConsumeMessageResult::SUCCESS) { + auto callback = [process_queue_ptr, message_id](bool ok) { + if (ok) { + SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), + message_id); + } else { + SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]", process_queue_ptr->simpleName(), + message_id); + } + }; + consumer->ack(msg, callback); + } else { + auto callback = [process_queue_ptr, message_id](bool ok) { + if (ok) { + SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); + } else { + SPDLOG_INFO( + "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Message will be re-consumed " + "after default invisible time", + process_queue_ptr->simpleName(), message_id); + } + }; + consumer->nack(msg, callback); + } + } + + } else if (MessageModel::BROADCASTING == consumer->messageModel()) { + int64_t committed_offset; + if (process_queue_ptr->committedOffset(committed_offset)) { + consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp index 2c34e9970..91fdad19b 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp @@ -1,7 +1,7 @@ #include "DefaultMQProducerImpl.h" #include "MessageAccessor.h" #include "MessageGroupQueueSelector.h" -#include "Metadata.h" +#include "MetadataConstants.h" #include "MixAll.h" #include "Protocol.h" #include "SendCallbacks.h" @@ -12,18 +12,19 @@ #include "UtilAll.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" +#include ROCKETMQ_NAMESPACE_BEGIN DefaultMQProducerImpl::DefaultMQProducerImpl(std::string group_name) - : BaseImpl(std::move(group_name)), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { + : ClientImpl(std::move(group_name)), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { // TODO: initialize client_config_ and fault_strategy_ } DefaultMQProducerImpl::~DefaultMQProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); } void DefaultMQProducerImpl::start() { - BaseImpl::start(); + ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected producer state: {}", state_.load(std::memory_order_relaxed)); return; @@ -33,7 +34,7 @@ void DefaultMQProducerImpl::start() { } void DefaultMQProducerImpl::shutdown() { - BaseImpl::shutdown(); + ClientImpl::shutdown(); State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED)) { diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp index 3609d0315..b19e2571d 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp @@ -1,11 +1,12 @@ #include "DefaultMQPullConsumerImpl.h" -#include "ClientManager.h" +#include "ClientManagerFactory.h" +#include "InvocationContext.h" #include "Signature.h" ROCKETMQ_NAMESPACE_BEGIN void DefaultMQPullConsumerImpl::start() { - BaseImpl::start(); + ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); return; @@ -17,7 +18,7 @@ void DefaultMQPullConsumerImpl::shutdown() { // Shutdown services started by current tier // Shutdown services that are started by the parent - BaseImpl::shutdown(); + ClientImpl::shutdown(); State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED)) { SPDLOG_INFO("DefaultMQPullConsumerImpl stopped"); @@ -115,13 +116,14 @@ void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback std::string target_host = query.message_queue.serviceAddress(); assert(!target_host.empty()); - auto callback = [this, target_host, cb](bool ok, const PullMessageResponse& response) { - if (!ok) { + auto callback = [this, target_host, cb](const InvocationContext* invocation_context) { + if (!invocation_context || !invocation_context->status.ok()) { MQClientException exception(fmt::format("Server[{}] is not reachable", target_host), -1, __FILE__, __LINE__); cb->onException(exception); return; } + auto response = invocation_context->response; auto biz_status = response.common().status(); if (google::rpc::Code::OK != biz_status.code()) { MQClientException exception(response.common().status().message(), biz_status.code(), __FILE__, __LINE__); @@ -143,7 +145,7 @@ void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_instance_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_instance_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), callback); } void DefaultMQPullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index 64751b648..d891de3c2 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -32,7 +32,7 @@ void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::strin void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { impl_->setConsumeFromWhere(policy); } -void DefaultMQPushConsumer::registerMessageListener(MQMessageListener* listener) { +void DefaultMQPushConsumer::registerMessageListener(MessageListener* listener) { impl_->registerMessageListener(listener); } @@ -59,10 +59,6 @@ bool DefaultMQPushConsumer::isTracingEnabled() { return impl_->isTracingEnabled( void DefaultMQPushConsumer::setAsyncPull(bool) {} -void DefaultMQPushConsumer::setMaxCacheMsgSizePerQueue(int max_cache_size) { - impl_->maxCachedMessageNumberPerQueue(max_cache_size); -} - void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { impl_->consumeBatchSize(batch_size); } void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { impl_->setCustomExecutor(executor); } diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp index 6a46969dd..614d7ebf1 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp @@ -1,164 +1,22 @@ #include "DefaultMQPushConsumerImpl.h" -#include "ClientManager.h" +#include "AsyncReceiveMessageCallback.h" +#include "ClientManagerFactory.h" #include "MessageAccessor.h" #include "MixAll.h" +#include "ProcessQueueImpl.h" #include "Signature.h" #include "rocketmq/MQClientException.h" #include ROCKETMQ_NAMESPACE_BEGIN -AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) - : process_queue_(std::move(process_queue)) { - receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); -} - -void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { - ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); - if (!process_queue_shared_ptr) { - SPDLOG_WARN("Process queue has been released. Drop PopResult: {}", result.toString()); - return; - } - - std::shared_ptr impl = process_queue_shared_ptr->getCallbackOwner().lock(); - if (impl->isStopped()) { - return; - } - - switch (result.status()) { - case ReceiveMessageStatus::OK: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", - result.sourceHost(), result.getMsgFoundList().size(), process_queue_shared_ptr->simpleName()); - process_queue_shared_ptr->cacheMessages(result.getMsgFoundList()); - impl->getConsumeMessageService()->dispatch(); - - if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::PULL) { - process_queue_shared_ptr->nextOffset(result.next_offset_); - } - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::DATA_CORRUPTED: - if (process_queue_shared_ptr->consumeType() == POP) { - process_queue_shared_ptr->cacheMessages(result.messages_); - impl->getConsumeMessageService()->dispatch(); - } - checkThrottleThenReceive(); - break; - - case ReceiveMessageStatus::OUT_OF_RANGE: - assert(ConsumeMessageType::PULL == process_queue_shared_ptr->consumeType()); - process_queue_shared_ptr->nextOffset(result.next_offset_); - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::DEADLINE_EXCEEDED: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=DEADLINE_EXCEEDED, queue={}", - result.sourceHost(), process_queue_shared_ptr->simpleName()); - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::INTERNAL: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=UNKNOWN, queue={}", result.sourceHost(), - process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - case ReceiveMessageStatus::RESOURCE_EXHAUSTED: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=RESOURCE_EXHAUSTED, queue={}", - result.sourceHost(), process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - case ReceiveMessageStatus::NOT_FOUND: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=NOT_FOUND, queue={}", result.sourceHost(), - process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - default: - SPDLOG_WARN("Unknown receive message status: {} from broker[host={}], queue={}", result.status(), - result.sourceHost(), process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - } -} - -const char* AsyncReceiveMessageCallback::RECEIVE_LATER_TASK_NAME = "receive-later-task"; - -void AsyncReceiveMessageCallback::checkThrottleThenReceive() { - auto process_queue = process_queue_.lock(); - if (!process_queue) { - SPDLOG_WARN("Process queue should have been destructed"); - return; - } - - if (process_queue->shouldThrottle()) { - SPDLOG_INFO("Number of messages in {} exceeds throttle threshold. Receive messages later.", - process_queue->simpleName()); - process_queue->updateThrottleTimestamp(); - receiveMessageLater(); - } else { - // Receive message immediately - receiveMessageImmediately(); - } -} - -void AsyncReceiveMessageCallback::onException(MQException& e) { - auto process_queue_ptr = process_queue_.lock(); - if (process_queue_ptr) { - SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", e.what(), process_queue_ptr->simpleName()); - // pop message later - receiveMessageLater(); - } -} - -void AsyncReceiveMessageCallback::receiveMessageLater() { - auto process_queue = process_queue_.lock(); - if (!process_queue) { - return; - } - - auto client_instance = process_queue->getClientInstance(); - std::weak_ptr receive_callback_weak_ptr(shared_from_this()); - - auto task = [receive_callback_weak_ptr]() { - auto async_receive_ptr = receive_callback_weak_ptr.lock(); - if (async_receive_ptr) { - async_receive_ptr->checkThrottleThenReceive(); - } - }; - - client_instance->getScheduler().schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), - std::chrono::seconds(0)); -} - -void AsyncReceiveMessageCallback::receiveMessageImmediately() { - ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); - if (!process_queue_shared_ptr) { - SPDLOG_INFO("ProcessQueue has been released. Ignore further receive message request-response cycles"); - return; - } - - std::shared_ptr impl = process_queue_shared_ptr->getCallbackOwner().lock(); - if (!impl) { - SPDLOG_INFO("Owner of ProcessQueue[{}] has been released. Ignore further receive message request-response cycles", - process_queue_shared_ptr->simpleName()); - return; - } - impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression(), - process_queue_shared_ptr->consumeType()); -} - -const int32_t DefaultMQPushConsumerImpl::MAX_CACHED_MESSAGE_COUNT = 65535; -const int32_t DefaultMQPushConsumerImpl::DEFAULT_CACHED_MESSAGE_COUNT = 1024; -const int32_t DefaultMQPushConsumerImpl::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE = 1; -const int32_t DefaultMQPushConsumerImpl::DEFAULT_CONSUME_THREAD_POOL_SIZE = 20; - -DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(std::string group_name) - : BaseImpl(std::move(group_name)), consume_thread_pool_size_(DEFAULT_CONSUME_THREAD_POOL_SIZE), - message_listener_ptr_(nullptr), consume_batch_size_(DEFAULT_CONSUME_MESSAGE_BATCH_SIZE), - max_cached_message_number_per_queue_(DEFAULT_CACHED_MESSAGE_COUNT) {} +DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} DefaultMQPushConsumerImpl::~DefaultMQPushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } void DefaultMQPushConsumerImpl::start() { - BaseImpl::start(); + ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected consumer state: {}", state_.load(std::memory_order_relaxed)); @@ -170,16 +28,17 @@ void DefaultMQPushConsumerImpl::start() { fetchRoutes(); heartbeat(); - if (message_listener_ptr_) { - if (message_listener_ptr_->getMessageListenerType() == messageListenerOrderly) { + if (message_listener_) { + if (message_listener_->listenerType() == MessageListenerType::FIFO) { SPDLOG_INFO("start orderly consume service: {}", group_name_); - consume_message_service_ = std::make_shared( - shared_from_this(), consume_thread_pool_size_, message_listener_ptr_); + consume_message_service_ = + std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); + consume_batch_size_ = 1; } else { // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. SPDLOG_INFO("start concurrently consume service: {}", group_name_); - consume_message_service_ = std::make_shared( - shared_from_this(), consume_thread_pool_size_, message_listener_ptr_); + consume_message_service_ = std::make_shared( + shared_from_this(), consume_thread_pool_size_, message_listener_); } consume_message_service_->start(); @@ -190,7 +49,10 @@ void DefaultMQPushConsumerImpl::start() { consume_message_service_->throttle(item.first, item.second); } } + } else { + SPDLOG_WARN("Message listener is unexpected nullptr"); } + std::weak_ptr consumer_weak_ptr(shared_from_this()); auto scan_assignment_functor = [consumer_weak_ptr]() { std::shared_ptr consumer = consumer_weak_ptr.lock(); @@ -225,18 +87,13 @@ void DefaultMQPushConsumerImpl::shutdown() { } // Shutdown services started by parent - BaseImpl::shutdown(); + ClientImpl::shutdown(); State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED)) { SPDLOG_INFO("DefaultMQPushConsumerImpl stopped"); } } -bool DefaultMQPushConsumerImpl::isStopped() const { - State current_state = state_.load(std::memory_order_relaxed); - return State::STOPPED == current_state || State::STOPPING == current_state; -} - void DefaultMQPushConsumerImpl::subscribe(const std::string& topic, const std::string& expression, ExpressionType expression_type) { absl::MutexLock lock(&topic_filter_expression_table_mtx_); @@ -249,7 +106,7 @@ void DefaultMQPushConsumerImpl::unsubscribe(const std::string& topic) { topic_filter_expression_table_.erase(topic); } -absl::flat_hash_map DefaultMQPushConsumerImpl::getTopicFilterExpressionTable() { +absl::flat_hash_map DefaultMQPushConsumerImpl::getTopicFilterExpressionTable() const { absl::flat_hash_map topic_filter_expression_table; { absl::MutexLock lock(&topic_filter_expression_table_mtx_); @@ -267,7 +124,7 @@ void DefaultMQPushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_fro void DefaultMQPushConsumerImpl::scanAssignments() { SPDLOG_DEBUG("Start of assignment scanning"); - if (isStopped()) { + if (!active()) { SPDLOG_INFO("Client has stopped. Abort scanning immediately."); return; } @@ -421,7 +278,7 @@ void DefaultMQPushConsumerImpl::syncProcessQueue(const std::string& topic, ConsumeMessageType consume_type = MessageModel::CLUSTERING == message_model_ ? ConsumeMessageType::POP : ConsumeMessageType::PULL; if (!receiveMessage(message_queue, filter_expression, consume_type)) { - if (!isStopped()) { + if (!active()) { SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", message_queue.simpleName()); // TODO: remove it from current assignment such that a second attempt will be made again in the next round. } @@ -436,7 +293,7 @@ ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const M ProcessQueueSharedPtr process_queue; { absl::MutexLock lock(&process_queue_table_mtx_); - if (isStopped()) { + if (!active()) { SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop creation of ProcessQueue"); return process_queue; } @@ -446,9 +303,8 @@ ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const M } else { SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); // create ProcessQueue - process_queue = - std::make_shared(message_queue, filter_expression, consume_type, - max_cached_message_number_per_queue_, shared_from_this(), client_instance_); + process_queue = std::make_shared(message_queue, filter_expression, consume_type, shared_from_this(), + client_instance_); std::shared_ptr receive_callback = std::make_shared(process_queue); process_queue->callback(receive_callback); @@ -461,7 +317,7 @@ ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const M bool DefaultMQPushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, ConsumeMessageType consume_type) { - if (isStopped()) { + if (!active()) { SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop further receive message request"); return false; } @@ -529,7 +385,6 @@ void DefaultMQPushConsumerImpl::ack(const MQMessageExt& msg, const std::function void DefaultMQPushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { std::string target_host = MessageAccessor::targetEndpoint(msg); - SPDLOG_DEBUG("Send message nack to broker. brokerAddress={}", target_host); absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -545,10 +400,35 @@ void DefaultMQPushConsumerImpl::nack(const MQMessageExt& msg, const std::functio request.set_client_id(clientId()); request.set_receipt_handle(msg.receiptHandle()); request.set_message_id(msg.getMsgId()); - request.set_reconsume_times(msg.getReconsumeTimes() + 1); - request.set_max_reconsume_times(max_delivery_attempts_); + request.set_delivery_attempt(msg.getDeliveryAttempt() + 1); + request.set_max_delivery_attempts(max_delivery_attempts_); client_instance_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + SPDLOG_DEBUG("Send message nack to broker server[host={}]", target_host); +} + +void DefaultMQPushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, + const std::function& cb) { + std::string target_host = MessageAccessor::targetEndpoint(message); + + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + + ForwardMessageToDeadLetterQueueRequest request; + request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_name(group_name_); + + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(message.getTopic()); + + request.set_client_id(clientId()); + request.set_message_id(message.getMsgId()); + + request.set_delivery_attempt(message.getDeliveryAttempt()); + request.set_max_delivery_attempts(max_delivery_attempts_); + + client_instance_->forwardMessageToDeadLetterQueue(target_host, metadata, request, + absl::ToChronoMilliseconds(io_timeout_), cb); } void DefaultMQPushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { @@ -561,7 +441,7 @@ void DefaultMQPushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, A request.set_receipt_handle(msg.receiptHandle()); } -int DefaultMQPushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } +uint32_t DefaultMQPushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } void DefaultMQPushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { if (thread_pool_size >= 1) { @@ -572,24 +452,22 @@ void DefaultMQPushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { uint32_t DefaultMQPushConsumerImpl::consumeBatchSize() const { return consume_batch_size_; } void DefaultMQPushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { - if (consume_batch_size >= 1) { - consume_batch_size_ = consume_batch_size; + + // For FIFO messages, consume batch size should always be 1. + if (message_listener_ && message_listener_->listenerType() == MessageListenerType::FIFO) { + return; } -} -void DefaultMQPushConsumerImpl::maxCachedMessageNumberPerQueue(int max_cached_message_number_per_queue) { - if (max_cached_message_number_per_queue > 0 && max_cached_message_number_per_queue < MAX_CACHED_MESSAGE_COUNT) { - max_cached_message_number_per_queue_ = max_cached_message_number_per_queue; - } else { - max_cached_message_number_per_queue_ = DEFAULT_CACHED_MESSAGE_COUNT; + if (consume_batch_size >= 1) { + consume_batch_size_ = consume_batch_size; } } -void DefaultMQPushConsumerImpl::registerMessageListener(MQMessageListener* message_listener) { - message_listener_ptr_ = message_listener; +void DefaultMQPushConsumerImpl::registerMessageListener(MessageListener* message_listener) { + message_listener_ = message_listener; } -int DefaultMQPushConsumerImpl::getProcessQueueTableSize() { +std::size_t DefaultMQPushConsumerImpl::getProcessQueueTableSize() { absl::MutexLock lock(&process_queue_table_mtx_); return process_queue_table_.size(); } @@ -605,17 +483,19 @@ void DefaultMQPushConsumerImpl::setThrottle(const std::string& topic, uint32_t t #ifdef ENABLE_TRACING nostd::shared_ptr DefaultMQPushConsumerImpl::getTracer() { - if (nullptr == client_instance_) { + if (nullptr == client_manager_) { return nostd::shared_ptr(nullptr); } - return client_instance_->getTracer(); + return client_manager_->getTracer(); } #endif -void DefaultMQPushConsumerImpl::iterateProcessQueue(const std::function& functor) { +void DefaultMQPushConsumerImpl::iterateProcessQueue(const std::function& callback) { absl::MutexLock lock(&process_queue_table_mtx_); for (const auto& item : process_queue_table_) { - functor(item.second); + if (item.second->hasPendingMessages()) { + callback(item.second); + } } } @@ -632,7 +512,7 @@ void DefaultMQPushConsumerImpl::fetchRoutes() { return; } - int countdown = topics.size(); + std::vector::size_type countdown = topics.size(); absl::Mutex mtx; absl::CondVar cv; int acquired = 0; @@ -687,7 +567,7 @@ void DefaultMQPushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) } ClientResourceBundle DefaultMQPushConsumerImpl::resourceBundle() { - ClientResourceBundle resource_bundle = BaseImpl::resourceBundle(); + ClientResourceBundle resource_bundle = ClientImpl::resourceBundle(); { absl::MutexLock lk(&topic_filter_expression_table_mtx_); for (auto& item : topic_filter_expression_table_) { diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/rocketmq/FilterExpression.cpp new file mode 100644 index 000000000..43207e929 --- /dev/null +++ b/src/main/cpp/rocketmq/FilterExpression.cpp @@ -0,0 +1,26 @@ +#include "FilterExpression.h" + +ROCKETMQ_NAMESPACE_BEGIN + +bool FilterExpression::accept(const MQMessageExt& message) const { + + switch (type_) { + case ExpressionType::TAG: { + if (WILD_CARD_TAG == content_) { + return true; + } else { + return message.getTags() == content_; + } + } + + case ExpressionType::SQL92: { + // Server should have strictly filtered. + return true; + } + } + return true; +} + +const char* FilterExpression::WILD_CARD_TAG = "*"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueue.cpp b/src/main/cpp/rocketmq/ProcessQueue.cpp deleted file mode 100644 index 4ccf64fdd..000000000 --- a/src/main/cpp/rocketmq/ProcessQueue.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "ProcessQueue.h" -#include "ClientInstance.h" -#include "DefaultMQPushConsumerImpl.h" -#include "Metadata.h" -#include "Protocol.h" -#include "Signature.h" -#include -#include -#include -#include - -using namespace std::chrono; - -ROCKETMQ_NAMESPACE_BEGIN - -ProcessQueue::ProcessQueue(MQMessageQueue message_queue, FilterExpression filter_expression, - ConsumeMessageType consume_type, int max_cache_size, - std::weak_ptr call_back_owner, - std::shared_ptr client_instance) - : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), - consume_type_(consume_type), max_message_number_(MixAll::MAX_MESSAGE_NUMBER_PER_BATCH), - invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), message_cached_number_(0), - max_cache_size_(max_cache_size), simple_name_(message_queue_.simpleName()), - call_back_owner_(std::move(call_back_owner)), client_instance_(std::move(client_instance)) { - SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); -} - -ProcessQueue::~ProcessQueue() { - SPDLOG_INFO("ProcessQueue={} should have been re-balanced away, thus, is destructed", simpleName()); -} - -void ProcessQueue::callback(std::shared_ptr callback) { callback_ = std::move(callback); } - -bool ProcessQueue::expired() const { - auto duration = std::chrono::steady_clock::now() - last_poll_timestamp_; - auto throttle_duration = std::chrono::steady_clock::now() - last_throttle_timestamp_; - if (duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ && - throttle_duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_) { - SPDLOG_WARN("ProcessQueue={} is expired. Duration from last poll is: {}ms; from last throttle is: {}ms", - simpleName(), MixAll::millisecondsOf(duration), MixAll::millisecondsOf(throttle_duration)); - return true; - } - return false; -} - -bool ProcessQueue::shouldThrottle() const { - int current = message_cached_number_.load(std::memory_order_relaxed); - bool need_throttle = current >= max_cache_size_; - if (need_throttle) { - SPDLOG_INFO("{}: Number of locally cached messages is {}, which exceeds threshold={}", simple_name_, current, - max_cache_size_); - } - return need_throttle; -} - -void ProcessQueue::receiveMessage() { - switch (consume_type_) { - case ConsumeMessageType::POP: - popMessage(); - break; - case ConsumeMessageType::PULL: - pullMessage(); - break; - } -} - -void ProcessQueue::popMessage() { - rmq::ReceiveMessageRequest request; - - absl::flat_hash_map metadata; - auto consumer_client = call_back_owner_.lock(); - if (!consumer_client) { - return; - } - Signature::sign(consumer_client.get(), metadata); - - wrapPopMessageRequest(metadata, request); - last_poll_timestamp_ = std::chrono::steady_clock::now(); - SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); - client_instance_->receiveMessage(message_queue_.serviceAddress(), metadata, request, - absl::ToChronoMilliseconds(consumer_client->getIoTimeout()), callback_); -} - -void ProcessQueue::pullMessage() { - rmq::PullMessageRequest request; - absl::flat_hash_map metadata; - wrapPullMessageRequest(metadata, request); - client_instance_->pullMessage(message_queue_.serviceAddress(), metadata, request, callback_); -} - -void ProcessQueue::cacheMessages(const std::vector& messages) { - absl::MutexLock lock(&cached_messages_mtx_); - cached_messages_.reserve(cached_messages_.size() + messages.size()); - cached_messages_.insert(cached_messages_.end(), messages.begin(), messages.end()); - message_cached_number_.fetch_add(messages.size(), std::memory_order_relaxed); - SPDLOG_DEBUG("{}: Number of locally cached messages is: {}", message_queue_.simpleName(), - message_cached_number_.load(std::memory_order_relaxed)); -} - -bool ProcessQueue::consume(int batch_size, std::vector& messages) { - absl::MutexLock lock(&cached_messages_mtx_); - if (cached_messages_.empty()) { - return false; - } - - for (auto it = cached_messages_.begin(); it != cached_messages_.end();) { - if (--batch_size < 0) { - break; - } - - messages.push_back(*it); - it = cached_messages_.erase(it); - } - return !cached_messages_.empty(); -} - -void ProcessQueue::wrapPopMessageRequest(absl::flat_hash_map& metadata, - rmq::ReceiveMessageRequest& request) { - std::shared_ptr consumer = call_back_owner_.lock(); - assert(consumer); - request.set_client_id(consumer->clientId()); - request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_arn(consumer->arn()); - request.mutable_partition()->set_id(message_queue_.getQueueId()); - request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); - - auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); - if (consumer->getTopicFilterExpressionTable().end() != search) { - FilterExpression expression = search->second; - switch (expression.type_) { - case TAG: - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - case SQL92: - request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - } - } else { - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression("*"); - } - - // Batch size - request.set_batch_size(max_message_number_); - - // Consume policy - request.set_consume_policy(rmq::ConsumePolicy::RESUME); - - // Set invisible time - request.mutable_invisible_duration()->set_seconds( - std::chrono::duration_cast(invisible_time_).count()); - auto fraction = invisible_time_ - std::chrono::duration_cast(invisible_time_); - int32_t nano_seconds = static_cast(std::chrono::duration_cast(fraction).count()); - request.mutable_invisible_duration()->set_nanos(nano_seconds); -} - -void ProcessQueue::wrapPullMessageRequest(absl::flat_hash_map& metadata, - rmq::PullMessageRequest& request) { - std::shared_ptr consumer = call_back_owner_.lock(); - assert(consumer); - request.set_client_id(consumer->clientId()); - request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_arn(consumer->arn()); - request.mutable_partition()->set_id(message_queue_.getQueueId()); - request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); - request.set_offset(next_offset_); - request.set_batch_size(consumer->receiveBatchSize()); - auto filter_expression_table = consumer->getTopicFilterExpressionTable(); - auto search = filter_expression_table.find(message_queue_.getTopic()); - if (filter_expression_table.end() != search) { - FilterExpression expression = search->second; - switch (expression.type_) { - case TAG: - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - case SQL92: - request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - } - } else { - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression("*"); - } -} - -std::weak_ptr ProcessQueue::getCallbackOwner() { return call_back_owner_; } - -std::shared_ptr ProcessQueue::getClientInstance() { return client_instance_; } - -MQMessageQueue ProcessQueue::getMQMessageQueue() { return message_queue_; } - -const FilterExpression& ProcessQueue::getFilterExpression() const { return filter_expression_; } - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp new file mode 100644 index 000000000..6abadada3 --- /dev/null +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -0,0 +1,317 @@ +#include "ProcessQueueImpl.h" +#include "ClientManagerImpl.h" +#include "MetadataConstants.h" +#include "Protocol.h" +#include "PushConsumer.h" +#include "Signature.h" +#include +#include +#include +#include + +using namespace std::chrono; + +ROCKETMQ_NAMESPACE_BEGIN + +ProcessQueueImpl::ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, + ConsumeMessageType consume_type, std::weak_ptr consumer, + std::shared_ptr client_instance) + : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), + consume_type_(consume_type), invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), + simple_name_(message_queue_.simpleName()), consumer_(std::move(consumer)), + client_manager_(std::move(client_instance)), cached_message_quantity_(0), cached_message_memory_(0) { + SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); +} + +ProcessQueueImpl::~ProcessQueueImpl() { + SPDLOG_INFO("ProcessQueue={} should have been re-balanced away, thus, is destructed", simpleName()); +} + +void ProcessQueueImpl::callback(std::shared_ptr callback) { + receive_callback_ = std::move(callback); +} + +bool ProcessQueueImpl::expired() const { + auto duration = std::chrono::steady_clock::now() - idle_since_; + if (duration > MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_) { + SPDLOG_WARN("ProcessQueue={} is expired. It remains idle for {}ms", simpleName(), MixAll::millisecondsOf(duration)); + return true; + } + return false; +} + +bool ProcessQueueImpl::shouldThrottle() const { + auto consumer = consumer_.lock(); + if (!consumer) { + return false; + } + + std::size_t quantity = cached_message_quantity_.load(std::memory_order_relaxed); + uint32_t quantity_threshold = consumer->maxCachedMessageQuantity(); + uint64_t memory_threshold = consumer->maxCachedMessageMemory(); + bool need_throttle = quantity >= quantity_threshold; + if (need_throttle) { + SPDLOG_INFO("{}: Number of locally cached messages is {}, which exceeds threshold={}", simple_name_, quantity, + quantity_threshold); + return true; + } + + if (memory_threshold) { + uint64_t bytes = cached_message_memory_.load(std::memory_order_relaxed); + need_throttle = bytes >= memory_threshold; + if (need_throttle) { + SPDLOG_INFO("{}: Locally cached messages take {} bytes, which exceeds threshold={}", simple_name_, bytes, + memory_threshold); + return true; + } + } + return false; +} + +void ProcessQueueImpl::receiveMessage() { + switch (consume_type_) { + case ConsumeMessageType::POP: + popMessage(); + break; + case ConsumeMessageType::PULL: + pullMessage(); + break; + } +} + +void ProcessQueueImpl::popMessage() { + rmq::ReceiveMessageRequest request; + + absl::flat_hash_map metadata; + auto consumer_client = consumer_.lock(); + if (!consumer_client) { + return; + } + Signature::sign(consumer_client.get(), metadata); + + wrapPopMessageRequest(metadata, request); + syncIdleState(); + SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); + client_manager_->receiveMessage(message_queue_.serviceAddress(), metadata, request, + absl::ToChronoMilliseconds(consumer_client->getLongPollingTimeout()), + receive_callback_); +} + +void ProcessQueueImpl::pullMessage() { + rmq::PullMessageRequest request; + absl::flat_hash_map metadata; + wrapPullMessageRequest(metadata, request); + syncIdleState(); + SPDLOG_DEBUG("Try to pull message from {}", message_queue_.simpleName()); + + auto consumer = consumer_.lock(); + auto timeout = consumer->getLongPollingTimeout(); + + auto callback = [this](const InvocationContext* invocation_context) { + if (!invocation_context) { + MQException e("Transport layer failure", -1, __FILE__, __LINE__); + receive_callback_->onException(e); + return; + } + + if (invocation_context->status.ok()) { + if (google::rpc::Code::OK == invocation_context->response.common().status().code()) { + ReceiveMessageResult result; + client_manager_->processPullResult(invocation_context->context, invocation_context->response, result, + invocation_context->remote_address); + receive_callback_->onSuccess(result); + } else { + auto status = invocation_context->response.common().status(); + MQException e(status.message(), status.code(), __FILE__, __LINE__); + receive_callback_->onException(e); + } + } else { + MQException e(invocation_context->status.error_message(), invocation_context->status.error_code(), __FILE__, + __LINE__); + receive_callback_->onException(e); + } + }; + client_manager_->pullMessage(message_queue_.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(timeout), + callback); +} + +bool ProcessQueueImpl::hasPendingMessages() const { + absl::MutexLock lk(&messages_mtx_); + return !cached_messages_.empty(); +} + +void ProcessQueueImpl::cacheMessages(const std::vector& messages) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + { + absl::MutexLock messages_lock_guard(&messages_mtx_); + // TODO: use lock-when semantics + absl::MutexLock offsets_lock_guard(&offsets_mtx_); + for (const auto& message : messages) { + const std::string& msg_id = message.getMsgId(); + if (!filter_expression_.accept(message)) { + const std::string& topic = message.getTopic(); + auto callback = [topic, msg_id](bool ok) { + if (ok) { + SPDLOG_DEBUG("Ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression", topic, + msg_id); + } else { + SPDLOG_WARN("Failed to ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression", + topic, msg_id); + } + }; + consumer->ack(message, callback); + continue; + } + cached_messages_.emplace_back(message); + cached_message_quantity_.fetch_add(1, std::memory_order_relaxed); + cached_message_memory_.fetch_add(message.getBody().size(), std::memory_order_relaxed); + if (MessageModel::BROADCASTING == consumer->messageModel()) { + if (offsets_.size() == 1 && offsets_.begin()->released_) { + int64_t previously_released = offsets_.begin()->offset_; + offsets_.erase(OffsetRecord(previously_released)); + } + offsets_.emplace(message.getQueueOffset()); + } + } + } +} + +bool ProcessQueueImpl::take(uint32_t batch_size, std::vector& messages) { + absl::MutexLock lock(&messages_mtx_); + if (cached_messages_.empty()) { + return false; + } + + for (auto it = cached_messages_.begin(); it != cached_messages_.end();) { + if (0 == batch_size--) { + break; + } + + messages.push_back(*it); + it = cached_messages_.erase(it); + } + return !cached_messages_.empty(); +} + +bool ProcessQueueImpl::committedOffset(int64_t& offset) { + absl::MutexLock lk(&offsets_mtx_); + if (offsets_.empty()) { + return false; + } + if (offsets_.begin()->released_) { + offset = offsets_.begin()->offset_ + 1; + } else { + offset = offsets_.begin()->offset_; + } + return true; +} + +void ProcessQueueImpl::release(uint64_t body_size, int64_t offset) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + cached_message_quantity_.fetch_sub(1); + cached_message_memory_.fetch_sub(body_size); + + if (MessageModel::BROADCASTING == consumer->messageModel()) { + absl::MutexLock lk(&offsets_mtx_); + if (offsets_.size() > 1) { + offsets_.erase(OffsetRecord(offset)); + } else { + assert(offsets_.begin()->offset_ == offset); + offsets_.erase(OffsetRecord(offset)); + offsets_.emplace(OffsetRecord(offset, true)); + } + } +} + +void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request) { + std::shared_ptr consumer = consumer_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_arn(consumer->arn()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + + auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); + if (consumer->getTopicFilterExpressionTable().end() != search) { + FilterExpression expression = search->second; + switch (expression.type_) { + case TAG: + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + case SQL92: + request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + } + } else { + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression("*"); + } + + // Batch size + request.set_batch_size(consumer->receiveBatchSize()); + + // Consume policy + request.set_consume_policy(rmq::ConsumePolicy::RESUME); + + // Set invisible time + request.mutable_invisible_duration()->set_seconds( + std::chrono::duration_cast(invisible_time_).count()); + auto fraction = invisible_time_ - std::chrono::duration_cast(invisible_time_); + int32_t nano_seconds = static_cast(std::chrono::duration_cast(fraction).count()); + request.mutable_invisible_duration()->set_nanos(nano_seconds); +} + +void ProcessQueueImpl::wrapPullMessageRequest(absl::flat_hash_map& metadata, + rmq::PullMessageRequest& request) { + std::shared_ptr consumer = consumer_.lock(); + assert(consumer); + request.set_client_id(consumer->clientId()); + request.mutable_group()->set_name(consumer->getGroupName()); + request.mutable_group()->set_arn(consumer->arn()); + request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); + request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + request.set_offset(next_offset_); + request.set_batch_size(consumer->receiveBatchSize()); + auto filter_expression_table = consumer->getTopicFilterExpressionTable(); + auto search = filter_expression_table.find(message_queue_.getTopic()); + if (filter_expression_table.end() != search) { + FilterExpression expression = search->second; + switch (expression.type_) { + case TAG: + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + case SQL92: + request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); + request.mutable_filter_expression()->set_expression(expression.content_); + break; + } + } else { + request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); + request.mutable_filter_expression()->set_expression("*"); + } +} + +std::weak_ptr ProcessQueueImpl::getConsumer() { return consumer_; } + +std::shared_ptr ProcessQueueImpl::getClientManager() { return client_manager_; } + +MQMessageQueue ProcessQueueImpl::getMQMessageQueue() { return message_queue_; } + +const FilterExpression& ProcessQueueImpl::getFilterExpression() const { return filter_expression_; } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index 02f0f37f8..66027a14d 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -55,7 +55,7 @@ void RetrySendCallback::onException(const MQException& e) { return; } - std::shared_ptr client = client_instance_.lock(); + std::shared_ptr client = client_manager_.lock(); if (!client) { SPDLOG_WARN("Client instance has destructed"); callback_->onException(e); diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h new file mode 100644 index 000000000..0c415c37b --- /dev/null +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -0,0 +1,37 @@ +#pragma once + +#include "ProcessQueue.h" +#include "ReceiveMessageCallback.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AsyncReceiveMessageCallback : public ReceiveMessageCallback, + public std::enable_shared_from_this { +public: + explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); + + ~AsyncReceiveMessageCallback() override = default; + + void onSuccess(ReceiveMessageResult& result) override; + + void onException(MQException& e) override; + + void receiveMessageLater(); + + void receiveMessageImmediately(); + +private: + /** + * Hold a weak_ptr to ProcessQueue. Once ProcessQueue was released, stop the + * pop-cycle immediately. + */ + ProcessQueueWeakPtr process_queue_; + + std::function receive_message_later_; + + void checkThrottleThenReceive(); + + static const char* RECEIVE_LATER_TASK_NAME; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/BaseImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h similarity index 90% rename from src/main/cpp/rocketmq/include/BaseImpl.h rename to src/main/cpp/rocketmq/include/ClientImpl.h index 43bee54c2..723c8c823 100644 --- a/src/main/cpp/rocketmq/include/BaseImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -1,16 +1,20 @@ -#include "ClientInstance.h" +#include "Client.h" +#include "ClientConfigImpl.h" +#include "ClientManager.h" #include "ClientResourceBundle.h" #include "InvocationContext.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" +#include ROCKETMQ_NAMESPACE_BEGIN -class BaseImpl : public ClientConfig, public ClientCallback { +class ClientImpl : virtual public Client, + virtual public ClientConfigImpl { public: - explicit BaseImpl(std::string group_name); + explicit ClientImpl(std::string group_name); - ~BaseImpl() override = default; + ~ClientImpl() override = default; virtual void start(); @@ -40,8 +44,11 @@ class BaseImpl : public ClientConfig, public ClientCallback { void healthCheck() LOCKS_EXCLUDED(isolated_endpoints_mtx_) override; + void schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) override; + protected: - ClientInstancePtr client_instance_; + ClientManagerPtr client_instance_; std::atomic state_; absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); @@ -77,7 +84,7 @@ class BaseImpl : public ClientConfig, public ClientCallback { /** * Sub-class is supposed to inherit from std::enable_shared_from_this. */ - virtual std::shared_ptr self() = 0; + virtual std::shared_ptr self() = 0; virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index 71f5ff06f..75879f789 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -6,17 +6,18 @@ #include "ProcessQueue.h" #include "RateLimiter.h" -#include "rocketmq/MQMessageListener.h" +#include "absl/container/flat_hash_map.h" +#include "rocketmq/MessageListener.h" #include "rocketmq/State.h" #include "src/cpp/server/dynamic_thread_pool.h" ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPushConsumerImpl; +class PushConsumer; class ConsumeMessageService { public: - ConsumeMessageService() = default; + ConsumeMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); virtual ~ConsumeMessageService() = default; @@ -26,28 +27,37 @@ class ConsumeMessageService { ConsumeMessageService(const ConsumeMessageService& other) = delete; ConsumeMessageService& operator=(const ConsumeMessageService& other) = delete; - virtual void start() = 0; - - virtual void shutdown(); + /** + * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable + * functor. + */ + virtual void start(); /** - * - * @param process_queue_ptr - * @param permits submit at most permits messages to consume threads; if permits is negative, all messages in process - * queue should be submitted. + * Stop the dispatcher thread and then reset the thread pool. */ - virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr, int32_t permits) = 0; + virtual void shutdown(); - virtual MessageListenerType getConsumeMsgServiceListenerType() = 0; - virtual void stopThreadPool() = 0; + virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr) = 0; - virtual void dispatch() = 0; + virtual MessageListenerType messageListenerType() = 0; + /** + * Signal dispatcher thread to check new pending messages. + */ + void signalDispatcher(); + + /** + * Set throttle threshold per topic. + * + * @param topic + * @param threshold + */ void throttle(const std::string& topic, uint32_t threshold); - bool hasConsumeRateLimiter(const std::string& topic) const; + bool hasConsumeRateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); - std::shared_ptr> rateLimiter(const std::string& topic) const; + std::shared_ptr> rateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); protected: RateLimiterObserver rate_limiter_observer_; @@ -55,60 +65,71 @@ class ConsumeMessageService { mutable absl::flat_hash_map>> rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ + + std::atomic state_; + + int thread_count_; + std::unique_ptr pool_; + std::weak_ptr consumer_; + + absl::Mutex dispatch_mtx_; + std::thread dispatch_thread_; + absl::CondVar dispatch_cv_; + + MessageListener* message_listener_; + + /** + * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. + */ + void dispatch(); }; -class ConsumeMessageConcurrentlyService : public ConsumeMessageService { +class ConsumeStandardMessageService : public ConsumeMessageService { public: - ConsumeMessageConcurrentlyService(std::weak_ptr consumer, int thread_count, - MQMessageListener* message_listener_ptr); + ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener_ptr); - ~ConsumeMessageConcurrentlyService() override = default; + ~ConsumeStandardMessageService() override = default; void start() override; - void shutdown() override; - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) override; + void shutdown() override; - MessageListenerType getConsumeMsgServiceListenerType() override; - void stopThreadPool() override; + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - void dispatch() override; + MessageListenerType messageListenerType() override; private: void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); - - void dispatch0(); - - int thread_count_; - MQMessageListener* message_listener_ptr_; - std::atomic state_; - std::unique_ptr pool_; - std::weak_ptr consumer_weak_ptr_; - - absl::Mutex dispatch_mtx_; - std::thread dispatch_thread_; - absl::CondVar dispatch_cv_; }; -class ConsumeMessageOrderlyService : public ConsumeMessageService { +class ConsumeFifoMessageService : public ConsumeMessageService, + public std::enable_shared_from_this { public: - ConsumeMessageOrderlyService(const std::weak_ptr&& consumer_impl_ptr, int thread_count, - MQMessageListener* message_listener_ptr); + ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); void start() override; + void shutdown() override; - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue, int32_t permits) override; + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - MessageListenerType getConsumeMsgServiceListenerType() override; - void stopThreadPool() override; - void dispatch() override; + MessageListenerType messageListenerType() override; private: - void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); - std::weak_ptr consumer_weak_ptr_; - MQMessageListener* message_listener_ptr_; - int thread_count_; - std::unique_ptr pool_; + void consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message); + + void submitConsumeTask0(const std::shared_ptr& consumer, const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message); + + void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + + void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + + void scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h new file mode 100644 index 000000000..59b692511 --- /dev/null +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Client.h" +#include "FilterExpression.h" +#include "absl/container/flat_hash_map.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Consumer : virtual public Client { +public: + ~Consumer() override = default; + + virtual absl::flat_hash_map getTopicFilterExpressionTable() const = 0; + + virtual uint32_t maxCachedMessageQuantity() const = 0; + + virtual uint64_t maxCachedMessageMemory() const = 0; + + virtual int32_t receiveBatchSize() const = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h index 47dbd4d27..6e6e47540 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h @@ -5,9 +5,8 @@ #include #include -#include "BaseImpl.h" -#include "ClientConfig.h" -#include "ClientInstance.h" +#include "ClientImpl.h" +#include "ClientManagerImpl.h" #include "MixAll.h" #include "TopicPublishInfo.h" #include "TransactionImpl.h" @@ -20,7 +19,8 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_this { +class DefaultMQProducerImpl : virtual public ClientImpl, + public std::enable_shared_from_this { public: explicit DefaultMQProducerImpl(std::string group_name); @@ -83,7 +83,7 @@ class DefaultMQProducerImpl : public BaseImpl, public std::enable_shared_from_th void compressBodyThreshold(uint32_t threshold) { compress_body_threshold_ = threshold; } protected: - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { return shared_from_this(); } void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; diff --git a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h index 5295709c0..bf1b2097f 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h @@ -3,17 +3,18 @@ #include #include -#include "BaseImpl.h" -#include "ClientInstance.h" -#include "Identifiable.h" -#include "rocketmq/ConsumerType.h" +#include "ClientConfig.h" +#include "ClientImpl.h" +#include "ClientManagerImpl.h" +#include "rocketmq/ConsumeType.h" #include "rocketmq/MQMessageQueue.h" ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPullConsumerImpl : public BaseImpl, public std::enable_shared_from_this { +class DefaultMQPullConsumerImpl : public ClientImpl, + public std::enable_shared_from_this { public: - explicit DefaultMQPullConsumerImpl(std::string group_name) : BaseImpl(std::move(group_name)) {} + explicit DefaultMQPullConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} void start() override; @@ -28,7 +29,7 @@ class DefaultMQPullConsumerImpl : public BaseImpl, public std::enable_shared_fro void prepareHeartbeatData(HeartbeatRequest& request) override; protected: - std::shared_ptr self() override { + std::shared_ptr self() override { return shared_from_this(); } }; diff --git a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h index e1c2a0b56..a1bdaa148 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h @@ -4,12 +4,13 @@ #include #include -#include "BaseImpl.h" -#include "ClientConfig.h" -#include "ClientInstance.h" +#include "ClientConfigImpl.h" +#include "ClientImpl.h" +#include "ClientManagerImpl.h" #include "ConsumeMessageService.h" #include "FilterExpression.h" #include "ProcessQueue.h" +#include "PushConsumer.h" #include "Scheduler.h" #include "TopicAssignmentInfo.h" #include "TopicPublishInfo.h" @@ -22,10 +23,12 @@ ROCKETMQ_NAMESPACE_BEGIN class ConsumeMessageService; -class ConsumeMessageOrderlyService; -class ConsumeMessageConcurrentlyService; +class ConsumeFifoMessageService; +class ConsumeStandardMessageService; -class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_from_this { +class DefaultMQPushConsumerImpl : virtual public ClientImpl, + virtual public PushConsumer, + public std::enable_shared_from_this { public: explicit DefaultMQPushConsumerImpl(std::string group_name); @@ -43,12 +46,12 @@ class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_fro void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); - absl::flat_hash_map getTopicFilterExpressionTable() + absl::flat_hash_map getTopicFilterExpressionTable() const override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); void setConsumeFromWhere(ConsumeFromWhere consume_from_where); - void registerMessageListener(MQMessageListener* message_listener); + void registerMessageListener(MessageListener* message_listener); void scanAssignments() LOCKS_EXCLUDED(process_queue_table_mtx_); @@ -59,9 +62,10 @@ class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_fro QueryAssignmentRequest& request); /** - * Query assignment of the specified topic from load balancer directly if message consuming mode is clustering. In - * case current client is operating in the broadcasting mode, assignments are constructed locally from topic route - * entries. + * Query assignment of the specified topic from load balancer directly if + * message consuming mode is clustering. In case current client is operating + * in the broadcasting mode, assignments are constructed locally from topic + * route entries. * * @param topic Topic to query * @return shared pointer to topic assignment info @@ -77,44 +81,45 @@ class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_fro LOCKS_EXCLUDED(process_queue_table_mtx_); bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, - ConsumeMessageType consume_type) LOCKS_EXCLUDED(process_queue_table_mtx_); + ConsumeMessageType consume_type) override LOCKS_EXCLUDED(process_queue_table_mtx_); - int consumeThreadPoolSize() const; + uint32_t consumeThreadPoolSize() const; void consumeThreadPoolSize(int thread_pool_size); - uint32_t consumeBatchSize() const; + int32_t maxDeliveryAttempts() const override { return max_delivery_attempts_; } - int32_t receiveBatchSize() const { return receive_message_batch_size_; } + uint32_t consumeBatchSize() const override; void consumeBatchSize(uint32_t consume_batch_size); - void maxCachedMessageNumberPerQueue(int max_cached_message_number_per_queue); + int32_t receiveBatchSize() const override { return receive_batch_size_; } - std::shared_ptr getConsumeMessageService(); + std::shared_ptr getConsumeMessageService() override; - void ack(const MQMessageExt& msg, const std::function& callback); + void ack(const MQMessageExt& msg, const std::function& callback) override; /** - * Negative acknowledge the given message; Refer to https://en.wikipedia.org/wiki/Acknowledgement_(data_networks) for + * Negative acknowledge the given message; Refer to + * https://en.wikipedia.org/wiki/Acknowledgement_(data_networks) for * background info. * * Current implementation is to change invisible time of the given message. * * @param message Message to negate on the broker side. */ - void nack(const MQMessageExt& message, const std::function& callback); + void nack(const MQMessageExt& message, const std::function& callback) override; - void wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request); + void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) override; - bool isStopped() const; + void wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request); // only for test - int getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); + std::size_t getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); void setCustomExecutor(const Executor& executor) { custom_executor_ = executor; } - const Executor& customExecutor() const { return custom_executor_; } + const Executor& customExecutor() const override { return custom_executor_; } void setThrottle(const std::string& topic, uint32_t threshold); @@ -122,37 +127,55 @@ class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_fro nostd::shared_ptr getTracer(); #endif - MessageModel messageModel() const { return message_model_; } + MessageModel messageModel() const override { return message_model_; } void setMessageModel(MessageModel message_model) { message_model_ = message_model; } void offsetStore(std::unique_ptr offset_store) { offset_store_ = std::move(offset_store); } + void updateOffset(const MQMessageQueue& message_queue, int64_t offset) override { + if (offset_store_) { + offset_store_->updateOffset(message_queue, offset); + } + } + + /** + * Max number of messages that may be cached per queue before applying + * back-pressure. + * @return + */ + uint32_t maxCachedMessageQuantity() const override { return MixAll::DEFAULT_CACHED_MESSAGE_COUNT; } + + /** + * Threshold of total cached message body size by queue before applying + * back-pressure. + * @return + */ + uint64_t maxCachedMessageMemory() const override { return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; } + + void iterateProcessQueue(const std::function& callback) override; + protected: - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { return shared_from_this(); } ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; private: absl::flat_hash_map topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); - absl::Mutex topic_filter_expression_table_mtx_; + mutable absl::Mutex topic_filter_expression_table_mtx_; /** * Consume message thread pool size. */ - int consume_thread_pool_size_; + uint32_t consume_thread_pool_size_{MixAll::DEFAULT_CONSUME_THREAD_POOL_SIZE}; - MQMessageListener* message_listener_ptr_; + MessageListener* message_listener_{nullptr}; std::shared_ptr consume_message_service_; + uint32_t consume_batch_size_{MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE}; - uint32_t consume_batch_size_; - - // TODO: make it configurable - int32_t receive_message_batch_size_{32}; - - int max_cached_message_number_per_queue_; + int32_t receive_batch_size_{MixAll::DEFAULT_RECEIVE_MESSAGE_BATCH_SIZE}; std::uintptr_t scan_assignment_handle_{0}; static const char* SCAN_ASSIGNMENT_TASK_NAME; @@ -167,52 +190,17 @@ class DefaultMQPushConsumerImpl : public BaseImpl, public std::enable_shared_fro throttle_table_ GUARDED_BY(throttle_table_mtx_); absl::Mutex throttle_table_mtx_; - // TODO: Make default value configurable - int32_t max_delivery_attempts_{16}; + int32_t max_delivery_attempts_{MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS}; MessageModel message_model_{MessageModel::CLUSTERING}; - std::unique_ptr offset_store_; + mutable std::unique_ptr offset_store_; void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); - void iterateProcessQueue(const std::function& functor); - - friend class ConsumeMessageConcurrentlyService; - friend class ConsumeMessageOrderlyService; - - static const int32_t MAX_CACHED_MESSAGE_COUNT; - static const int32_t DEFAULT_CACHED_MESSAGE_COUNT; - static const int32_t DEFAULT_CONSUME_MESSAGE_BATCH_SIZE; - static const int32_t DEFAULT_CONSUME_THREAD_POOL_SIZE; -}; - -class AsyncReceiveMessageCallback : public ReceiveMessageCallback, - public std::enable_shared_from_this { -public: - explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); - - ~AsyncReceiveMessageCallback() override = default; - - void onSuccess(ReceiveMessageResult& result) override; - - void onException(MQException& e) override; - - void receiveMessageLater(); - - void receiveMessageImmediately(); - -private: - /** - * Hold a weak_ptr to ProcessQueue. Once ProcessQueue was released, stop the pop-cycle immediately. - */ - ProcessQueueWeakPtr process_queue_; - - std::function receive_message_later_; - - void checkThrottleThenReceive(); - - static const char* RECEIVE_LATER_TASK_NAME; + friend class ConsumeMessageService; + friend class ConsumeFifoMessageService; + friend class ConsumeStandardMessageService; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h index b0f4fa4bc..c71fe9ed0 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -1,7 +1,6 @@ #pragma once - +#include "rocketmq/MQMessageExt.h" #include "rocketmq/ExpressionType.h" -#include "rocketmq/RocketMQ.h" #include ROCKETMQ_NAMESPACE_BEGIN @@ -13,12 +12,17 @@ struct FilterExpression { explicit FilterExpression(std::string expression, ExpressionType expression_type = ExpressionType::TAG) : content_(std::move(expression)), type_(expression_type), version_(std::chrono::steady_clock::now()) { if (ExpressionType::TAG == type_ && content_.empty()) { - content_ = "*"; + content_ = WILD_CARD_TAG; } } + + bool accept(const MQMessageExt& message) const; + std::string content_; ExpressionType type_; std::chrono::steady_clock::time_point version_; + + static const char* WILD_CARD_TAG; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index 8a13b1524..f5ab21fea 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -1,139 +1,60 @@ #pragma once -#include -#include -#include -#include - -#include "ClientInstance.h" +#include "ConsumeMessageType.h" #include "FilterExpression.h" -#include "MixAll.h" #include "ReceiveMessageCallback.h" -#include "TopicAssignmentInfo.h" -#include "absl/container/flat_hash_map.h" -#include "apache/rocketmq/v1/service.pb.h" -#include "rocketmq/ConsumerType.h" #include "rocketmq/MQMessageExt.h" -#include "rocketmq/MQMessageQueue.h" +#include ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPushConsumerImpl; -class ProcessQueue { -public: - ProcessQueue(MQMessageQueue message_queue, FilterExpression filter_expression, ConsumeMessageType consume_type, - int max_cache_size, std::weak_ptr call_back_owner, - std::shared_ptr client_instance); - - ~ProcessQueue(); - - void callback(std::shared_ptr callback); - - MQMessageQueue getMQMessageQueue(); - - bool expired() const; - - bool shouldThrottle() const; - - const FilterExpression& getFilterExpression() const; +class PushConsumer; - std::weak_ptr getCallbackOwner(); +class ClientManager; - std::shared_ptr getClientInstance(); - - void receiveMessage(); - - const std::string& simpleName() const { return simple_name_; } - - std::string topic() const { return message_queue_.getTopic(); } - - /** - * Put message fetched from broker into cache. - * - * @param messages - */ - void cacheMessages(const std::vector& messages) LOCKS_EXCLUDED(cached_messages_mtx_); - - /** - * @return Number of messages that is not yet dispatched to thread pool, likely, due to topic-rate-limiting. - */ - uint32_t cachedMessagesSize() const LOCKS_EXCLUDED(cached_messages_mtx_) { - absl::MutexLock lk(&cached_messages_mtx_); - return cached_messages_.size(); - } - - /** - * Dispatch messages from cache to thread pool in form of consumeTask. - * @param batch_size - * @param messages - * @return true if there are more messages to consume in cache - */ - bool consume(int batch_size, std::vector& messages) LOCKS_EXCLUDED(cached_messages_mtx_); - - void updateThrottleTimestamp() { last_throttle_timestamp_ = std::chrono::steady_clock::now(); } - - std::atomic_int& messageCachedNumber() { return message_cached_number_; } +class ProcessQueue { +public: + virtual ~ProcessQueue() = default; - ConsumeMessageType consumeType() const { return consume_type_; } + virtual bool expired() const = 0; - void nextOffset(int64_t next_offset) { - assert(next_offset >= 0); - next_offset_ = next_offset; - } + virtual void callback(std::shared_ptr callback) = 0; - int64_t nextOffset() const { return next_offset_; } + virtual void receiveMessage() = 0; -private: - MQMessageQueue message_queue_; + virtual void nextOffset(int64_t next_offset) = 0; - /** - * Expression used to filter message in the server side. - */ - const FilterExpression filter_expression_; + virtual bool hasPendingMessages() const = 0; - ConsumeMessageType consume_type_{ConsumeMessageType::POP}; + virtual std::string topic() const = 0; - int max_message_number_; - std::chrono::milliseconds invisible_time_; + virtual bool take(uint32_t batch_size, std::vector& messages) = 0; - std::chrono::steady_clock::time_point last_poll_timestamp_{std::chrono::steady_clock::now()}; + virtual std::weak_ptr getConsumer() = 0; - std::chrono::steady_clock::time_point last_throttle_timestamp_{std::chrono::steady_clock::now()}; + virtual const std::string& simpleName() const = 0; - absl::Time born_timestamp_{absl::Now()}; + virtual MQMessageQueue getMQMessageQueue() = 0; - ConsumeInitialMode initial_mode_{ConsumeInitialMode::MAX}; + virtual bool committedOffset(int64_t& offset) = 0; - /** - * Number of messages that are not yet acknowledged. - */ - std::atomic_int message_cached_number_; + virtual void release(uint64_t body_size, int64_t offset) = 0; - /** - * Maximum number of locally cached messages. Once exceeding this threshold, fetch-loop should be throttled. - */ - int max_cache_size_; + virtual ConsumeMessageType consumeType() const = 0; - std::string simple_name_; + virtual void cacheMessages(const std::vector& messages) = 0; - // callback - std::weak_ptr call_back_owner_; - std::shared_ptr client_instance_; + virtual bool shouldThrottle() const = 0; - std::shared_ptr callback_; + virtual std::shared_ptr getClientManager() = 0; - mutable std::vector cached_messages_; - mutable absl::Mutex cached_messages_mtx_; + virtual void syncIdleState() = 0; - int64_t next_offset_{-1}; + virtual const FilterExpression& getFilterExpression() const = 0; - void popMessage(); - void wrapPopMessageRequest(absl::flat_hash_map& metadata, - rmq::ReceiveMessageRequest& request); + virtual bool bindFifoConsumeTask() = 0; - void pullMessage(); - void wrapPullMessageRequest(absl::flat_hash_map& metadata, - rmq::PullMessageRequest& request); + virtual bool unbindFifoConsumeTask() = 0; }; using ProcessQueueSharedPtr = std::shared_ptr; diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h new file mode 100644 index 000000000..094436788 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Assignment.h" +#include "ClientManager.h" +#include "FilterExpression.h" +#include "MixAll.h" +#include "ProcessQueue.h" +#include "ReceiveMessageCallback.h" +#include "TopicAssignmentInfo.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "rocketmq/ConsumeType.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessageQueue.h" +#include "gtest/gtest_prod.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct OffsetRecord { + explicit OffsetRecord(int64_t offset) : offset_(offset), released_(false) {} + OffsetRecord(int64_t offset, bool released) : offset_(offset), released_(released) {} + int64_t offset_; + bool released_; +}; + +ROCKETMQ_NAMESPACE_END + +namespace std { + +template <> struct less { + bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) { + return lhs.offset_ < rhs.offset_; + } +}; + +} // namespace std + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer; + +/** + * @brief Once messages are fetched(either pulled or popped) from remote server, they are firstly put into cache. + * Dispatcher thread, after waking up, will submit them into thread-pool. Messages at this phase are called "inflight" + * state. Once messages are processed by user-passed-in callback, their quota will be released for future incoming + * messages. + */ +class ProcessQueueImpl : virtual public ProcessQueue { +public: + ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, ConsumeMessageType consume_type, + std::weak_ptr consumer, std::shared_ptr client_instance); + + ~ProcessQueueImpl() override; + + void callback(std::shared_ptr callback) override; + + MQMessageQueue getMQMessageQueue() override; + + bool expired() const override; + + bool shouldThrottle() const override LOCKS_EXCLUDED(messages_mtx_); + + const FilterExpression& getFilterExpression() const override; + + std::weak_ptr getConsumer() override; + + std::shared_ptr getClientManager() override; + + void receiveMessage() override; + + const std::string& simpleName() const override { return simple_name_; } + + std::string topic() const override { return message_queue_.getTopic(); } + + bool hasPendingMessages() const override LOCKS_EXCLUDED(messages_mtx_); + + /** + * Put message fetched from broker into cache. + * + * @param messages + */ + void cacheMessages(const std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + + /** + * @return Number of messages that is not yet dispatched to thread pool, likely, due to topic-rate-limiting. + */ + uint32_t cachedMessagesSize() const LOCKS_EXCLUDED(messages_mtx_) { + absl::MutexLock lk(&messages_mtx_); + return cached_messages_.size(); + } + + /** + * Dispatch messages from cache to thread pool in form of consumeTask. + * @param batch_size + * @param messages + * @return true if there are more messages to consume in cache + */ + bool take(uint32_t batch_size, std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); + + void syncIdleState() override { idle_since_ = std::chrono::steady_clock::now(); } + + ConsumeMessageType consumeType() const override { return consume_type_; } + + /** + * @brief Expose for test purpose only. + * + * @param consume_type + */ + void consumeType(ConsumeMessageType consume_type) { consume_type_ = consume_type; } + + void nextOffset(int64_t next_offset) override { + assert(next_offset >= 0); + next_offset_ = next_offset; + } + + int64_t nextOffset() const { return next_offset_; } + + bool committedOffset(int64_t& offset) override LOCKS_EXCLUDED(offsets_mtx_); + + void release(uint64_t body_size, int64_t offset) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + + bool unbindFifoConsumeTask() override { + bool expected = true; + return has_fifo_task_bound_.compare_exchange_strong(expected, false, std::memory_order_relaxed); + } + + bool bindFifoConsumeTask() override { + bool expected = false; + return has_fifo_task_bound_.compare_exchange_strong(expected, true, std::memory_order_relaxed); + } + +private: + MQMessageQueue message_queue_; + + /** + * Expression used to filter message in the server side. + */ + const FilterExpression filter_expression_; + + ConsumeMessageType consume_type_{ConsumeMessageType::POP}; + + std::chrono::milliseconds invisible_time_; + + std::chrono::steady_clock::time_point idle_since_{std::chrono::steady_clock::now()}; + + absl::Time create_timestamp_{absl::Now()}; + + std::string simple_name_; + + std::weak_ptr consumer_; + std::shared_ptr client_manager_; + + std::shared_ptr receive_callback_; + + /** + * Messages that are pending to be submitted to thread pool. + */ + mutable std::vector cached_messages_ GUARDED_BY(messages_mtx_); + + mutable absl::Mutex messages_mtx_; + + /** + * @brief Quantity of the cached messages. + * + */ + std::atomic cached_message_quantity_; + + /** + * @brief Total body memory size of the cached messages. + * + */ + std::atomic cached_message_memory_; + + int64_t next_offset_{0}; + + /** + * If this process queue is used in FIFO scenario, this field marks if there is an task in thread pool. + */ + std::atomic_bool has_fifo_task_bound_{false}; + + std::set offsets_ GUARDED_BY(offsets_mtx_); + absl::Mutex offsets_mtx_; + + void popMessage(); + void wrapPopMessageRequest(absl::flat_hash_map& metadata, + rmq::ReceiveMessageRequest& request); + + void pullMessage(); + void wrapPullMessageRequest(absl::flat_hash_map& metadata, + rmq::PullMessageRequest& request); + + FRIEND_TEST(ProcessQueueTest, testExpired); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h new file mode 100644 index 000000000..848bd448a --- /dev/null +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -0,0 +1,44 @@ +#pragma once + +#include "ConsumeMessageService.h" +#include "Consumer.h" +#include "ProcessQueue.h" +#include "rocketmq/Executor.h" +#include "rocketmq/MessageModel.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer : virtual public Consumer { +public: + ~PushConsumer() override = default; + + virtual void iterateProcessQueue(const std::function& cb) = 0; + + virtual MessageModel messageModel() const = 0; + + virtual void ack(const MQMessageExt& msg, const std::function& callback) = 0; + + virtual void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) = 0; + + virtual const Executor& customExecutor() const = 0; + + virtual uint32_t consumeBatchSize() const = 0; + + virtual int32_t maxDeliveryAttempts() const = 0; + + virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; + + virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; + + virtual std::shared_ptr getConsumeMessageService() = 0; + + virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, + ConsumeMessageType consume_type) = 0; +}; + +using PushConsumerSharedPtr = std::shared_ptr; +using PushConsumerWeakPtr = std::weak_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index c08af2b28..8742f09e8 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -1,6 +1,6 @@ #pragma once -#include "ClientInstance.h" +#include "ClientManager.h" #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "apache/rocketmq/v1/service.grpc.pb.h" @@ -45,10 +45,10 @@ class AwaitSendCallback : public SendCallback { class RetrySendCallback : public SendCallback { public: - RetrySendCallback(std::weak_ptr client_instance, - absl::flat_hash_map metadata, SendMessageRequest request, - int max_attempt_times, SendCallback* callback, std::vector candidates) - : client_instance_(std::move(client_instance)), metadata_(std::move(metadata)), request_(std::move(request)), + RetrySendCallback(std::weak_ptr client_manager, absl::flat_hash_map metadata, + SendMessageRequest request, int max_attempt_times, SendCallback* callback, + std::vector candidates) + : client_manager_(std::move(client_manager)), metadata_(std::move(metadata)), request_(std::move(request)), max_attempt_times_(max_attempt_times), callback_(callback), candidates_(std::move(candidates)) {} void onSuccess(const SendResult& send_result) override; @@ -56,7 +56,7 @@ class RetrySendCallback : public SendCallback { void onException(const MQException& e) override; private: - std::weak_ptr client_instance_; + std::weak_ptr client_manager_; absl::flat_hash_map metadata_; SendMessageRequest request_; int attempt_times_{0}; diff --git a/src/main/cpp/rocketmq/mocks/CMakeLists.txt b/src/main/cpp/rocketmq/mocks/CMakeLists.txt new file mode 100644 index 000000000..3374fd42b --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(rocketmq_mocks INTERFACE) +target_include_directories(rocketmq_mocks + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(rocketmq_mocks + INTERFACE + rocketmq_library + gmock) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h new file mode 100644 index 000000000..7f5b7c357 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ClientMock.h" +#include "Consumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumerMock : virtual public Consumer, virtual public ClientMock { +public: + MOCK_METHOD((absl::flat_hash_map), getTopicFilterExpressionTable, (), (const override)); + + MOCK_METHOD(uint32_t, maxCachedMessageQuantity, (), (const override)); + + MOCK_METHOD(uint64_t, maxCachedMessageMemory, (), (const override)); + + MOCK_METHOD(int32_t, receiveBatchSize, (), (const override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h new file mode 100644 index 000000000..ae7ff1722 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h @@ -0,0 +1,49 @@ +#include "ProcessQueue.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProcessQueueMock : public ProcessQueue { +public: + MOCK_METHOD(bool, expired, (), (const override)); + + MOCK_METHOD(void, callback, (std::shared_ptr), (override)); + + MOCK_METHOD(void, receiveMessage, (), (override)); + + MOCK_METHOD(void, nextOffset, (int64_t), (override)); + + MOCK_METHOD(bool, hasPendingMessages, (), (const override)); + + MOCK_METHOD(std::string, topic, (), (const override)); + + MOCK_METHOD(bool, take, (uint32_t, (std::vector&)), (override)); + + MOCK_METHOD(std::weak_ptr, getConsumer, (), (override)); + + MOCK_METHOD(const std::string&, simpleName, (), (const override)); + + MOCK_METHOD(bool, committedOffset, (int64_t&), (override)); + + MOCK_METHOD(void, release, (uint64_t, int64_t), (override)); + + MOCK_METHOD(ConsumeMessageType, consumeType, (), (const override)); + + MOCK_METHOD(void, cacheMessages, (const std::vector&), (override)); + + MOCK_METHOD(bool, shouldThrottle, (), (const override)); + + MOCK_METHOD((std::shared_ptr), getClientManager, (), (override)); + + MOCK_METHOD(void, syncIdleState, (), (override)); + + MOCK_METHOD(const FilterExpression&, getFilterExpression, (), (const override)); + + MOCK_METHOD(bool, bindFifoConsumeTask, (), (override)); + + MOCK_METHOD(bool, unbindFifoConsumeTask, (), (override)); + + MOCK_METHOD(MQMessageQueue, getMQMessageQueue, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h new file mode 100644 index 000000000..7c82f650d --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ConsumerMock.h" +#include "PushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMock { +public: + MOCK_METHOD(void, iterateProcessQueue, (const std::function&), (override)); + + MOCK_METHOD(MessageModel, messageModel, (), (const override)); + + MOCK_METHOD(void, ack, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(void, forwardToDeadLetterQueue, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(const Executor&, customExecutor, (), (const override)); + + MOCK_METHOD(uint32_t, consumeBatchSize, (), (const override)); + + MOCK_METHOD(int32_t, maxDeliveryAttempts, (), (const override)); + + MOCK_METHOD(void, updateOffset, (const MQMessageQueue&, int64_t), (override)); + + MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); + + MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); + + MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&, ConsumeMessageType), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index 45c57b92b..18298c50b 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -17,7 +17,7 @@ cc_test( "RpcClientTest.cpp", ], deps = [ - "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/client:client_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], @@ -32,7 +32,7 @@ cc_test( "TopicPublishInfoTest.cpp", ], deps = [ - "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/client:client_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], diff --git a/src/test/cpp/it/ClientInstanceTest.cpp b/src/test/cpp/it/ClientInstanceTest.cpp index 1b2e95f12..710e6acb6 100644 --- a/src/test/cpp/it/ClientInstanceTest.cpp +++ b/src/test/cpp/it/ClientInstanceTest.cpp @@ -1,5 +1,5 @@ -#include "ClientInstance.h" -#include "ClientManager.h" +#include "ClientManagerFactory.h" +#include "ClientManagerImpl.h" #include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp index 9f24d8785..08e1e57ea 100644 --- a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp +++ b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp @@ -6,7 +6,7 @@ using namespace rocketmq; -class SampleMQMessageListener : public MessageListenerConcurrently { +class SampleMQMessageListener : public StandardMessageListener { public: ConsumeStatus consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { @@ -35,7 +35,7 @@ class DefaultMQPushConsumerTest : public testing::Test { protected: DefaultMQPushConsumer push_consumer_; - MQMessageListener* listener_; + MessageListener* listener_; }; TEST_F(DefaultMQPushConsumerTest, testStartupAndShutdown) { diff --git a/src/test/cpp/it/RpcClientIntegrationTest.cpp b/src/test/cpp/it/RpcClientIntegrationTest.cpp index 01de2c05d..e2df326b2 100644 --- a/src/test/cpp/it/RpcClientIntegrationTest.cpp +++ b/src/test/cpp/it/RpcClientIntegrationTest.cpp @@ -2,7 +2,7 @@ #include #include -#include "ClientConfig.h" +#include "ClientConfigImpl.h" #include "MixAll.h" #include "Protocol.h" #include "RpcClientImpl.h" diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 7c38f58ff..7af5975ee 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -1,5 +1,4 @@ -#include "ClientConfig.h" -#include "IdentifiableMock.h" +#include "ClientConfigImpl.h" #include "InvocationContext.h" #include "LogInterceptorFactory.h" #include "MixAll.h" @@ -38,10 +37,9 @@ class RpcClientTest : public ::testing::Test { tls_channel_credential_options_.watch_root_certs(); tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - credentials_observable_ = std::make_shared(); credentials_provider_ = std::make_shared(); - ON_CALL(*credentials_observable_, tenantId).WillByDefault(testing::ReturnRef(tenant_id_)); - ON_CALL(*credentials_observable_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + client_config_.tenantId(tenant_id_); + client_config_.setCredentialsProvider(credentials_provider_); } void SetUp() override { @@ -172,9 +170,8 @@ class RpcClientTest : public ::testing::Test { std::string tenant_id_{"sample-tenant"}; std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; - ClientConfig client_config_; absl::flat_hash_map metadata_; - std::shared_ptr credentials_observable_; + ClientConfigImpl client_config_; CredentialsProviderPtr credentials_provider_; std::shared_ptr certificate_provider_; grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index 34e80fe47..5c1bc0bfc 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -1,12 +1,12 @@ #include "TopicPublishInfo.h" -#include "IdentifiableMock.h" #include "LogInterceptorFactory.h" -#include "RpcClient.h" +#include "RpcClientImpl.h" #include "Signature.h" #include "TlsHelper.h" #include "rocketmq/MQMessageQueue.h" +#include "ClientConfigImpl.h" #include "gtest/gtest.h" -#include +#include "grpcpp/security/tls_credentials_options.h" ROCKETMQ_NAMESPACE_BEGIN @@ -37,10 +37,9 @@ class TopicPublishInfoTest : public ::testing::Test { if (!name_server_list.empty()) { target_ = *name_server_list.begin(); } - credentials_observable_ = std::make_shared(); std::vector> interceptor_factories; - ON_CALL(*credentials_observable_, tenantId).WillByDefault(testing::ReturnRef(tenant_id_)); - ON_CALL(*credentials_observable_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + client_config_.tenantId(tenant_id_); + client_config_.setCredentialsProvider(credentials_provider_); interceptor_factories.emplace_back(absl::make_unique()); auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); @@ -62,17 +61,16 @@ class TopicPublishInfoTest : public ::testing::Test { std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; std::string target_{"dns:grpc.dev:9876"}; - ClientConfig client_config_; + ClientConfigImpl client_config_; absl::flat_hash_map metadata_; std::shared_ptr completion_queue_; - std::shared_ptr client_; + std::shared_ptr client_; CredentialsProviderPtr credentials_provider_; std::shared_ptr certificate_provider_; grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_option_; std::shared_ptr server_authorization_check_config_; std::shared_ptr channel_credential_; grpc::ChannelArguments channel_arguments_; - std::shared_ptr credentials_observable_; }; TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { diff --git a/src/test/cpp/ut/AbseilTimeTest.cpp b/src/test/cpp/ut/AbseilTimeTest.cpp deleted file mode 100644 index bcd00b98b..000000000 --- a/src/test/cpp/ut/AbseilTimeTest.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "absl/time/time.h" -#include - -TEST(AbseilTimeTest, testTimeFormat) { - auto now = absl::FromChrono(std::chrono::system_clock::now()); - auto utc_time_zone = absl::UTCTimeZone(); - auto local_time_zone = absl::LocalTimeZone(); - const char* time_format = "%Y%m%dT%H%M%SZ"; - std::cout << "UTC: " << absl::FormatTime(time_format, now, utc_time_zone) << std::endl; - std::cout << "Beijing Time: " << absl::FormatTime(time_format, now, local_time_zone) << std::endl; -} \ No newline at end of file diff --git a/src/test/cpp/ut/AsanDemo.cpp b/src/test/cpp/ut/AsanDemo.cpp deleted file mode 100644 index b4fd0791e..000000000 --- a/src/test/cpp/ut/AsanDemo.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "gtest/gtest.h" -#include - -TEST(AsanTest, testMemoryLeakage) { - auto p = new int(1); - std::cout << *p << std::endl; -} \ No newline at end of file diff --git a/src/test/cpp/ut/BUILD b/src/test/cpp/ut/BUILD index aca78b243..9bf771b53 100644 --- a/src/test/cpp/ut/BUILD +++ b/src/test/cpp/ut/BUILD @@ -1,71 +1 @@ -load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") -cc_test( - name = "gtest_example", - srcs = [ - "GTestExample.cpp", - ], - deps = [ - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "log_test", - srcs = [ - "LogTest.cpp", - ], - deps = [ - "@com_github_gabime_spdlog//:spdlog", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "asan_demo", - srcs = [ - "AsanDemo.cpp", - ], - deps = [ - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "absl_time_test", - srcs = [ - "AbseilTimeTest.cpp", - ], - deps = [ - "@com_google_googletest//:gtest_main", - "@com_google_absl//absl/time", - ], -) - -cc_test( - name = "hex_test", - srcs = [ - "HexTest.cpp", - ], - deps = [ - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "zlib_test", - srcs = [ - "ZlibTest.cpp", - ], - deps = [ - "@zlib//:zlib", - "@com_google_googletest//:gtest_main", - ], -) - -cc_binary( - name = "mac_test", - srcs = [ - "MacTest.cpp", - ], -) \ No newline at end of file +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/CMakeLists.txt b/src/test/cpp/ut/CMakeLists.txt index 40f6f66b6..b5efbd2a1 100644 --- a/src/test/cpp/ut/CMakeLists.txt +++ b/src/test/cpp/ut/CMakeLists.txt @@ -1,36 +1,3 @@ -add_executable(gtest_example GTestExample.cpp) -target_link_libraries(gtest_example gtest_main) -add_test(ut_gtest_example gtest_example) - -add_executable(log_test LogTest.cpp) -target_link_libraries(log_test - spdlog - gtest_main) -add_test(ut_log_test log_test) - -add_executable(event_test EventTest.cpp) -target_link_libraries(event_test - gtest_main - uv_a) - -add_executable(zlib_test ZlibTest.cpp) -target_link_libraries(zlib_test - PRIVATE - gtest_main - zlib) - -add_executable(abseil_time_test AbseilTimeTest.cpp) -target_link_libraries(abseil_time_test - PRIVATE - gtest_main - absl::time) - -add_executable(hex_test HexTest.cpp) -target_link_libraries(hex_test - PRIVATE - absl::strings - gtest_main) - add_subdirectory(base) add_subdirectory(concurrent) add_subdirectory(scheduler) diff --git a/src/test/cpp/ut/EventTest.cpp b/src/test/cpp/ut/EventTest.cpp deleted file mode 100644 index 7360f9712..000000000 --- a/src/test/cpp/ut/EventTest.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "uv.h" -#include "gtest/gtest.h" -#include - -class EventTest : public testing::Test { -public: - static void sample_callback(uv_timer_t* handle) { - if (count++ > 3) { - uv_timer_stop(handle); - } - } - - void SetUp() {} - - void TearDown() {} - -protected: - static int count; -}; -int EventTest::count = 0; - -TEST_F(EventTest, testEvent) { - uv_loop_t* loop = uv_default_loop(); - uv_timer_t timer; - uv_timer_init(loop, &timer); - uv_timer_start(&timer, &EventTest::sample_callback, 10, 20); - uv_run(loop, UV_RUN_DEFAULT); - uv_loop_close(loop); - EXPECT_TRUE(EventTest::count >= 3); -} - -TEST_F(EventTest, test_loop_close) { - uv_loop_t* loop = uv_default_loop(); - if (uv_loop_alive(loop)) { - uv_loop_close(loop); - } -} \ No newline at end of file diff --git a/src/test/cpp/ut/GTestExample.cpp b/src/test/cpp/ut/GTestExample.cpp deleted file mode 100644 index 9f76467a5..000000000 --- a/src/test/cpp/ut/GTestExample.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "gtest/gtest.h" -#include - -TEST(GTestExample, testExample) { std::cout << "GTest works" << std::endl; } \ No newline at end of file diff --git a/src/test/cpp/ut/HexTest.cpp b/src/test/cpp/ut/HexTest.cpp deleted file mode 100644 index f7caf2d4b..000000000 --- a/src/test/cpp/ut/HexTest.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "absl/strings/str_cat.h" -#include "gtest/gtest.h" -#include - -TEST(HexTest, testHex) { - const char* data = "hello"; - const char dict[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - std::string s; - for (std::size_t i = 0; i < strlen(data); i++) { - char c = *(data + i); - s.append(&dict[(0xF0 & c) >> 4], 1); - s.append(&dict[(0x0F & c)], 1); - } - - std::string expect{"68656C6C6F"}; - EXPECT_EQ(expect, s); -} \ No newline at end of file diff --git a/src/test/cpp/ut/LogTest.cpp b/src/test/cpp/ut/LogTest.cpp deleted file mode 100644 index ddd8e1998..000000000 --- a/src/test/cpp/ut/LogTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "spdlog/sinks/rotating_file_sink.h" -#include "spdlog/sinks/stdout_color_sinks.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include - -TEST(SpdlogTest, testLog) { - std::cout << "It works" << std::endl; - std::cout << "It works" << std::endl; - spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); - spdlog::set_level(spdlog::level::trace); - spdlog::info("Welcome to the world of spdlog"); - spdlog::info("Code assistant is working"); - spdlog::info("It works in Visual Studio Code"); - - auto console_sink = std::make_shared(); - console_sink->set_level(spdlog::level::trace); - console_sink->set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); - - auto rotating_file_sink = std::make_shared("logs/log.txt", 65536, 3, true); - - spdlog::logger logger("multi-sink-logger", {console_sink, rotating_file_sink}); - logger.set_level(spdlog::level::debug); - logger.info("This log is expected present in both console and file"); - - spdlog::log(spdlog::source_loc(__FILE__, __LINE__, "testLog"), spdlog::level::debug, "Hello {}", "world"); - - SPDLOG_INFO("Hello, macro {}, {}:{}", "world", __FILE__, __LINE__); -} diff --git a/src/test/cpp/ut/MacTest.cpp b/src/test/cpp/ut/MacTest.cpp deleted file mode 100644 index 9a4adc50a..000000000 --- a/src/test/cpp/ut/MacTest.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -** showip.c -- show IP addresses for a host given on the command line -*/ - -#include -#include -#include -#include -#include - -#if defined(__APPLE__) -#include -#define AF_FAMILY AF_LINK -#elif defined(__linux__) -#include -#include -#include -#define AF_FAMILY AF_PACKET -#endif - -#include -#include -#include -#include -#include -#include - -#include - -bool macAddress(std::vector &mac) { - struct ifaddrs *ifap = nullptr, *ifaptr = nullptr; - if (getifaddrs(&ifap)) { - return false; - } - - for (ifaptr = ifap; ifaptr; ifaptr = ifaptr->ifa_next) { - if (!ifaptr->ifa_addr) { - continue; - } - - if (ifaptr->ifa_addr->sa_family == AF_FAMILY) { -#if defined(__APPLE__) - unsigned char* ptr = - reinterpret_cast(LLADDR(reinterpret_cast(ifaptr->ifa_addr))); -#elif defined(__linux__) - unsigned char* ptr = reinterpret_cast(reinterpret_cast(ifaptr->ifa_addr)->sll_addr); -#endif - bool all_zero = true; - for (int i = 0; i < 6; i++) { - if (*(ptr + i) != 0) { - all_zero = false; - break; - } - } - if (all_zero) { - std::cout << "Skip MAC address of Network Interface: " << ifaptr->ifa_name << std::endl; - continue; - } - std::cout << "Use MAC address of Network Interface: " << ifaptr->ifa_name << std::endl; - // MAC address has 48 bits - mac.resize(6); - memcpy(mac.data(), ptr, 6); - // break; - } else if (ifaptr->ifa_addr->sa_family == AF_INET) { - char buffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, &reinterpret_cast(ifaptr->ifa_addr)->sin_addr, buffer, INET_ADDRSTRLEN); - std::cout << "Inet: " << ifaptr->ifa_name << ": " << buffer << std::endl; - } - } - freeifaddrs(ifap); - return true; -} - -int main(int argc, char* argv[]) { - std::vector data; - if (macAddress(data)) { - for (auto& item : data) { - std::printf("%02X:", item); - } - std::cout << std::endl; - } - - return 0; -} \ No newline at end of file diff --git a/src/test/cpp/ut/ZlibTest.cpp b/src/test/cpp/ut/ZlibTest.cpp deleted file mode 100644 index 530880150..000000000 --- a/src/test/cpp/ut/ZlibTest.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "zlib.h" -#include "gtest/gtest.h" -#include - -TEST(ZlibTest, testDeflate) { - - uint32_t len = 1024 * 1024; - auto data = new unsigned char[len]; - - for (uint32_t i = 0; i < len; i++) { - data[i] = static_cast(i % 128); - } - - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - stream.avail_in = len; - stream.next_in = data; - - deflateInit(&stream, Z_BEST_COMPRESSION); - uint32_t bound = deflateBound(&stream, len); - auto compressed = new unsigned char[bound]; - stream.avail_out = bound; - stream.next_out = compressed; - - int status = deflate(&stream, Z_FINISH); - if (stream.msg) { - std::cout << stream.msg << std::endl; - } - EXPECT_EQ(Z_STREAM_END, status); - deflateEnd(&stream); - - z_stream inflate_stream; - inflate_stream.zfree = Z_NULL; - inflate_stream.zalloc = Z_NULL; - inflate_stream.opaque = Z_NULL; - - inflate_stream.avail_in = stream.total_out; - inflate_stream.next_in = compressed; - - uint32_t buffer_len = 1024 * 4; - auto buffer = new unsigned char[buffer_len]; - - inflateInit(&inflate_stream); - inflate_stream.next_out = buffer; - inflate_stream.avail_out = buffer_len; - - std::vector raw; - while (true) { - status = inflate(&inflate_stream, Z_SYNC_FLUSH); - - if (Z_STREAM_ERROR == status) { - if (inflate_stream.msg) { - std::cerr << inflate_stream.msg << std::endl; - } - break; - } - raw.reserve(raw.size() + buffer_len - inflate_stream.avail_out); - std::copy(&buffer[0], &buffer[buffer_len - inflate_stream.avail_out], std::back_inserter(raw)); - inflate_stream.avail_out = buffer_len; - inflate_stream.next_out = buffer; - - if (Z_STREAM_END == status) { - std::cout << "Inflation done! Total in: " << inflate_stream.total_in << ", Total out:" << inflate_stream.total_out - << std::endl; - break; - } - - if (Z_OK == status) { - std::cout << "Some data are inflated and flushed. Available in: " << inflate_stream.avail_in - << ", Total out:" << inflate_stream.total_out << std::endl; - } - } - - EXPECT_EQ(std::vector(data, data + len), raw); - - delete[] data; - delete[] compressed; - delete[] buffer; -} \ No newline at end of file diff --git a/src/test/cpp/ut/base/CMakeLists.txt b/src/test/cpp/ut/base/CMakeLists.txt index c4a88fe34..b175c241a 100644 --- a/src/test/cpp/ut/base/CMakeLists.txt +++ b/src/test/cpp/ut/base/CMakeLists.txt @@ -30,4 +30,10 @@ target_link_libraries(unique_id_generator_test PRIVATE gtest_main base_library) -add_test(ut_unique_id_generator unique_id_generator_test) \ No newline at end of file +add_test(ut_unique_id_generator unique_id_generator_test) + +add_executable(ghttp_client_test GHttpClientTest.cpp) +target_link_libraries(ghttp_client_test + PRIVATE + base_library + gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index 6c2c5fec0..dab0dff53 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -4,7 +4,20 @@ using namespace rocketmq; -class MixAllTest : public testing::Test {}; +class MixAllTest : public testing::Test { +public: + static std::string toUpperCase(const std::string& s) { + std::string result; + for (const char & c : s) { + if ('a' <= c && 'z' >= c) { + result.push_back(static_cast('A' + (c - 'a'))); + } else { + result.push_back(c); + } + } + return result; + } +}; TEST_F(MixAllTest, testValidate_empty_topic) { MQMessage message; @@ -53,4 +66,22 @@ TEST_F(MixAllTest, testHex) { std::vector bin; EXPECT_TRUE(MixAll::hexToBinary(hex, bin)); EXPECT_EQ(hex, MixAll::hex(bin.data(), bin.size())); +} + +TEST_F(MixAllTest, testMD5) { + std::string data("abc"); + std::string digest; + bool success = MixAll::md5(data, digest); + std::string expect("900150983CD24FB0D6963F7D28E17F72"); + EXPECT_TRUE(success); + EXPECT_EQ(digest, expect); +} + +TEST_F(MixAllTest, testSHA1) { + std::string data("abc"); + std::string digest; + bool ok = MixAll::sha1(data, digest); + EXPECT_TRUE(ok); + std::string expect("a9993e364706816aba3e25717850c26c9cd0d89d"); + EXPECT_EQ(digest, toUpperCase(expect)); } \ No newline at end of file diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel index cecd8e4a3..d77cae4ff 100644 --- a/src/test/cpp/ut/client/BUILD.bazel +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -19,8 +19,30 @@ cc_test( ], deps = [ "//src/main/cpp/client:client_library", - "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/client:client_mocks", "@com_google_googletest//:gtest_main", "@boringssl//:ssl", ], +) + +cc_test( + name = "client_test", + srcs = [ + "ClientTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "client_manager_test", + srcs = [ + "ClientManagerTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_mocks", + "@com_google_googletest//:gtest_main", + ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CMakeLists.txt b/src/test/cpp/ut/client/CMakeLists.txt index be090ed9b..4851bfd66 100644 --- a/src/test/cpp/ut/client/CMakeLists.txt +++ b/src/test/cpp/ut/client/CMakeLists.txt @@ -17,7 +17,13 @@ target_link_libraries(tls_helper_test add_executable(rpc_client_unit_test RpcClientTest.cpp) target_link_libraries(rpc_client_unit_test PRIVATE - rpc_client_mock + client_mocks client_library gtest_main) -add_test(ut_rpc_client rpc_client_unit_test) \ No newline at end of file +add_test(ut_rpc_client rpc_client_unit_test) + +add_executable(client_manager_test ClientManagerTest.cpp) +target_link_libraries(client_manager_test + PRIVATE + client_mocks + gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp new file mode 100644 index 000000000..319c54a1b --- /dev/null +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -0,0 +1,73 @@ +#include "ClientManagerImpl.h" +#include "RpcClientMock.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerTest : public testing::Test { +public: + void SetUp() override { + client_manager_ = std::make_shared(arn_); + client_manager_->start(); + rpc_client_ = std::make_shared>(); + ON_CALL(*rpc_client_, ok).WillByDefault(testing::Return(true)); + client_manager_->addRpcClient(target_host_, rpc_client_); + } + + void TearDown() override { client_manager_->shutdown(); } + +protected: + std::string arn_{"arn:mq://test"}; + std::string topic_{"TestTopic"}; + std::string target_host_{"ipv4:10.0.0.0:10911"}; + std::shared_ptr client_manager_; + std::shared_ptr> rpc_client_; + absl::Duration io_timeout_{absl::Seconds(3)}; +}; + +TEST_F(ClientManagerTest, testBasic) { + // Ensure that start/shutdown works well. +} + +TEST_F(ClientManagerTest, testResolveRoute) { + auto rpc_cb = [](const QueryRouteRequest& request, InvocationContext* invocation_context) { + auto partition = new rmq::Partition(); + partition->mutable_topic()->set_arn(request.topic().arn()); + partition->mutable_topic()->set_name(request.topic().name()); + partition->mutable_broker()->set_name("broker-0"); + partition->mutable_broker()->set_id(0); + auto address = new rmq::Address(); + address->set_host("10.0.0.1"); + address->set_port(10911); + partition->mutable_broker()->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + partition->mutable_broker()->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + invocation_context->response.mutable_partitions()->AddAllocated(partition); + + invocation_context->onCompletion(true); + }; + EXPECT_CALL(*rpc_client_, asyncQueryRoute).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(rpc_cb)); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + Metadata metadata; + QueryRouteRequest request; + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(topic_); + auto callback = [&](bool, const TopicRouteDataPtr&) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + client_manager_->resolveRoute(target_host_, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + EXPECT_TRUE(completed); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/test/cpp/ut/client/ClientTest.cpp new file mode 100644 index 000000000..2596f4901 --- /dev/null +++ b/src/test/cpp/ut/client/ClientTest.cpp @@ -0,0 +1,27 @@ +#include "ClientMock.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientTest : public testing::Test { +public: + void SetUp() override { + client_ = std::make_shared>(); + ON_CALL(*client_, active).WillByDefault(testing::Invoke([]() { + std::cout << "active() is invoked" << std::endl; + return true; + })); + } + + void TearDown() override {} + +protected: + std::shared_ptr> client_; +}; + +TEST_F(ClientTest, testActive) { EXPECT_TRUE(client_->active()); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 073bb7c6b..4ebf77470 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -33,39 +33,14 @@ cc_test( ], ) -cc_library( - name = "semaphore_library", - hdrs = [ - "include/Semaphore.h", - ], - srcs = [ - "Semaphore.cpp", - ], - strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", - deps = [ - "@com_google_absl//absl/base", - "@com_google_absl//absl/synchronization", - ], -) - -cc_test( - name = "semaphore_test", - srcs = [ - "SemaphoreTest.cpp", - ], - deps = [ - ":semaphore_library", - "@com_github_gabime_spdlog//:spdlog", - "@com_google_googletest//:gtest_main", - ], -) - cc_test( name = "process_queue_test", srcs = [ "ProcessQueueTest.cpp", ], deps = [ + "//src/main/cpp/client:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], @@ -79,7 +54,7 @@ cc_library( srcs = [], strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", deps = [ - "//src/main/cpp/client:rpc_client_mock", + "//src/main/cpp/client:client_mocks", "@com_google_googletest//:gtest_main", ], ) @@ -140,4 +115,39 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "filter_expression_test", + srcs = [ + "FilterExpressionTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "consume_message_service_test", + srcs = [ + "ConsumeStandardMessageServiceTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/rocketmq:rocketmq_mocks", + "//src/main/cpp/base:message_listener_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "consumer_test", + srcs = [ + "ConsumerTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_mocks", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt index 709bb8493..e885e7be3 100644 --- a/src/test/cpp/ut/rocketmq/CMakeLists.txt +++ b/src/test/cpp/ut/rocketmq/CMakeLists.txt @@ -1,11 +1,3 @@ -add_library(semaphore_library - Semaphore.cpp) -target_include_directories(semaphore_library - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(semaphore_library - absl::synchronization) - add_library(client_test_interface INTERFACE) target_include_directories(client_test_interface INTERFACE @@ -15,19 +7,11 @@ target_link_libraries(client_test_interface rpc_client_mock gtest_main) -add_executable(semaphore_test SemaphoreTest.cpp) -target_link_libraries(semaphore_test - gtest_main - semaphore_library - spdlog::spdlog) -add_test(ut_semaphore_test semaphore_test) - add_executable(default_mq_producer_test DefaultMQProducerTest.cpp) target_link_libraries(default_mq_producer_test PRIVATE client_test_interface - rocketmq_library - semaphore_library) + rocketmq_library) add_test(ut_default_mq_producer_test default_mq_producer_test) add_executable(default_mq_push_consumer_test DefaultMQPushConsumerTest.cpp) @@ -49,8 +33,12 @@ target_link_libraries(message_test add_executable(process_queue_test ProcessQueueTest.cpp) target_link_libraries(process_queue_test - gtest_main - rocketmq_library) + PRIVATE + gtest_main + base_mocks + client_mocks + rocketmq_mocks + rocketmq_library) add_executable(ut_credentials_provider CredentialsProviderTest.cpp) target_link_libraries(ut_credentials_provider @@ -61,4 +49,37 @@ add_executable(ut_send_callbacks SendCallbacksTest.cpp) target_link_libraries(ut_send_callbacks PRIVATE gtest_main - rocketmq_library) \ No newline at end of file + rocketmq_library) + +add_executable(consume_message_service ConsumeStandardMessageServiceTest.cpp) +target_link_libraries(consume_message_service + PRIVATE + base_mocks + gtest_main + rocketmq_library + rocketmq_mocks) + +add_executable(filter_expression_test FilterExpressionTest.cpp) +target_link_libraries(filter_expression_test + PRIVATE + gtest_main + rocketmq_library) + +add_executable(message_group_queue_selector_test MessageGroupQueueSelectorTest.cpp) +target_link_libraries(message_group_queue_selector_test + PRIVATE + gtest_main + rocketmq_library) + +add_executable(sts_credentials_provider_test StsCredentialsProviderImplTest.cpp) +target_link_libraries(sts_credentials_provider_test + PRIVATE + base_mocks + gtest_main + rocketmq_library) + +add_executable(consumer_test ConsumerTest.cpp) +target_link_libraries(consumer_test + PRIVATE + rocketmq_mocks + gtest_main) diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp new file mode 100644 index 000000000..f17dfdafa --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -0,0 +1,88 @@ +#include "ConsumeMessageService.h" +#include "MessageListenerMock.h" +#include "ProcessQueueMock.h" +#include "PushConsumerMock.h" +#include "grpc/grpc.h" +#include "rocketmq/MQMessageExt.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeStandardMessageServiceTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + consumer_ = std::make_shared>(); + ON_CALL(*consumer_, consumeBatchSize).WillByDefault(testing::Return(consume_batch_size_)); + ON_CALL(*consumer_, messageModel).WillByDefault(testing::Return(MessageModel::CLUSTERING)); + std::weak_ptr consumer = std::dynamic_pointer_cast(consumer_); + consume_standard_message_service_ = + std::make_shared(consumer, thread_count_, &message_listener_); + process_queue_ = std::make_shared>(); + ON_CALL(*process_queue_, topic).WillByDefault(testing::Return(topic_)); + auto mock_take = [this](uint32_t batch_size, std::vector& messages) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + message.setBody(body_); + messages.emplace_back(message); + return false; + }; + ON_CALL(*process_queue_, take).WillByDefault(testing::Invoke(mock_take)); + ON_CALL(*process_queue_, getConsumer).WillByDefault(testing::Return(consumer)); + ON_CALL(*consumer_, customExecutor).WillByDefault(testing::ReturnRef(executor_)); + } + + void TearDown() override { grpc_shutdown(); } + +protected: + int thread_count_{2}; + std::string topic_{"TestTopic"}; + std::string tag_{"TagA"}; + std::string body_{"Body Content"}; + uint32_t consume_batch_size_; + std::shared_ptr> consumer_; + std::shared_ptr consume_standard_message_service_; + testing::NiceMock message_listener_; + std::shared_ptr> process_queue_; + Executor executor_; +}; + +TEST_F(ConsumeStandardMessageServiceTest, testStartAndShutdown) { + consume_standard_message_service_->start(); + consume_standard_message_service_->shutdown(); +} + +TEST_F(ConsumeStandardMessageServiceTest, testConsume) { + ASSERT_FALSE(executor_); + consume_standard_message_service_->start(); + + auto callback = [this](const std::function& cb) { cb(process_queue_); }; + + ON_CALL(*consumer_, iterateProcessQueue).WillByDefault(testing::Invoke(callback)); + + bool completed = false; + bool success = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto listener_cb = [&](const std::vector& messages) { + absl::MutexLock lk(&mtx); + completed = true; + success = !messages.empty(); + cv.SignalAll(); + return ConsumeMessageResult::SUCCESS; + }; + + ON_CALL(message_listener_, consumeMessage).WillByDefault(testing::Invoke(listener_cb)); + EXPECT_CALL(*process_queue_, release).Times(testing::AtLeast(1)); + EXPECT_CALL(*consumer_, ack).Times(testing::AtLeast(1)); + + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + + consume_standard_message_service_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp new file mode 100644 index 000000000..2924a6e9f --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp @@ -0,0 +1,11 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "ConsumerMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(ConsumerMockTest, testMock) { + testing::NiceMock mock; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 56017e4bf..33504a4c3 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" #include #include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -21,4 +22,17 @@ TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { ASSERT_EQ(credentials.accessSecret(), access_secret); } +TEST_F(CredentialsProviderTest, testEnvironmentVariable) { + const char* access_key = "abc"; + const char* access_secret = "def"; + + setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY, access_key, 1); + setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET, access_secret, 1); + + EnvironmentVariablesCredentialsProvider provider; + const Credentials& credentials = provider.getCredentials(); + EXPECT_STREQ(access_key, credentials.accessKey().c_str()); + EXPECT_STREQ(access_secret, credentials.accessSecret().c_str()); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 16d4172fe..7b21b0b4c 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -1,10 +1,10 @@ #include "rocketmq/DefaultMQPushConsumer.h" -#include "ClientManager.h" +#include "ClientManagerFactory.h" #include "DefaultMQPushConsumerImpl.h" +#include "InvocationContext.h" #include "MQClientTest.h" #include "absl/time/time.h" #include "rocketmq/MQMessageExt.h" -#include "rocketmq/MQMessageListener.h" #include "spdlog/spdlog.h" #include @@ -148,6 +148,13 @@ class DefaultMQPushConsumerUnitTest : public MQClientTest { invocation_context->onCompletion(true); } + void + mockForwardMessageToDeadLetterQueue(const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) { + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); + invocation_context->onCompletion(true); + } + protected: std::shared_ptr> rpc_client_for_broker_; const int64_t max_offset_{100}; @@ -161,9 +168,9 @@ std::atomic_bool completed_{false}; std::mutex completion_mtx_; std::condition_variable completion_cv_; -class MessageListenerUnitTests : public ROCKETMQ_NAMESPACE::MessageListenerConcurrently { +class MessageListenerUnitTests : public StandardMessageListener { public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { if (!msgs.empty()) { std::unique_lock lk(completion_mtx_); bool expected = false; @@ -176,7 +183,7 @@ class MessageListenerUnitTests : public ROCKETMQ_NAMESPACE::MessageListenerConcu SPDLOG_INFO("Received a message[Topic={}, MessageId={}]", msg.getTopic(), msg.getMsgId()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - return ConsumeStatus::CONSUME_SUCCESS; + return ConsumeMessageResult::SUCCESS; } }; diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp new file mode 100644 index 000000000..465d41fd8 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp @@ -0,0 +1,28 @@ +#include "FilterExpression.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class FilterExpressionTest : public testing::Test { +public: + FilterExpressionTest() : filter_expression_("TagA") {} + + void SetUp() override {} + + void TearDown() override {} + +protected: + FilterExpression filter_expression_; +}; + +TEST_F(FilterExpressionTest, testAccept) { + MQMessageExt message; + message.setTags("TagA"); + EXPECT_TRUE(filter_expression_.accept(message)); + + MQMessageExt message2; + message2.setTags("TagB"); + EXPECT_FALSE(filter_expression_.accept(message2)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 0526a9ca5..2329f0eaf 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -1,8 +1,340 @@ -#include "ProcessQueue.h" +#include "Assignment.h" +#include "ClientManagerMock.h" +#include "ConsumeMessageType.h" +#include "InvocationContext.h" +#include "MessageAccessor.h" +#include "ProcessQueueImpl.h" +#include "PushConsumerMock.h" +#include "ReceiveMessageCallbackMock.h" +#include "ReceiveMessageResult.h" +#include "RpcClientMock.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/CredentialsProvider.h" +#include "rocketmq/MQMessageExt.h" #include "gtest/gtest.h" +#include +#include +#include +#include +#include ROCKETMQ_NAMESPACE_BEGIN -class ProcessQueueTest : public testing::Test {}; +class ProcessQueueTest : public testing::Test { +public: + void SetUp() override { + rpc_client_ = std::make_shared>(); + message_queue_.serviceAddress(service_address_); + message_queue_.setTopic(topic_); + message_queue_.setBrokerName(broker_name_); + message_queue_.setQueueId(queue_id_); + client_manager_ = std::make_shared>(); + credentials_provider_ = std::make_shared(access_key_, access_secret_); + consumer_ = std::make_shared>(); + auto consumer = std::dynamic_pointer_cast(consumer_); + process_queue_ = std::make_shared(message_queue_, filter_expression_, ConsumeMessageType::POP, + consumer, client_manager_); + receive_message_callback_ = std::make_shared>(); + process_queue_->callback(receive_message_callback_); + } + + void TearDown() override {} + +protected: + std::string tenant_id_{"tenant-0"}; + std::string access_key_{"ak"}; + std::string access_secret_{"secret"}; + std::shared_ptr credentials_provider_; + std::string group_name_{"TestGroup"}; + std::string client_id_{"Client-0"}; + std::string broker_name_{"broker-a"}; + std::string region_{"cn-hangzhou"}; + std::string service_name_{"MQ"}; + int queue_id_{0}; + std::string topic_{"TestTopic"}; + std::string service_address_{"ipv4:10.0.0.1:10911"}; + std::string tag_{"TagA"}; + FilterExpression filter_expression_{tag_}; + MQMessageQueue message_queue_; + std::shared_ptr> rpc_client_; + std::shared_ptr> client_manager_; + std::shared_ptr> consumer_; + std::shared_ptr process_queue_; + std::shared_ptr> receive_message_callback_; + std::string arn_{"arn:test"}; + std::string message_body_{"Sample body"}; + + uint32_t threshold_quantity_{32}; + uint64_t threshold_memory_{4096}; + uint32_t consume_batch_size_{8}; +}; + +TEST_F(ProcessQueueTest, testBind) { + EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); + EXPECT_FALSE(process_queue_->bindFifoConsumeTask()); + EXPECT_TRUE(process_queue_->unbindFifoConsumeTask()); + EXPECT_FALSE(process_queue_->unbindFifoConsumeTask()); + EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); +} + +TEST_F(ProcessQueueTest, testExpired) { + EXPECT_FALSE(process_queue_->expired()); + process_queue_->idle_since_ -= MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_; + EXPECT_TRUE(process_queue_->expired()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle) { + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_FALSE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle_ByQuantity) { + std::vector messages; + for (uint32_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::to_string(i)); + messages.emplace_back(message); + } + + process_queue_->cacheMessages(messages); + + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_TRUE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testShouldThrottle_ByMemory) { + std::vector messages; + size_t body_length = 1024 * 4; + for (uint32_t i = 0; i < threshold_quantity_ / 2; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + + process_queue_->cacheMessages(messages); + + EXPECT_CALL(*consumer_, maxCachedMessageQuantity) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, maxCachedMessageMemory) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(threshold_memory_)); + EXPECT_TRUE(process_queue_->shouldThrottle()); +} + +TEST_F(ProcessQueueTest, testHasPendingMessages) { EXPECT_FALSE(process_queue_->hasPendingMessages()); } + +TEST_F(ProcessQueueTest, testHasPendingMessages2) { + std::vector messages; + size_t body_length = 1024; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_TRUE(process_queue_->hasPendingMessages()); +} + +TEST_F(ProcessQueueTest, testTake) { + std::vector messages; + EXPECT_FALSE(process_queue_->take(consume_batch_size_, messages)); + EXPECT_TRUE(messages.empty()); +} + +TEST_F(ProcessQueueTest, testTake2) { + + { + std::vector messages; + size_t body_length = 1024; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + EXPECT_TRUE(process_queue_->take(consume_batch_size_, msgs)); + EXPECT_FALSE(msgs.empty()); + EXPECT_EQ(tag_, msgs.begin()->getTags()); + EXPECT_EQ(topic_, msgs.begin()->getTopic()); + EXPECT_EQ(threshold_quantity_ - consume_batch_size_, process_queue_->cachedMessagesSize()); +} + +TEST_F(ProcessQueueTest, testRelease) { + EXPECT_CALL(*consumer_, messageModel) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); + + int64_t offset; + EXPECT_FALSE(process_queue_->committedOffset(offset)); + + size_t body_length = 1024; + { + std::vector messages; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + process_queue_->take(1, msgs); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(0, offset); + + process_queue_->release(body_length, 0); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(1, offset); +} + +TEST_F(ProcessQueueTest, testOffset) { + EXPECT_CALL(*consumer_, messageModel) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); + + int64_t offset; + EXPECT_FALSE(process_queue_->committedOffset(offset)); + + size_t body_length = 1024; + { + std::vector messages; + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + MessageAccessor::setQueueId(message, 0); + MessageAccessor::setQueueOffset(message, i); + message.setBody(std::string(body_length, 'c')); + messages.emplace_back(message); + } + process_queue_->cacheMessages(messages); + EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); + } + + std::vector msgs; + process_queue_->take(threshold_quantity_, msgs); + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(0, offset); + + for (size_t i = 0; i < threshold_quantity_; i++) { + process_queue_->release(body_length, i); + } + + EXPECT_TRUE(process_queue_->committedOffset(offset)); + EXPECT_EQ(threshold_quantity_, offset); +} + +TEST_F(ProcessQueueTest, testReceiveMessage_POP) { + EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); + EXPECT_CALL(*consumer_, arn).WillRepeatedly(testing::ReturnRef(arn_)); + EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); + EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); + EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); + EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); + EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); + EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + + absl::flat_hash_map filter_table; + filter_table.insert({topic_, filter_expression_}); + + EXPECT_CALL(*consumer_, getTopicFilterExpressionTable).WillRepeatedly(testing::Return(filter_table)); + EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); + + auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, + const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, + std::shared_ptr& cb) { + ReceiveMessageResult receive_message_result; + receive_message_result.status_ = ReceiveMessageStatus::OK; + + for (size_t i = 0; i < threshold_quantity_; i++) { + MQMessageExt message; + message.setTopic(topic_); + message.setTags(tag_); + message.setBody(message_body_); + MessageAccessor::setQueueId(message, queue_id_); + MessageAccessor::setQueueOffset(message, i); + receive_message_result.messages_.emplace_back(message); + } + cb->onSuccess(receive_message_result); + }; + + EXPECT_CALL(*client_manager_, receiveMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(receive_message_mock)); + process_queue_->receiveMessage(); +} + +TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { + process_queue_->consumeType(ConsumeMessageType::PULL); + EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); + EXPECT_CALL(*consumer_, arn).WillRepeatedly(testing::ReturnRef(arn_)); + EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); + EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); + EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); + EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); + EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); + EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + + absl::flat_hash_map filter_table; + filter_table.insert({topic_, filter_expression_}); + + EXPECT_CALL(*consumer_, getTopicFilterExpressionTable).WillRepeatedly(testing::Return(filter_table)); + EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); + + auto invocation_context = new InvocationContext(); + auto pull_message_mock = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(invocation_context); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(pull_message_mock)); + EXPECT_CALL(*client_manager_, processPullResult); + process_queue_->receiveMessage(); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/Semaphore.cpp b/src/test/cpp/ut/rocketmq/Semaphore.cpp deleted file mode 100644 index 235248823..000000000 --- a/src/test/cpp/ut/rocketmq/Semaphore.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "Semaphore.h" - -using namespace rocketmq; - -void Semaphore::acquire() { - absl::MutexLock lock(&mtx_); - if (value_ > 0) { - value_--; - return; - } else { - while (value_ <= 0) { - cv_.Wait(&mtx_); - } - value_--; - return; - } -} - -void Semaphore::release() { - { - absl::MutexLock lock(&mtx_); - value_++; - } - cv_.Signal(); -} diff --git a/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp b/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp deleted file mode 100644 index 1fdab567b..000000000 --- a/src/test/cpp/ut/rocketmq/SemaphoreTest.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Semaphore.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include - -using namespace rocketmq; - -TEST(SempahoreTest, testSemaphore) { - Semaphore limiter(0); - std::thread t([&] { - auto start = std::chrono::system_clock::now(); - spdlog::info("Start to acquire"); - limiter.acquire(); - spdlog::info("Acquired"); - auto duration = std::chrono::system_clock::now() - start; - EXPECT_TRUE(std::chrono::duration_cast(duration).count() >= 1); - }); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - spdlog::info("Releasing token..."); - limiter.release(); - spdlog::info("Token released"); - - if (t.joinable()) { - t.join(); - } -} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index f9bba3d5b..54a101950 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -1,13 +1,13 @@ #pragma once -#include "ClientManager.h" +#include "ClientManagerFactory.h" #include "RpcClientMock.h" +#include "grpc/grpc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include -#include "grpc/grpc.h" ROCKETMQ_NAMESPACE_BEGIN @@ -17,7 +17,7 @@ class MQClientTest : public testing::Test { void SetUp() override { name_server_list_.emplace_back(name_server_address_); - client_instance_ = std::make_shared(arn_); + client_instance_ = std::make_shared(arn_); rpc_client_ns_ = std::make_shared>(); ON_CALL(*rpc_client_ns_, needHeartbeat()).WillByDefault(testing::Return(false)); ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); @@ -25,7 +25,7 @@ class MQClientTest : public testing::Test { .WillByDefault(testing::Invoke( std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); - ClientManager::getInstance().addClientInstance(arn_, client_instance_); + ClientManagerFactory::getInstance().addClientInstance(arn_, client_instance_); } void TearDown() override { @@ -36,7 +36,7 @@ class MQClientTest : public testing::Test { protected: grpc::internal::GrpcLibraryInitializer initializer_; - std::shared_ptr client_instance_; + std::shared_ptr client_instance_; std::shared_ptr> rpc_client_ns_; const int16_t port_{10911}; const int32_t partition_num_{24}; diff --git a/src/test/cpp/ut/rocketmq/include/Semaphore.h b/src/test/cpp/ut/rocketmq/include/Semaphore.h deleted file mode 100644 index ab24e554b..000000000 --- a/src/test/cpp/ut/rocketmq/include/Semaphore.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" - -namespace rocketmq { -class Semaphore { -public: - Semaphore(int value) : value_(value) {} - - void acquire(); - - void release(); - -private: - int value_; - absl::Mutex mtx_; - absl::CondVar cv_; -}; -} // namespace rocketmq From bc3a7e9a743f989e51fbc6adeb5e2e72f81a384f Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 2 Aug 2021 15:47:32 +0800 Subject: [PATCH 020/133] STS bears Security Token header when making requests (#6) Add session-token when sign with STS access key/secret --- README.md | 3 +++ api/rocketmq/Credentials.h | 8 +++++--- docs/DevelopmentGuide.md | 18 ------------------ src/main/cpp/base/MetadataConstants.cpp | 1 + src/main/cpp/base/include/MetadataConstants.h | 1 + src/main/cpp/client/Signature.cpp | 4 ++++ src/main/cpp/rocketmq/CredentialsProvider.cpp | 14 +++++++------- .../include/StsCredentialsProviderImpl.h | 4 ++-- 8 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 docs/DevelopmentGuide.md diff --git a/README.md b/README.md index c80c6a0ef..f9db365b3 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ if "com_google_googletest" not in native.existing_rules(): ### IDE [Visual Studio Code](https://code.visualstudio.com/) + [Clangd](https://clangd.llvm.org/) is the recommended development toolset. 1. VSCode + Clangd + [Clangd](https://clangd.llvm.org/) is a really nice code completion tool. Clangd requires compile_commands.json to work properly. To generate the file, we need clone another repository along with the current one. @@ -85,5 +86,7 @@ if "com_google_googletest" not in native.existing_rules(): "clangd.arguments": ["-log=verbose", "-pretty", "--background-index"], "clangd.onConfigChanged": "restart", ``` + 2. CLion + Bazel Plugin + Bazel also has a plugin for CLion. \ No newline at end of file diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index a72bdb677..1b19a2f22 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -15,10 +15,10 @@ class Credentials { : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), expiration_instant_(std::chrono::system_clock::time_point::max()) {} - Credentials(std::string access_key, std::string access_secret, std::string token, + Credentials(std::string access_key, std::string access_secret, std::string session_token, std::chrono::system_clock::time_point expiration) - : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), session_token_(std::move(token)), - expiration_instant_(expiration) {} + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), + session_token_(std::move(session_token)), expiration_instant_(expiration) {} bool operator==(const Credentials& rhs) const { return access_key_ == rhs.access_key_ && access_secret_ == rhs.access_secret_ && @@ -35,6 +35,8 @@ class Credentials { const std::string& accessSecret() const { return access_secret_; } + const std::string& sessionToken() const { return session_token_; } + std::chrono::system_clock::time_point expirationInstant() const { return expiration_instant_; } private: diff --git a/docs/DevelopmentGuide.md b/docs/DevelopmentGuide.md deleted file mode 100644 index 6933d804d..000000000 --- a/docs/DevelopmentGuide.md +++ /dev/null @@ -1,18 +0,0 @@ -### 前言 -C++是一门复杂的开发语言. 我们需要遵从一些约定, 以降低使用它的复杂度, 提升工程的可读性. 目前业界最广为接受的约定无疑是Google的 -[C++ Style Guide](https://google.github.io/styleguide/cppguide.html)了, 本项目尽量遵从这个约定. - -尽量, 主要是出于考虑商业产品API的一致性. 我们需要和现有[SDK API](http://gitlab.alibaba-inc.com/middleware/rocketmq-client4cpp)保持一致. -这样, 我们就有一些妥协的结果. 以异常为例, 在实现内部, 我们不适用异常. 但在DefaultMQProducer这个层面, 为了保持已有API兼容, 我们需 -要将返回值转义为异常. - -### 编译工具 -项目使用CMake和Bazel两种管理工具. - - -### Define the following environment variables -export no_grpc_proxy=mq.docker.dev -export GRPC_TRACE=all -export GRPC_VERBOSITY=DEBUG -export http_proxy=http://30.57.177.111:3128 -export https_proxy=http://30.57.177.111:3128 \ No newline at end of file diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp index 5f29a06a2..718cdbac1 100644 --- a/src/main/cpp/base/MetadataConstants.cpp +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -5,6 +5,7 @@ ROCKETMQ_NAMESPACE_BEGIN const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; const char* MetadataConstants::ARN_KEY = "x-mq-arn"; const char* MetadataConstants::AUTHORIZATION = "authorization"; +const char* MetadataConstants::STS_SESSION_TOKEN = "x-mq-session-token"; const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; const char* MetadataConstants::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; const char* MetadataConstants::CREDENTIAL_KEY = "Credential"; diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index fdccf183c..61260d278 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -9,6 +9,7 @@ class MetadataConstants { static const char* TENANT_ID_KEY; static const char* ARN_KEY; static const char* AUTHORIZATION; + static const char* STS_SESSION_TOKEN; static const char* DATE_TIME_KEY; static const char* ALGORITHM_KEY; static const char* CREDENTIAL_KEY; diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index 250692d2c..e72c1237d 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -55,6 +55,10 @@ void Signature::sign(ClientConfig* client, absl::flat_hash_map& headers, - const std::string body) { - SPDLOG_DEBUG("Received STS reponse. Code: {}", code); + const std::string& body) { + SPDLOG_DEBUG("Received STS response. Code: {}", code); if (GHttpClient::STATUS_OK == code) { google::protobuf::Struct doc; google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(body, &doc); @@ -135,8 +135,8 @@ void StsCredentialsProviderImpl::refresh() { std::string access_key = fields.at(FIELD_ACCESS_KEY).string_value(); assert(fields.contains(FIELD_ACCESS_SECRET)); std::string access_secret = fields.at(FIELD_ACCESS_SECRET).string_value(); - assert(fields.contains(FIELD_SECURITY_TOKEN)); - std::string security_token = fields.at(FIELD_SECURITY_TOKEN).string_value(); + assert(fields.contains(FIELD_SESSION_TOKEN)); + std::string session_token = fields.at(FIELD_SESSION_TOKEN).string_value(); assert(fields.contains(FIELD_EXPIRATION)); std::string expiration_string = fields.at(FIELD_EXPIRATION).string_value(); absl::Time expiration_instant; @@ -146,7 +146,7 @@ void StsCredentialsProviderImpl::refresh() { absl::MutexLock lk(&mtx_); access_key_ = std::move(access_key); access_secret_ = std::move(access_secret); - security_token_ = std::move(security_token); + session_token_ = std::move(session_token); expiration_ = absl::ToChronoTime(expiration_instant); } else { SPDLOG_WARN("Failed to parse expiration time. Message: {}", parse_error); @@ -178,7 +178,7 @@ const char* StsCredentialsProviderImpl::RAM_ROLE_HOST = "100.100.100.200"; const char* StsCredentialsProviderImpl::RAM_ROLE_URL_PREFIX = "/latest/meta-data/Ram/security-credentials/"; const char* StsCredentialsProviderImpl::FIELD_ACCESS_KEY = "AccessKeyId"; const char* StsCredentialsProviderImpl::FIELD_ACCESS_SECRET = "AccessKeySecret"; -const char* StsCredentialsProviderImpl::FIELD_SECURITY_TOKEN = "SecurityToken"; +const char* StsCredentialsProviderImpl::FIELD_SESSION_TOKEN = "SecurityToken"; const char* StsCredentialsProviderImpl::FIELD_EXPIRATION = "Expiration"; const char* StsCredentialsProviderImpl::EXPIRATION_DATE_TIME_FORMAT = "%Y-%m-%d%ET%H:%H:%S%Ez"; diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h index 0567e9dd2..d936195a1 100644 --- a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -29,7 +29,7 @@ class StsCredentialsProviderImpl : public CredentialsProvider { static const char* RAM_ROLE_URL_PREFIX; static const char* FIELD_ACCESS_KEY; static const char* FIELD_ACCESS_SECRET; - static const char* FIELD_SECURITY_TOKEN; + static const char* FIELD_SESSION_TOKEN; static const char* FIELD_EXPIRATION; static const char* EXPIRATION_DATE_TIME_FORMAT; @@ -37,7 +37,7 @@ class StsCredentialsProviderImpl : public CredentialsProvider { std::string access_key_ GUARDED_BY(mtx_); std::string access_secret_ GUARDED_BY(mtx_); - std::string security_token_ GUARDED_BY(mtx_); + std::string session_token_ GUARDED_BY(mtx_); std::chrono::system_clock::time_point expiration_; absl::Mutex mtx_; From 36891e8c92decda3df33ad3af561fade82b2efaf Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 16:02:26 +0800 Subject: [PATCH 021/133] Use macro when declaring namespace --- README.md | 2 +- src/main/cpp/base/HostInfo.cpp | 6 +++--- src/main/cpp/base/include/HostInfo.h | 7 ++++--- src/main/cpp/client/TopicAssignmentInfo.cpp | 6 ++++-- src/main/cpp/client/include/BrokerData.h | 5 +++-- src/main/cpp/client/include/HeartbeatDataCallback.h | 6 ++++-- src/main/cpp/client/include/SendMessageContext.h | 6 ++++-- .../cpp/it/DefaultMQPushConsumerIntegrationTest.cpp | 5 +++-- src/test/cpp/ut/admin/AdminServerTest.cpp | 10 ++++++++-- src/test/cpp/ut/base/MixAllTest.cpp | 8 +++++--- src/test/cpp/ut/base/RateLimiterTest.cpp | 6 ++++-- src/test/cpp/ut/base/UtilAllTest.cpp | 5 +++-- src/test/cpp/ut/logger/LoggerTest.cpp | 8 +++++--- src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 5 +++-- 14 files changed, 54 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index f9db365b3..93c2bfc8d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ if "com_google_googletest" not in native.existing_rules(): 1. Build From the repository root, ``` - bazel build :all + bazel build //... ``` 2. Run Unit Tests From the repository root, diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp index 0987a20c6..c30c0a84b 100644 --- a/src/main/cpp/base/HostInfo.cpp +++ b/src/main/cpp/base/HostInfo.cpp @@ -1,9 +1,10 @@ #include "HostInfo.h" #include "absl/strings/match.h" +#include "rocketmq/RocketMQ.h" #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN const char* HostInfo::ENV_LABEL_SITE = "SIGMA_APP_SITE"; const char* HostInfo::ENV_LABEL_UNIT = "SIGMA_APP_UNIT"; @@ -55,5 +56,4 @@ void HostInfo::appendLabel(std::string& query_string, const char* key, const std query_string.append(",").append(key).append(":").append(value); } } - -} // namespace rocketmq \ No newline at end of file +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h index 4f65a110d..68b8bfa91 100644 --- a/src/main/cpp/base/include/HostInfo.h +++ b/src/main/cpp/base/include/HostInfo.h @@ -1,9 +1,9 @@ #pragma once +#include "rocketmq/RocketMQ.h" #include -namespace rocketmq { - +ROCKETMQ_NAMESPACE_BEGIN struct HostInfo { std::string site_; std::string unit_; @@ -26,4 +26,5 @@ struct HostInfo { static void appendLabel(std::string& query_string, const char* key, const std::string& value); }; -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index 73704f84c..162304217 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -1,7 +1,9 @@ #include "TopicAssignmentInfo.h" +#include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN + thread_local uint32_t TopicAssignment::query_which_broker_ = 0; TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) @@ -75,4 +77,4 @@ TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) std::sort(assignment_list_.begin(), assignment_list_.end()); } -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/BrokerData.h b/src/main/cpp/client/include/BrokerData.h index 2817efc97..50c215f26 100644 --- a/src/main/cpp/client/include/BrokerData.h +++ b/src/main/cpp/client/include/BrokerData.h @@ -2,12 +2,13 @@ #include "RpcClient.h" #include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/RocketMQ.h" #include #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN namespace rmq = apache::rocketmq::v1; @@ -49,4 +50,4 @@ class BrokerData { std::string debug_string_; }; -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h index 30ffe7955..9be195d8a 100644 --- a/src/main/cpp/client/include/HeartbeatDataCallback.h +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -1,8 +1,9 @@ #pragma once #include "RpcClient.h" +#include "rocketmq/RocketMQ.h" -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN /** * This callback will be invoked to collect and prepare heartbeat data, which will be sent to brokers. @@ -11,4 +12,5 @@ class HeartbeatDataCallback { public: virtual void onHeartbeatDataCallback(HeartbeatRequest& request) = 0; }; -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index 301aa912e..f3a19dcb7 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -1,9 +1,10 @@ #pragma once #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" +#include "rocketmq/RocketMQ.h" #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class SendMessageContext { public: const std::string& getProducerGroup() const { return producer_group_; }; @@ -27,4 +28,5 @@ class SendMessageContext { std::string message_id_; long long queue_offset_{-1}; }; -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp index 08e1e57ea..84ef7b0c5 100644 --- a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp +++ b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp @@ -1,11 +1,11 @@ #include "rocketmq/DefaultMQPushConsumer.h" +#include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" #include #include -using namespace rocketmq; - +ROCKETMQ_NAMESPACE_BEGIN class SampleMQMessageListener : public StandardMessageListener { public: ConsumeStatus consumeMessage(const std::vector& msgs) override { @@ -43,3 +43,4 @@ TEST_F(DefaultMQPushConsumerTest, testStartupAndShutdown) { push_consumer_.start(); std::this_thread::sleep_for(std::chrono::minutes(10)); } +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp index e6ff67ca9..684447bdf 100644 --- a/src/test/cpp/ut/admin/AdminServerTest.cpp +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -3,13 +3,16 @@ #include "AdminServerImpl.h" #include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "rocketmq/RocketMQ.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/spdlog.h" #include namespace rmq = apache::rocketmq::v1; -using namespace rocketmq::admin; + +ROCKETMQ_NAMESPACE_BEGIN +namespace admin { TEST(AdminServerTest, testSetUp) { @@ -40,4 +43,7 @@ TEST(AdminServerTest, testSetUp) { EXPECT_EQ(spdlog::level::info, logger->level()); admin_server->stop(); -} \ No newline at end of file +} + +} // namespace admin +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index dab0dff53..33f16fff5 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -1,9 +1,9 @@ #include "MixAll.h" #include "rocketmq/MQMessage.h" +#include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" -using namespace rocketmq; - +ROCKETMQ_NAMESPACE_BEGIN class MixAllTest : public testing::Test { public: static std::string toUpperCase(const std::string& s) { @@ -84,4 +84,6 @@ TEST_F(MixAllTest, testSHA1) { EXPECT_TRUE(ok); std::string expect("a9993e364706816aba3e25717850c26c9cd0d89d"); EXPECT_EQ(digest, toUpperCase(expect)); -} \ No newline at end of file +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp index 0226841c9..b072d1f12 100644 --- a/src/test/cpp/ut/base/RateLimiterTest.cpp +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -1,9 +1,10 @@ #include "RateLimiter.h" +#include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class RateLimiterTest : public ::testing::Test { public: void TearDown() override { observer.stop(); } @@ -46,4 +47,5 @@ TEST_F(RateLimiterTest, basicTest) { t.join(); report.join(); } -} // namespace rocketmq \ No newline at end of file + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index 326dedf03..add0c2812 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -1,11 +1,12 @@ #include "UtilAll.h" #include "absl/strings/str_split.h" +#include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class UtilAllTest : public testing::Test { public: @@ -55,4 +56,4 @@ TEST_F(UtilAllTest, split) { EXPECT_EQ(expected, segments); } -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp index 7aabb0db1..aadbdac45 100644 --- a/src/test/cpp/ut/logger/LoggerTest.cpp +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -1,8 +1,8 @@ #include "rocketmq/Logger.h" +#include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" - -using namespace rocketmq; +ROCKETMQ_NAMESPACE_BEGIN class LoggerTest : public testing::Test {}; @@ -22,4 +22,6 @@ TEST_F(LoggerTest, testDefaultLogger) { SPDLOG_WARN("Warn message is here"); SPDLOG_ERROR("This is an error message"); } -} \ No newline at end of file +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp index 85da2b82f..c1eee2cb3 100644 --- a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -1,3 +1,4 @@ +#include "rocketmq/RocketMQ.h" #include "rocketmq/State.h" #include "gtest/gtest.h" #include @@ -8,7 +9,7 @@ #include #include -namespace rocketmq { +ROCKETMQ_NAMESPACE_BEGIN class ExecutorImpl { public: explicit ExecutorImpl(unsigned int core) : state_(State::CREATED), core_(core) { @@ -132,4 +133,4 @@ TEST_F(ExecutorTest, testSubmit) { EXPECT_EQ(true, submit_status.load()); } -} // namespace rocketmq +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 5b518a5c7bbb1197cd7562c16fef27a739edcfa1 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 2 Aug 2021 16:05:39 +0800 Subject: [PATCH 022/133] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 71422e013421ba1635bdf66c72905a71e1555e8d Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 16:07:24 +0800 Subject: [PATCH 023/133] Remove unused file --- bazel-rocketmq-client-cpp | 1 - 1 file changed, 1 deletion(-) delete mode 120000 bazel-rocketmq-client-cpp diff --git a/bazel-rocketmq-client-cpp b/bazel-rocketmq-client-cpp deleted file mode 120000 index 476412eb2..000000000 --- a/bazel-rocketmq-client-cpp +++ /dev/null @@ -1 +0,0 @@ -/home/yangkun.ayk/.cache/bazel/_bazel_yangkun.ayk/dfc3abe54cc318d8d185535bc4ec65fa/execroot/org_apache_rocketmq \ No newline at end of file From 3d1927e5871fef00f203d20452334e5ab54d9a1b Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 16:12:07 +0800 Subject: [PATCH 024/133] Remove bazel remote cache --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bbdf1a5ab..8287300e9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: - name: Use Bazel if: matrix.os != 'windows' run: bazel -h - - name: Compile Targets - run: bazel build --remote_cache=http://${{ secrets.USER_NAME }}:${{ secrets.PASSWORD }}@47.253.54.230:9090 //... - - name: Run Unit Test Cases - run: bazel test --remote_cache=http://${{ secrets.USER_NAME }}:${{ secrets.PASSWORD }}@47.253.54.230:9090 //src/test/cpp/ut/... \ No newline at end of file + - name: Compile All Targets + run: bazel build //... + - name: Run Unit Tests + run: bazel test //src/test/cpp/ut/... \ No newline at end of file From 2b264701448ddd89f22bdb98defd23ec2e4e79b6 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 16:45:44 +0800 Subject: [PATCH 025/133] Add unit test for HostInfo --- src/test/cpp/ut/base/BUILD.bazel | 11 +++++++++ src/test/cpp/ut/base/HostInfoTest.cpp | 35 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/test/cpp/ut/base/HostInfoTest.cpp diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index 9a499258c..e2c680fcf 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -66,4 +66,15 @@ cc_test( "@com_google_absl//absl/container:flat_hash_set", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "host_info_test", + srcs = [ + "HostInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp new file mode 100644 index 000000000..a70aa47ef --- /dev/null +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -0,0 +1,35 @@ +#include "gtest/gtest.h" +#include "HostInfo.h" +#include "rocketmq/RocketMQ.h" +#include +#include "fmt/format.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HostInfoTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} + +protected: + std::string site_{"site"}; + std::string unit_{"unit"}; + std::string app_{"app"}; + std::string stage_{"stage"}; +}; + +TEST_F(HostInfoTest, testQueryString) { + int overwrite = 1; + setenv(HostInfo::ENV_LABEL_SITE, site_.c_str(), overwrite); + setenv(HostInfo::ENV_LABEL_UNIT, unit_.c_str(), overwrite); + setenv(HostInfo::ENV_LABEL_APP, app_.c_str(), overwrite); + setenv(HostInfo::ENV_LABEL_STAGE, stage_.c_str(), overwrite); + + HostInfo host_info; + std::string query_string = host_info.queryString(); + std::string query_string_template("labels=site:{},unit:{},app:{},stage:{}"); + EXPECT_EQ(query_string, fmt::format(query_string_template, site_, unit_, app_, stage_)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 2fdd3902ca76278bdb80eca1068aa4df2fa8624c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 17:28:08 +0800 Subject: [PATCH 026/133] Debug MessageTest --- api/rocketmq/MQMessage.h | 6 ++++ src/main/cpp/base/include/Protocol.h | 6 ++-- src/test/cpp/ut/base/BUILD.bazel | 11 ++++++++ src/test/cpp/ut/base/MQMessageTest.cpp | 38 ++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/test/cpp/ut/base/MQMessageTest.cpp diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index fe3174ae1..76a6387de 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -37,6 +37,12 @@ class MQMessage { void setTags(const std::string& tags); const std::vector& getKeys() const; + + /** + * @brief Add a unique key for the message + * TODO: a message may be associated with multiple keys. setKey, actually mean attach the given key to the message. Better rename it. + * @param key Unique key in perspective of bussiness logic. + */ void setKey(const std::string& key); void setKeys(const std::vector& keys); diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 8101bd991..f48039bbc 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -14,14 +14,14 @@ class Protocol { static const char* PROTOCOL_VERSION; }; -enum MessageType : int8_t { +enum class MessageType : int8_t { NORMAL = 0, FIFO = 1, DELAY = 2, TRANSACTION = 3, }; -enum DigestType : int8_t { +enum class DigestType : int8_t { CRC32 = 0, MD5 = 1, SHA1 = 2, @@ -33,7 +33,7 @@ struct Digest { Digest() = default; }; -enum Encoding : int8_t { +enum class Encoding : int8_t { IDENTITY = 0, GZIP = 1, SNAPPY = 2, diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index e2c680fcf..ea6597dfc 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -77,4 +77,15 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "message_test", + srcs = [ + "MQMessageTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp new file mode 100644 index 000000000..f4dc4dc41 --- /dev/null +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -0,0 +1,38 @@ +#include "rocketmq/MQMessage.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageTest : public testing::Test { +public: + void SetUp() override { + message.setTopic(topic_); + message.setKey(key_); + message.setBody(body_data_, strlen(body_data_)); + message.setDelayTimeLevel(delay_level_); + } + + void TearDown() override {} + +protected: + std::string topic_{"Test"}; + std::string key_{"key0"}; + const char* body_data_{"body content"}; + int delay_level_{1}; + MQMessage message; +}; + +TEST_F(MQMessageTest, testAssignment) { + MQMessage msg; + for (auto& key : msg.getKeys()) { + std::cout << "Key = " << key << std::endl; + } + msg = message; + EXPECT_EQ(msg.getTopic(), topic_); + EXPECT_EQ(msg.getDelayTimeLevel(), delay_level_); + EXPECT_EQ(*msg.getKeys().begin(), key_); + EXPECT_EQ(msg.getBody(), body_data_); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 6ef43caadfef14cd7af5ccac1dad69835c90043c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 2 Aug 2021 17:44:20 +0800 Subject: [PATCH 027/133] BugFix: fix MQMessage --- src/main/cpp/base/MQMessage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 63a905ca3..7a42d1e2d 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -17,7 +17,9 @@ MQMessage::MQMessage(const std::string& topic, const std::string& tags, const st : impl_(new MessageImpl) { impl_->topic_.name = topic; impl_->system_attribute_.tag = tags; - impl_->system_attribute_.keys.emplace_back(keys); + if (!keys.empty()) { + impl_->system_attribute_.keys.emplace_back(keys); + } impl_->body_.clear(); impl_->body_.reserve(body.length()); impl_->body_.append(body.data(), body.length()); From 67b53edaf6b2b2e9675da649ae2fa397459098bb Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 2 Aug 2021 20:32:14 +0800 Subject: [PATCH 028/133] Improve unit test coverage for base module (#7) Improve unit test coverage for base module --- src/main/cpp/base/BUILD.bazel | 20 +------- src/main/cpp/base/TopAddressing.cpp | 9 +++- src/main/cpp/base/include/Protocol.h | 4 +- src/main/cpp/base/include/TopAddressing.h | 6 ++- src/main/cpp/rocketmq/ClientImpl.cpp | 1 + src/test/cpp/ut/base/BUILD.bazel | 23 +++++++++ src/test/cpp/ut/base/MQMessageExtTest.cpp | 28 +++++++++++ src/test/cpp/ut/base/MQMessageTest.cpp | 3 -- src/test/cpp/ut/base/TopAddressingTest.cpp | 54 ++++++++++++++++++++++ src/test/cpp/ut/rocketmq/BUILD.bazel | 4 +- 10 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 src/test/cpp/ut/base/MQMessageExtTest.cpp create mode 100644 src/test/cpp/ut/base/TopAddressingTest.cpp diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index f50a0c4ff..1bbe6fadb 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -14,20 +14,6 @@ cc_library( ], ) - -cc_library( - name = "http_client_mocks", - hdrs = [ - "mocks/include/HttpClientMock.h", - ], - strip_include_prefix = "//src/main/cpp/base/mocks/include", - deps = [ - ":http_client_interface", - "//external:gtest", - ], -) - - cc_library( name = "ghttp_client", hdrs = [ @@ -81,10 +67,8 @@ cc_library( ) cc_library( - name = "message_listener_mocks", - hdrs = [ - "mocks/include/MessageListenerMock.h", - ], + name = "base_mocks", + hdrs = glob(["mocks/include/*h"]), strip_include_prefix = "//src/main/cpp/base/mocks/include", deps = [ "//api:rocketmq_interface", diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index 9a1c840ac..17e36cee6 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -1,12 +1,14 @@ #include "TopAddressing.h" +#include "GHttpClient.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_split.h" -#include +#include "spdlog/spdlog.h" #include ROCKETMQ_NAMESPACE_BEGIN + TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") {} TopAddressing::TopAddressing(std::string host, int port, std::string path) @@ -48,4 +50,9 @@ void TopAddressing::fetchNameServerAddresses(const std::functionget(HttpProtocol::HTTP, host_, port_, query_string, callback); } +void TopAddressing::injectHttpClient(std::unique_ptr http_client) { + http_client_->shutdown(); + http_client_.swap(http_client); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index f48039bbc..a25db9a61 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -58,12 +58,12 @@ struct SystemAttribute { Digest digest; Encoding body_encoding; MessageType message_type; - absl::Time born_timestamp{absl::UnixEpoch()}; + absl::Time born_timestamp{absl::Now()}; std::string born_host; absl::Time store_timestamp{absl::UnixEpoch()}; std::string store_host; absl::Time delivery_timestamp{absl::UnixEpoch()}; - absl::Time decode_timestamp{absl::UnixEpoch()}; + absl::Time decode_timestamp{absl::Now()}; int32_t delay_level{0}; std::string receipt_handle; int32_t partition_id{0}; diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h index e556efe58..bcdf61006 100644 --- a/src/main/cpp/base/include/TopAddressing.h +++ b/src/main/cpp/base/include/TopAddressing.h @@ -7,7 +7,7 @@ #include #include -#include "GHttpClient.h" +#include "HttpClient.h" #include "HostInfo.h" ROCKETMQ_NAMESPACE_BEGIN @@ -22,13 +22,15 @@ class TopAddressing { void fetchNameServerAddresses(const std::function&)>& cb); + void injectHttpClient(std::unique_ptr http_client); + private: std::string host_; int port_{8080}; std::string path_; HostInfo host_info_; - std::unique_ptr http_client_; + std::unique_ptr http_client_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 14a246314..2cbe393f8 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,5 +1,6 @@ #include "ClientImpl.h" #include "ClientManagerFactory.h" +#include "GHttpClient.h" #include "InvocationContext.h" #include "LoggerImpl.h" #include "MessageAccessor.h" diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index ea6597dfc..d44c2e7cc 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -88,4 +88,27 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "message_ext_test", + srcs = [ + "MQMessageExtTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "top_addressing_test", + srcs = [ + "TopAddressingTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "//src/main/cpp/base:base_mocks", + "@com_google_googletest//:gtest_main", + ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp new file mode 100644 index 000000000..36a62bacc --- /dev/null +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -0,0 +1,28 @@ +#include "rocketmq/MQMessageExt.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class MQMessageExtTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} + +protected: + MQMessageExt message_; +}; + +TEST_F(MQMessageExtTest, testGetQueueId) { + EXPECT_EQ(message_.getQueueId(), 0); +} + +TEST_F(MQMessageExtTest, testBornTimestamp) { + auto born_timestamp = message_.bornTimestamp(); + EXPECT_TRUE(std::chrono::system_clock::now() - born_timestamp < std::chrono::seconds(1)); +} + +TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { EXPECT_EQ(message_.getDeliveryAttempt(), 0); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp index f4dc4dc41..d2feaef23 100644 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -25,9 +25,6 @@ class MQMessageTest : public testing::Test { TEST_F(MQMessageTest, testAssignment) { MQMessage msg; - for (auto& key : msg.getKeys()) { - std::cout << "Key = " << key << std::endl; - } msg = message; EXPECT_EQ(msg.getTopic(), topic_); EXPECT_EQ(msg.getDelayTimeLevel(), delay_level_); diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp new file mode 100644 index 000000000..6e24e1bd0 --- /dev/null +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -0,0 +1,54 @@ +#include "TopAddressing.h" +#include "HttpClientMock.h" +#include "grpc/grpc.h" +#include "gtest/gtest.h" +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class TopAddressingTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + top_addressing_ = absl::make_unique(); + auto http_client_ = absl::make_unique>(); + auto mock_get = + [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& + cb) { + absl::flat_hash_map headers; + std::string body("10.0.0.1:9876"); + cb(200, headers, body); + }; + ON_CALL(*http_client_, get).WillByDefault(testing::Invoke(mock_get)); + top_addressing_->injectHttpClient(std::move(http_client_)); + } + + void TearDown() override { grpc_shutdown(); } + +protected: + std::unique_ptr top_addressing_; +}; + +TEST_F(TopAddressingTest, testFetchNameServerAddresses) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto callback = [&](bool ok, const std::vector& name_server_list) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + top_addressing_->fetchNameServerAddresses(callback); + { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + EXPECT_TRUE(completed); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 4ebf77470..462fc724c 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -111,7 +111,7 @@ cc_test( "StsCredentialsProviderImplTest.cpp", ], deps = [ - "//src/main/cpp/base:http_client_mocks", + "//src/main/cpp/base:base_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], @@ -136,7 +136,7 @@ cc_test( deps = [ "//src/main/cpp/rocketmq:rocketmq_library", "//src/main/cpp/rocketmq:rocketmq_mocks", - "//src/main/cpp/base:message_listener_mocks", + "//src/main/cpp/base:base_mocks", "@com_google_googletest//:gtest_main", ], ) From 0a4ff04dc216c2901392e9dce8574addfc8380d1 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 3 Aug 2021 13:41:13 +0800 Subject: [PATCH 029/133] Improve unit test coverage for client module (#8) Improve unit test coverage for client module --- .bazelrc | 2 +- src/main/cpp/base/BUILD.bazel | 10 - src/main/cpp/base/mocks/BUILD.bazel | 12 + src/main/cpp/client/BUILD.bazel | 12 - src/main/cpp/client/ClientManagerFactory.cpp | 16 +- src/main/cpp/client/ClientManagerImpl.cpp | 13 +- src/main/cpp/client/include/ClientManager.h | 2 +- .../cpp/client/include/ClientManagerFactory.h | 2 +- .../cpp/client/include/ClientManagerImpl.h | 7 +- src/main/cpp/client/mocks/BUILD.bazel | 13 + .../client/mocks/include/ClientManagerMock.h | 2 +- src/main/cpp/rocketmq/BUILD.bazel | 10 - src/main/cpp/rocketmq/mocks/BUILD.bazel | 12 + src/test/cpp/it/BUILD.bazel | 7 +- src/test/cpp/ut/base/BUILD.bazel | 2 +- src/test/cpp/ut/client/BUILD.bazel | 28 +- .../ut/client/ClientManagerFactoryTest.cpp | 24 ++ src/test/cpp/ut/client/ClientManagerTest.cpp | 292 +++++++++++++++++- .../cpp/ut/client/TopicAssignmentInfoTest.cpp | 99 ++++++ src/test/cpp/ut/rocketmq/BUILD.bazel | 14 +- src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 3 +- .../cpp/ut/rocketmq/include/MQClientTest.h | 2 +- 22 files changed, 506 insertions(+), 78 deletions(-) create mode 100644 src/main/cpp/base/mocks/BUILD.bazel create mode 100644 src/main/cpp/client/mocks/BUILD.bazel create mode 100644 src/main/cpp/rocketmq/mocks/BUILD.bazel create mode 100644 src/test/cpp/ut/client/ClientManagerFactoryTest.cpp create mode 100644 src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp diff --git a/.bazelrc b/.bazelrc index 528f4c1f5..7ed0205e8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -124,4 +124,4 @@ build:coverage --combined_report=lcov build:coverage --strategy=TestRunner=sandboxed,local build:coverage --strategy=CoverageReport=sandboxed,local build:coverage --collect_code_coverage -build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:]" \ No newline at end of file +build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:],-//src/main/cpp/base/mocks[/:],-//src/main/cpp/client/mocks[/:],-//src/main/cpp/rocketmq/mocks[/:]" \ No newline at end of file diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 1bbe6fadb..e8d97809b 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -64,14 +64,4 @@ cc_library( "@boringssl//:crypto", "@boringssl//:ssl", ], -) - -cc_library( - name = "base_mocks", - hdrs = glob(["mocks/include/*h"]), - strip_include_prefix = "//src/main/cpp/base/mocks/include", - deps = [ - "//api:rocketmq_interface", - "//external:gtest", - ], ) \ No newline at end of file diff --git a/src/main/cpp/base/mocks/BUILD.bazel b/src/main/cpp/base/mocks/BUILD.bazel new file mode 100644 index 000000000..b50f88c77 --- /dev/null +++ b/src/main/cpp/base/mocks/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "base_mocks", + hdrs = glob(["include/*h"]), + strip_include_prefix = "//src/main/cpp/base/mocks/include", + deps = [ + "//api:rocketmq_interface", + "//external:gtest", + ], +) \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index d3a88db61..909cfb12d 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -1,5 +1,4 @@ load("@rules_cc//cc:defs.bzl", "cc_library") - package(default_visibility = ["//visibility:public"]) cc_library( @@ -25,15 +24,4 @@ cc_library( ], defines = [ ], -) - -cc_library( - name = "client_mocks", - hdrs = glob(["mocks/include/*.h"]), - srcs = glob(["mocks/*.cpp"]), - strip_include_prefix = "//src/main/cpp/client/mocks/include", - deps = [ - ":client_library", - "//external:gtest", - ], ) \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 05515df28..86bde48bd 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -13,23 +13,23 @@ ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& clie absl::MutexLock lock(&client_manager_table_mtx_); auto search = client_manager_table_.find(client_config.arn()); if (search != client_manager_table_.end()) { - ClientManagerPtr client_instance = search->second.lock(); - if (client_instance) { + ClientManagerPtr client_manager = search->second.lock(); + if (client_manager) { SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.arn()); - return client_instance; + return client_manager; } else { client_manager_table_.erase(client_config.arn()); } } - ClientManagerPtr client_instance = std::make_shared(client_config.arn()); - std::weak_ptr client_instance_weak_ptr(client_instance); + ClientManagerPtr client_manager = std::make_shared(client_config.arn()); + std::weak_ptr client_instance_weak_ptr(client_manager); client_manager_table_.insert_or_assign(client_config.arn(), client_instance_weak_ptr); - SPDLOG_INFO("Created new client_instance[ARN={}]", client_config.arn()); - return client_instance; + SPDLOG_INFO("Created a new client manager[ARN={}]", client_config.arn()); + return client_manager; } } -void ClientManagerFactory::addClientInstance(const std::string& arn, const ClientManagerPtr& client_manager) { +void ClientManagerFactory::addClientManager(const std::string& arn, const ClientManagerPtr& client_manager) { absl::MutexLock lk(&client_manager_table_mtx_); client_manager_table_.insert_or_assign(arn, std::weak_ptr(client_manager)); } diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 1339fe8d6..20c52d404 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -49,12 +49,12 @@ ClientManagerImpl::ClientManagerImpl(std::string arn) tls_channel_credential_options_.watch_root_certs(); tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - SPDLOG_INFO("ClientInstance[ARN={}] created", arn_); + SPDLOG_INFO("ClientManager[ARN={}] created", arn_); } ClientManagerImpl::~ClientManagerImpl() { shutdown(); - SPDLOG_INFO("ClientInstance[ARN={}] destructed", arn_); + SPDLOG_INFO("ClientManager[ARN={}] destructed", arn_); } void ClientManagerImpl::start() { @@ -153,7 +153,7 @@ void ClientManagerImpl::healthCheck( { absl::MutexLock lk(&rpc_clients_mtx_); if (!rpc_clients_.contains(target_host)) { - SPDLOG_WARN("Try to perform health check for {}, which is unknown to ClientInstance", target_host); + SPDLOG_WARN("Try to perform health check for {}, which is unknown to client manager", target_host); cb(target_host, nullptr); return; } @@ -339,11 +339,6 @@ void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata client->asyncHeartbeat(request, invocation_context); } -bool ClientManagerImpl::active() { - State state = state_.load(std::memory_order_relaxed); - return State::STARTED == state || State::STARTING == state; -} - void ClientManagerImpl::doHeartbeat() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { @@ -665,7 +660,7 @@ void ClientManagerImpl::queryAssignment(const std::string& target, const Metadat void ClientManagerImpl::receiveMessage(const std::string& target_host, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - std::shared_ptr& cb) { + const std::shared_ptr& cb) { SPDLOG_DEBUG("Prepare to pop message from {} asynchronously. Request: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 513054543..58e4717a4 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -69,7 +69,7 @@ class ClientManager { const std::function& cb) = 0; virtual void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, - std::chrono::milliseconds timeout, std::shared_ptr& cb) = 0; + std::chrono::milliseconds timeout, const std::shared_ptr& cb) = 0; virtual bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, SendCallback* cb) = 0; diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h index dfd746643..a50e3d0ba 100644 --- a/src/main/cpp/client/include/ClientManagerFactory.h +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -19,7 +19,7 @@ class ClientManagerFactory { ClientManagerPtr getClientManager(const ClientConfig& client_config) LOCKS_EXCLUDED(client_manager_table_mtx_); // For test purpose only - void addClientInstance(const std::string& arn, const ClientManagerPtr& client_manager) + void addClientManager(const std::string& arn, const ClientManagerPtr& client_manager) LOCKS_EXCLUDED(client_manager_table_mtx_); private: diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 8b9ae3c9f..aae3d670e 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -44,7 +44,8 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share public: /** * @brief Construct a new Client Manager Impl object - * TODO: Make it protected such that instantiating it through ClientManagerFactory only, achieving Singleton effectively. + * TODO: Make it protected such that instantiating it through ClientManagerFactory only, achieving Singleton + * effectively. * @param arn Abstract resource namespace, in which this client manager lives. */ explicit ClientManagerImpl(std::string arn); @@ -120,7 +121,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const std::function& cb) override; void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, - std::chrono::milliseconds timeout, std::shared_ptr& cb) override + std::chrono::milliseconds timeout, const std::shared_ptr& cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); /** @@ -200,8 +201,6 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, ReceiveMessageResult& result, const std::string& target_host); - bool active(); - void doHeartbeat(); void pollCompletionQueue(); diff --git a/src/main/cpp/client/mocks/BUILD.bazel b/src/main/cpp/client/mocks/BUILD.bazel new file mode 100644 index 000000000..b7ac37138 --- /dev/null +++ b/src/main/cpp/client/mocks/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "client_mocks", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/client/mocks/include", + deps = [ + "//src/main/cpp/client:client_library", + "//external:gtest", + ], +) \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 677e0bdfd..ca8af8ee5 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -72,7 +72,7 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, receiveMessage, (const std::string&, const Metadata&, const ReceiveMessageRequest&, std::chrono::milliseconds, - (std::shared_ptr&)), + (const std::shared_ptr&)), (override)); MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback*), (override)); diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index cdca0a8dc..858d8262c 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -12,14 +12,4 @@ cc_library( "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", ], -) - -cc_library( - name = "rocketmq_mocks", - hdrs = glob(["mocks/include/*.h"]), - strip_include_prefix = "//src/main/cpp/rocketmq/mocks/include", - deps = [ - ":rocketmq_library", - "//src/main/cpp/client:client_mocks", - ], ) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/BUILD.bazel b/src/main/cpp/rocketmq/mocks/BUILD.bazel new file mode 100644 index 000000000..541cb9b8c --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "rocketmq_mocks", + hdrs = glob(["include/*.h"]), + strip_include_prefix = "//src/main/cpp/rocketmq/mocks/include", + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/client/mocks:client_mocks", + ], +) \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index 18298c50b..6bc10df54 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -17,13 +17,10 @@ cc_test( "RpcClientTest.cpp", ], deps = [ - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], - copts = [ - "-Isrc/main/client/mocks/include", - ], ) cc_test( @@ -32,7 +29,7 @@ cc_test( "TopicPublishInfoTest.cpp", ], deps = [ - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index d44c2e7cc..686de3184 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -108,7 +108,7 @@ cc_test( ], deps = [ "//src/main/cpp/base:base_library", - "//src/main/cpp/base:base_mocks", + "//src/main/cpp/base/mocks:base_mocks", "@com_google_googletest//:gtest_main", ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel index d77cae4ff..4d8eeb920 100644 --- a/src/test/cpp/ut/client/BUILD.bazel +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -19,7 +19,7 @@ cc_test( ], deps = [ "//src/main/cpp/client:client_library", - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "@com_google_googletest//:gtest_main", "@boringssl//:ssl", ], @@ -31,7 +31,7 @@ cc_test( "ClientTest.cpp", ], deps = [ - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "@com_google_googletest//:gtest_main", ], ) @@ -42,7 +42,29 @@ cc_test( "ClientManagerTest.cpp", ], deps = [ - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "@com_google_googletest//:gtest_main", ] +) + +cc_test( + name = "client_manager_factory_test", + srcs = [ + "ClientManagerFactoryTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "topic_assignment_info_test", + srcs = [ + "TopicAssignmentInfoTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp new file mode 100644 index 000000000..273c6eec7 --- /dev/null +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -0,0 +1,24 @@ +#include "gtest/gtest.h" +#include "ClientManagerFactory.h" +#include "ClientConfigMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ClientManagerFactoryTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} + +protected: + testing::NiceMock client_config_; + std::string arn_{"arn:mq://test"}; +}; + +TEST_F(ClientManagerFactoryTest, testGetClientManager) { + EXPECT_CALL(client_config_, arn).Times(testing::AtLeast(1)).WillRepeatedly(testing::ReturnRef(arn_)); + ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); + EXPECT_TRUE(client_manager); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index 319c54a1b..1400b0469 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -1,7 +1,8 @@ #include "ClientManagerImpl.h" +#include "ReceiveMessageCallbackMock.h" #include "RpcClientMock.h" +#include "apache/rocketmq/v1/definition.pb.h" #include "gtest/gtest.h" -#include #include ROCKETMQ_NAMESPACE_BEGIN @@ -14,6 +15,9 @@ class ClientManagerTest : public testing::Test { rpc_client_ = std::make_shared>(); ON_CALL(*rpc_client_, ok).WillByDefault(testing::Return(true)); client_manager_->addRpcClient(target_host_, rpc_client_); + receive_message_callback_ = std::make_shared>(); + metadata_.insert({"foo", "bar"}); + metadata_.insert({"name", "Donald.J.Trump"}); } void TearDown() override { client_manager_->shutdown(); } @@ -24,7 +28,12 @@ class ClientManagerTest : public testing::Test { std::string target_host_{"ipv4:10.0.0.0:10911"}; std::shared_ptr client_manager_; std::shared_ptr> rpc_client_; + std::shared_ptr> receive_message_callback_; absl::Duration io_timeout_{absl::Seconds(3)}; + Metadata metadata_; + std::string message_body_{"Message body"}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; }; TEST_F(ClientManagerTest, testBasic) { @@ -53,7 +62,6 @@ TEST_F(ClientManagerTest, testResolveRoute) { absl::Mutex mtx; absl::CondVar cv; - Metadata metadata; QueryRouteRequest request; request.mutable_topic()->set_arn(arn_); request.mutable_topic()->set_name(topic_); @@ -62,7 +70,7 @@ TEST_F(ClientManagerTest, testResolveRoute) { completed = true; cv.SignalAll(); }; - client_manager_->resolveRoute(target_host_, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->resolveRoute(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); { absl::MutexLock lk(&mtx); cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); @@ -70,4 +78,282 @@ TEST_F(ClientManagerTest, testResolveRoute) { EXPECT_TRUE(completed); } +TEST_F(ClientManagerTest, testQueryAssignment) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_query_assignment = [&](const QueryAssignmentRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncQueryAssignment) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_assignment)); + QueryAssignmentRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok, const QueryAssignmentResponse& response) { callback_invoked = true; }; + + client_manager_->queryAssignment(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testReceiveMessage) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_async_receive = [&](const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncReceive) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_async_receive)); + ReceiveMessageRequest request; + + EXPECT_CALL(*receive_message_callback_, onSuccess).Times(testing::AtLeast(1)); + + client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), + receive_message_callback_); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); +} + +TEST_F(ClientManagerTest, testReceiveMessage_Failure) { + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_async_receive = [&](const ReceiveMessageRequest& request, + InvocationContext* invocation_context) { + invocation_context->status = grpc::Status::CANCELLED; + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncReceive) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_async_receive)); + ReceiveMessageRequest request; + + EXPECT_CALL(*receive_message_callback_, onException).Times(testing::AtLeast(1)); + + client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), + receive_message_callback_); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); +} + +TEST_F(ClientManagerTest, testAck) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_ack = [&](const AckMessageRequest& request, InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncAck).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_ack)); + AckMessageRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok) { callback_invoked = true; }; + + client_manager_->ack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testNack) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_nack = [&](const NackMessageRequest& request, InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncNack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_nack)); + NackMessageRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok) { callback_invoked = true; }; + + client_manager_->nack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testForwardMessageToDeadLetterQueue) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_forward = [&](const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncForwardMessageToDeadLetterQueue) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_forward)); + ForwardMessageToDeadLetterQueueRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok) { callback_invoked = true; }; + + client_manager_->forwardMessageToDeadLetterQueue(target_host_, metadata_, request, + absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, testMultiplexingCall) {} + +TEST_F(ClientManagerTest, testEndTransaction) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_end_transaction = [&](const EndTransactionRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncEndTransaction) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_end_transaction)); + EndTransactionRequest request; + bool callback_invoked = false; + auto callback = [&](bool ok, const EndTransactionResponse& response) { callback_invoked = true; }; + + client_manager_->endTransaction(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + +TEST_F(ClientManagerTest, processPullResult) { + grpc::ClientContext client_context; + PullMessageResponse response; + auto message = new rmq::Message; + message->mutable_topic()->set_arn(arn_); + message->mutable_topic()->set_name(topic_); + message->set_body(message_body_); + message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); + std::string checksum; + EXPECT_TRUE(MixAll::md5(message_body_, checksum)); + message->mutable_system_attribute()->mutable_body_digest()->set_checksum(checksum); + message->mutable_system_attribute()->set_tag(tag_); + message->mutable_system_attribute()->mutable_keys()->Add(key_.c_str()); + response.mutable_messages()->AddAllocated(message); + ReceiveMessageResult result; + client_manager_->processPullResult(client_context, response, result, target_host_); + EXPECT_EQ(1, result.messages_.size()); + const auto& msg = *result.messages_.begin(); + EXPECT_EQ(msg.getTopic(), topic_); + EXPECT_EQ(msg.getTags(), tag_); + EXPECT_EQ(msg.getBody(), message_body_); +} + +TEST_F(ClientManagerTest, testHealthCheck) { + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto mock_health_check = [&](const HealthCheckRequest& request, + InvocationContext* invocation_context) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + invocation_context->onCompletion(true); + }; + + EXPECT_CALL(*rpc_client_, asyncHealthCheck) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_health_check)); + HealthCheckRequest request; + bool callback_invoked = false; + auto callback = [&](const std::string& target_host, + const InvocationContext* invocation_context) { callback_invoked = true; }; + + client_manager_->healthCheck(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + EXPECT_TRUE(callback_invoked); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp new file mode 100644 index 000000000..3f464dcde --- /dev/null +++ b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp @@ -0,0 +1,99 @@ +#include "TopicAssignmentInfo.h" +#include "rocketmq/ConsumeType.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class QueryAssignmentInfoTest : public testing::Test { +protected: + std::string arn{"arn:mq://test"}; + std::string topic{"TopicTest"}; + std::string broker_name{"broker-a"}; + int broker_id = 0; + int total = 16; +}; + +TEST_F(QueryAssignmentInfoTest, testCtor) { + QueryAssignmentResponse response; + for (int i = 0; i < total; i++) { + auto assignment = new rmq::Assignment; + assignment->set_mode(rmq::ConsumeMessageType::POP); + assignment->mutable_partition()->mutable_topic()->set_arn(arn); + assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::READ); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name); + broker->set_id(broker_id); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_EQ(total, assignment.assignmentList().size()); + const auto& item = *assignment.assignmentList().begin(); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name); + EXPECT_EQ(item.messageQueue().getTopic(), topic); + EXPECT_EQ(item.consumeType(), ConsumeMessageType::POP); + EXPECT_TRUE(item.messageQueue().getQueueId() < 16); +} + +TEST_F(QueryAssignmentInfoTest, testCtor2) { + QueryAssignmentResponse response; + for (int i = 0; i < total; i++) { + auto assignment = new rmq::Assignment; + assignment->set_mode(rmq::ConsumeMessageType::POP); + assignment->mutable_partition()->mutable_topic()->set_arn(arn); + assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name); + broker->set_id(broker_id); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_EQ(total, assignment.assignmentList().size()); + const auto& item = *assignment.assignmentList().begin(); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name); + EXPECT_EQ(item.messageQueue().getTopic(), topic); + EXPECT_EQ(item.consumeType(), ConsumeMessageType::POP); + EXPECT_TRUE(item.messageQueue().getQueueId() < 16); +} + +TEST_F(QueryAssignmentInfoTest, testCtor3) { + QueryAssignmentResponse response; + for (int i = 0; i < total; i++) { + auto assignment = new rmq::Assignment; + assignment->set_mode(rmq::ConsumeMessageType::POP); + assignment->mutable_partition()->mutable_topic()->set_arn(arn); + assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->set_id(i); + assignment->mutable_partition()->set_permission(rmq::Permission::NONE); + auto broker = assignment->mutable_partition()->mutable_broker(); + broker->set_name(broker_name); + broker->set_id(broker_id); + broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + + auto address = new rmq::Address; + address->set_host("10.0.0.1"); + address->set_port(10911); + broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); + response.mutable_assignments()->AddAllocated(assignment); + } + TopicAssignment assignment(response); + EXPECT_TRUE(assignment.assignmentList().empty()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 462fc724c..e2a77a6f3 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -39,8 +39,8 @@ cc_test( "ProcessQueueTest.cpp", ], deps = [ - "//src/main/cpp/client:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_mocks", + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], @@ -54,7 +54,7 @@ cc_library( srcs = [], strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", deps = [ - "//src/main/cpp/client:client_mocks", + "//src/main/cpp/client/mocks:client_mocks", "@com_google_googletest//:gtest_main", ], ) @@ -111,7 +111,7 @@ cc_test( "StsCredentialsProviderImplTest.cpp", ], deps = [ - "//src/main/cpp/base:base_mocks", + "//src/main/cpp/base/mocks:base_mocks", "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], @@ -135,8 +135,8 @@ cc_test( ], deps = [ "//src/main/cpp/rocketmq:rocketmq_library", - "//src/main/cpp/rocketmq:rocketmq_mocks", - "//src/main/cpp/base:base_mocks", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "//src/main/cpp/base/mocks:base_mocks", "@com_google_googletest//:gtest_main", ], ) @@ -147,7 +147,7 @@ cc_test( "ConsumerTest.cpp", ], deps = [ - "//src/main/cpp/rocketmq:rocketmq_mocks", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", "@com_google_googletest//:gtest_main", ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 2329f0eaf..98e3d7427 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -15,6 +15,7 @@ #include "rocketmq/MQMessageExt.h" #include "gtest/gtest.h" #include +#include #include #include #include @@ -284,7 +285,7 @@ TEST_F(ProcessQueueTest, testReceiveMessage_POP) { auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - std::shared_ptr& cb) { + const std::shared_ptr& cb) { ReceiveMessageResult receive_message_result; receive_message_result.status_ = ReceiveMessageStatus::OK; diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 54a101950..941454c4c 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -25,7 +25,7 @@ class MQClientTest : public testing::Test { .WillByDefault(testing::Invoke( std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); - ClientManagerFactory::getInstance().addClientInstance(arn_, client_instance_); + ClientManagerFactory::getInstance().addClientManager(arn_, client_instance_); } void TearDown() override { From 8ee91d76c3ed0ad9c9eb16f7bf6deb68126c11da Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 3 Aug 2021 19:46:34 +0800 Subject: [PATCH 030/133] Enhance coverage of the rocketmq module (#9) Enhance coverage of the rocketmq module --- api/rocketmq/DefaultMQProducer.h | 4 +- api/rocketmq/DefaultMQPullConsumer.h | 4 +- api/rocketmq/DefaultMQPushConsumer.h | 4 +- api/rocketmq/ErrorCode.h | 2 + api/rocketmq/SendResult.h | 62 +++-- src/main/cpp/rocketmq/CMakeLists.txt | 6 +- src/main/cpp/rocketmq/ClientImpl.cpp | 34 +-- .../rocketmq/ConsumeFifoMessageService.cpp | 2 +- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 8 +- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 4 +- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 4 +- ...ultMQProducerImpl.cpp => ProducerImpl.cpp} | 136 ++++++----- ...lConsumerImpl.cpp => PullConsumerImpl.cpp} | 22 +- ...hConsumerImpl.cpp => PushConsumerImpl.cpp} | 127 +++++----- src/main/cpp/rocketmq/TransactionImpl.cpp | 6 +- .../cpp/rocketmq/include/AwaitPullCallback.h | 11 +- src/main/cpp/rocketmq/include/ClientImpl.h | 2 +- ...DefaultMQProducerImpl.h => ProducerImpl.h} | 11 +- ...QPullConsumerImpl.h => PullConsumerImpl.h} | 9 +- ...QPushConsumerImpl.h => PushConsumerImpl.h} | 10 +- .../cpp/rocketmq/include/TransactionImpl.h | 6 +- .../cpp/ut/rocketmq/AwaitPullCallbackTest.cpp | 68 ++++++ src/test/cpp/ut/rocketmq/BUILD.bazel | 35 +++ src/test/cpp/ut/rocketmq/CMakeLists.txt | 6 + .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 8 +- .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 6 +- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 231 ++++++++++++++++++ .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 163 ++++++++++++ .../cpp/ut/rocketmq/include/MQClientTest.h | 5 +- 29 files changed, 765 insertions(+), 231 deletions(-) rename src/main/cpp/rocketmq/{DefaultMQProducerImpl.cpp => ProducerImpl.cpp} (73%) rename src/main/cpp/rocketmq/{DefaultMQPullConsumerImpl.cpp => PullConsumerImpl.cpp} (86%) rename src/main/cpp/rocketmq/{DefaultMQPushConsumerImpl.cpp => PushConsumerImpl.cpp} (76%) rename src/main/cpp/rocketmq/include/{DefaultMQProducerImpl.h => ProducerImpl.h} (94%) rename src/main/cpp/rocketmq/include/{DefaultMQPullConsumerImpl.h => PullConsumerImpl.h} (64%) rename src/main/cpp/rocketmq/include/{DefaultMQPushConsumerImpl.h => PushConsumerImpl.h} (95%) create mode 100644 src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/ProducerImplTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index c2dfca56c..5384701f0 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -18,7 +18,7 @@ ROCKETMQ_NAMESPACE_BEGIN * This class employs pointer-to-implementation paradigm to achieve the goal of stable ABI. * Refer https://en.cppreference.com/w/cpp/language/pimpl for an explanation. */ -class DefaultMQProducerImpl; +class ProducerImpl; class DefaultMQProducer { public: @@ -113,7 +113,7 @@ class DefaultMQProducer { void setRegion(const std::string& region); private: - std::shared_ptr impl_; + std::shared_ptr impl_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index 0374b55f7..3f29a5920 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -14,7 +14,7 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPullConsumerImpl; +class PullConsumerImpl; class DefaultMQPullConsumer { public: @@ -39,7 +39,7 @@ class DefaultMQPullConsumer { void setCredentialsProvider(std::shared_ptr credentials_provider); private: - std::shared_ptr impl_; + std::shared_ptr impl_; }; diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index ae57b2c16..9ec51b926 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -15,7 +15,7 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPushConsumerImpl; +class PushConsumerImpl; class DefaultMQPushConsumer { public: @@ -89,7 +89,7 @@ class DefaultMQPushConsumer { void setMessageModel(MessageModel message_model); private: - std::shared_ptr impl_; + std::shared_ptr impl_; std::string group_name_; }; diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index e4f3a25c2..89d00e8c5 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -20,4 +20,6 @@ constexpr int MESSAGE_ILLEGAL = -7; constexpr int BAD_CONFIGURATION = -8; +constexpr int MESSAGE_QUEUE_ILLEGAL = -9; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 549d8a7f3..681943cac 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -6,25 +6,33 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class SendStatus : int8_t { SEND_OK, SEND_FLUSH_DISK_TIMEOUT, SEND_FLUSH_SLAVE_TIMEOUT, SEND_SLAVE_NOT_AVAILABLE }; +enum class SendStatus : int8_t { + SEND_OK, + + // Deprecated. Remove in the next release. + SEND_FLUSH_DISK_TIMEOUT, + + // Deprecated. Remove in the next release. + SEND_FLUSH_SLAVE_TIMEOUT, + + // Deprecated. Remove in the next release. + SEND_SLAVE_NOT_AVAILABLE +}; class SendResult { - public: - SendResult() {} - - SendResult(const SendStatus& send_status, std::string msg_id, MQMessageQueue message_queue, - long long queue_offset, std::string region_id) - : send_status_(send_status), - message_id_(std::move(msg_id)), - message_queue_(std::move(message_queue)), - queue_offset_(queue_offset), +public: + SendResult() = default; + + SendResult(const SendStatus &send_status, std::string msg_id, + MQMessageQueue message_queue, long long queue_offset, + std::string region_id) + : send_status_(send_status), message_id_(std::move(msg_id)), + message_queue_(std::move(message_queue)), queue_offset_(queue_offset), region_id_(std::move(region_id)) {} - ~SendResult() {} + SendResult(const SendResult &other) { this->operator=(other); } - SendResult(const SendResult& other) { this->operator=(other); } - - SendResult& operator=(const SendResult& other) { + SendResult &operator=(const SendResult &other) { if (this == &other) { return *this; } @@ -38,31 +46,37 @@ class SendResult { return *this; } - const std::string& getMsgId() const { return message_id_; } + const std::string &getMsgId() const { return message_id_; } - void setMsgId(const std::string& msg_id) { message_id_ = msg_id; } + void setMsgId(const std::string &msg_id) { message_id_ = msg_id; } - const std::string& getRegionId() const { return region_id_; } + const std::string &getRegionId() const { return region_id_; } - void setRegionId(const std::string& region_id) { region_id_ = region_id; } + void setRegionId(const std::string ®ion_id) { region_id_ = region_id; } SendStatus getSendStatus() const { return send_status_; } - void setSendStatus(const SendStatus& send_status) { send_status_ = send_status; } + void setSendStatus(const SendStatus &send_status) { + send_status_ = send_status; + } - MQMessageQueue& getMessageQueue() const { return message_queue_; } + MQMessageQueue &getMessageQueue() const { return message_queue_; } - void setMessageQueue(const MQMessageQueue& message_queue) { message_queue_ = message_queue; } + void setMessageQueue(const MQMessageQueue &message_queue) { + message_queue_ = message_queue; + } long long getQueueOffset() const { return queue_offset_; } void setQueueOffset(long long queue_offset) { queue_offset_ = queue_offset; } - const std::string& getTransactionId() const { return transaction_id_; } + const std::string &getTransactionId() const { return transaction_id_; } - void setTransactionId(const std::string& transaction_id) { transaction_id_ = transaction_id; } + void setTransactionId(const std::string &transaction_id) { + transaction_id_ = transaction_id; + } - private: +private: SendStatus send_status_; std::string message_id_; mutable MQMessageQueue message_queue_; diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt index 87be4a332..13e578007 100644 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -10,11 +10,11 @@ target_sources(rocketmq_obj ConsumeFifoMessageService.cpp CredentialsProvider.cpp DefaultMQProducer.cpp - DefaultMQProducerImpl.cpp + ProducerImpl.cpp DefaultMQPushConsumer.cpp - DefaultMQPushConsumerImpl.cpp + PushConsumerImpl.cpp DefaultMQPullConsumer.cpp - DefaultMQPullConsumerImpl.cpp + PullConsumerImpl.cpp FilterExpression.cpp MessageGroupQueueSelector.cpp ProcessQueueImpl.cpp diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 2cbe393f8..428088b6a 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -23,8 +23,8 @@ void ClientImpl::start() { return; } - client_instance_ = ClientManagerFactory::getInstance().getClientManager(*this); - client_instance_->start(); + client_manager_ = ClientManagerFactory::getInstance().getClientManager(*this); + client_manager_->start(); bool update_name_server_list = false; { absl::MutexLock lock(&name_server_list_mtx_); @@ -48,7 +48,7 @@ void ClientImpl::start() { } }; name_server_update_handle_ = - client_instance_->getScheduler().schedule(name_server_update_functor, UPDATE_NAME_SERVER_LIST_TASK_NAME, + client_manager_->getScheduler().schedule(name_server_update_functor, UPDATE_NAME_SERVER_LIST_TASK_NAME, std::chrono::milliseconds(0), std::chrono::seconds(30)); } @@ -59,7 +59,7 @@ void ClientImpl::start() { } }; - route_update_handle_ = client_instance_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, + route_update_handle_ = client_manager_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, std::chrono::seconds(10), std::chrono::seconds(30)); state_.store(State::STARTED); } @@ -67,14 +67,14 @@ void ClientImpl::start() { void ClientImpl::shutdown() { state_.store(State::STOPPING, std::memory_order_relaxed); if (name_server_update_handle_) { - client_instance_->getScheduler().cancel(name_server_update_handle_); + client_manager_->getScheduler().cancel(name_server_update_handle_); } if (route_update_handle_) { - client_instance_->getScheduler().cancel(route_update_handle_); + client_manager_->getScheduler().cancel(route_update_handle_); } - client_instance_.reset(); + client_manager_.reset(); } const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; @@ -188,7 +188,7 @@ void ClientImpl::renewNameServerList() { } } }; - client_instance_->topAddressing().fetchNameServerAddresses(callback); + client_manager_->topAddressing().fetchNameServerAddresses(callback); } bool ClientImpl::selectNameServer(std::string& selected, bool change) { @@ -235,7 +235,7 @@ void ClientImpl::fetchRouteFor(const std::string& topic, const std::function metadata; Signature::sign(this, metadata); - client_instance_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } void ClientImpl::updateRouteInfo() { @@ -292,7 +292,7 @@ void ClientImpl::heartbeat() { } SPDLOG_DEBUG("Heartbeat to {} OK", target); }; - client_instance_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } } @@ -341,7 +341,7 @@ void ClientImpl::updateRouteCache(const std::string& topic, const TopicRouteData void ClientImpl::multiplexing(const std::string& target, const MultiplexingRequest& request) { absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_instance_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + client_manager_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); } @@ -354,7 +354,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextgetScheduler().schedule(multiplexingLater, task_name, std::chrono::seconds(3), + client_manager_->getScheduler().schedule(multiplexingLater, task_name, std::chrono::seconds(3), std::chrono::seconds(0)); return; } @@ -368,7 +368,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContext metadata; Signature::sign(this, metadata); - client_instance_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + client_manager_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); break; } @@ -377,7 +377,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextresponse.verify_message_consumption_request().message(); MQMessageExt message; MultiplexingRequest request; - if (!client_instance_->wrapMessage(data, message)) { + if (!client_manager_->wrapMessage(data, message)) { SPDLOG_WARN("Message to verify consumption is corrupted"); request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( google::rpc::Code::INVALID_ARGUMENT); @@ -397,7 +397,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextresponse.resolve_orphaned_transaction_request().orphaned_transactional_message(); MQMessageExt message; - if (client_instance_->wrapMessage(orphan, message)) { + if (client_manager_->wrapMessage(orphan, message)) { MessageAccessor::setTargetEndpoint(message, ctx->remote_address); const std::string& transaction_id = ctx->response.resolve_orphaned_transaction_request().transaction_id(); resolveOrphanedTransactionalMessage(transaction_id, message); @@ -452,13 +452,13 @@ void ClientImpl::healthCheck() { HealthCheckRequest request; absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_instance_->healthCheck(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->healthCheck(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } } void ClientImpl::schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) { - client_instance_->getScheduler().schedule(task, task_name, delay, std::chrono::milliseconds(0)); + client_manager_->getScheduler().schedule(task, task_name, delay, std::chrono::milliseconds(0)); } void ClientImpl::onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) { diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index cdad3e23e..43f6ec6d1 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -1,7 +1,7 @@ #include "ConsumeMessageService.h" -#include "DefaultMQPushConsumerImpl.h" #include "MessageAccessor.h" #include "ProcessQueue.h" +#include "PushConsumerImpl.h" #include #include #include diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index afee3ffea..e70e8a3ce 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -1,13 +1,13 @@ #include "rocketmq/DefaultMQProducer.h" -#include "DefaultMQProducerImpl.h" #include "MixAll.h" +#include "ProducerImpl.h" #include "absl/strings/str_split.h" ROCKETMQ_NAMESPACE_BEGIN DefaultMQProducer::DefaultMQProducer(const std::string& group_name) - : impl_(std::make_shared(group_name)) {} + : impl_(std::make_shared(group_name)) {} void DefaultMQProducer::start() { impl_->start(); } @@ -103,8 +103,6 @@ void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credential impl_->setCredentialsProvider(std::move(credentials_provider)); } -void DefaultMQProducer::setRegion(const std::string& region) { - impl_->region(region); -} +void DefaultMQProducer::setRegion(const std::string& region) { impl_->region(region); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index 338a977c0..23add520c 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -1,12 +1,12 @@ #include "rocketmq/DefaultMQPullConsumer.h" #include "AwaitPullCallback.h" -#include "DefaultMQPullConsumerImpl.h" +#include "PullConsumerImpl.h" #include "absl/strings/str_split.h" ROCKETMQ_NAMESPACE_BEGIN DefaultMQPullConsumer::DefaultMQPullConsumer(const std::string& group_name) - : impl_(std::make_shared(group_name)) {} + : impl_(std::make_shared(group_name)) {} void DefaultMQPullConsumer::start() { impl_->start(); } diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index d891de3c2..a7b4223fa 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -1,5 +1,5 @@ #include "rocketmq/DefaultMQPushConsumer.h" -#include "DefaultMQPushConsumerImpl.h" +#include "PushConsumerImpl.h" #include "absl/strings/str_split.h" #include @@ -12,7 +12,7 @@ DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) : gr std::string err_msg = "create consumer with same group name in a process, group name :" + group_name; THROW_MQ_EXCEPTION(MQClientException, err_msg, -1); } else { - impl_ = std::make_shared(group_name); + impl_ = std::make_shared(group_name); consumerTable.insert(group_name); } } diff --git a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp similarity index 73% rename from src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp rename to src/main/cpp/rocketmq/ProducerImpl.cpp index 91fdad19b..dd825bcd7 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -1,4 +1,4 @@ -#include "DefaultMQProducerImpl.h" +#include "ProducerImpl.h" #include "MessageAccessor.h" #include "MessageGroupQueueSelector.h" #include "MetadataConstants.h" @@ -16,24 +16,24 @@ ROCKETMQ_NAMESPACE_BEGIN -DefaultMQProducerImpl::DefaultMQProducerImpl(std::string group_name) +ProducerImpl::ProducerImpl(std::string group_name) : ClientImpl(std::move(group_name)), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { // TODO: initialize client_config_ and fault_strategy_ } -DefaultMQProducerImpl::~DefaultMQProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); } +ProducerImpl::~ProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); } -void DefaultMQProducerImpl::start() { +void ProducerImpl::start() { ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected producer state: {}", state_.load(std::memory_order_relaxed)); return; } - client_instance_->addClientObserver(shared_from_this()); + client_manager_->addClientObserver(shared_from_this()); } -void DefaultMQProducerImpl::shutdown() { +void ProducerImpl::shutdown() { ClientImpl::shutdown(); State expected = State::STOPPING; @@ -42,18 +42,18 @@ void DefaultMQProducerImpl::shutdown() { } } -bool DefaultMQProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } +bool ProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } -void DefaultMQProducerImpl::ensureRunning() const { +void ProducerImpl::ensureRunning() const { if (!isRunning()) { THROW_MQ_EXCEPTION(MQClientException, "Invoke #start() first", ILLEGAL_STATE); } } -void DefaultMQProducerImpl::validate(const MQMessage& message) {} +void ProducerImpl::validate(const MQMessage& message) {} -std::string DefaultMQProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, - const MQMessageQueue& message_queue) { +std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue) { request.mutable_message()->mutable_topic()->set_arn(arn_); request.mutable_message()->mutable_topic()->set_name(message.getTopic()); @@ -104,7 +104,7 @@ std::string DefaultMQProducerImpl::wrapSendMessageRequest(const MQMessage& messa return message_id; } -SendResult DefaultMQProducerImpl::send(const MQMessage& message) { +SendResult ProducerImpl::send(const MQMessage& message) { ensureRunning(); auto topic_publish_info = getPublishInfo(message.getTopic()); if (!topic_publish_info) { @@ -127,14 +127,14 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message) { THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); } -SendResult DefaultMQProducerImpl::send(const MQMessage& message, const std::string& message_group) { +SendResult ProducerImpl::send(const MQMessage& message, const std::string& message_group) { MessageGroupQueueSelector selector(message_group); return send(message, &selector, nullptr); } -SendResult DefaultMQProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue) { +SendResult ProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue) { ensureRunning(); - std::vector message_queue_list{message_queue}; + std::vector message_queue_list{withServiceAddress(message_queue)}; AwaitSendCallback callback; send0(message, &callback, message_queue_list, max_attempt_times_); callback.await(); @@ -144,7 +144,7 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message, const MQMessage THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); } -SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg) { +SendResult ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg) { ensureRunning(); std::vector message_queue_list; executeMessageQueueSelector(message, selector, arg, message_queue_list); @@ -160,8 +160,7 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSel THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); } -SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, - int max_attempt_times) { +SendResult ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int max_attempts) { ensureRunning(); std::vector message_queue_list; executeMessageQueueSelector(message, selector, arg, message_queue_list); @@ -169,7 +168,7 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSel THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); } AwaitSendCallback callback; - send0(message, &callback, message_queue_list, max_attempt_times); + send0(message, &callback, message_queue_list, max_attempts); callback.await(); if (callback) { return callback.sendResult(); @@ -177,7 +176,7 @@ SendResult DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSel THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); } -void DefaultMQProducerImpl::send(const MQMessage& message, SendCallback* cb) { +void ProducerImpl::send(const MQMessage& message, SendCallback* cb) { ensureRunning(); auto callback = [this, message, cb](const TopicPublishInfoPtr& publish_info) { if (!publish_info) { @@ -194,15 +193,13 @@ void DefaultMQProducerImpl::send(const MQMessage& message, SendCallback* cb) { asyncPublishInfo(message.getTopic(), callback); } -void DefaultMQProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue, - SendCallback* callback) { +void ProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* callback) { ensureRunning(); - std::vector message_queue_list{message_queue}; + std::vector message_queue_list{withServiceAddress(message_queue)}; send0(message, callback, message_queue_list, max_attempt_times_); } -void DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, - SendCallback* callback) { +void ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* callback) { ensureRunning(); auto cb = [this, message, selector, callback, arg](const TopicPublishInfoPtr& ptr) { @@ -221,7 +218,7 @@ void DefaultMQProducerImpl::send(const MQMessage& message, MessageQueueSelector* asyncPublishInfo(message.getTopic(), cb); } -void DefaultMQProducerImpl::sendOneway(const MQMessage& message) { +void ProducerImpl::sendOneway(const MQMessage& message) { ensureRunning(); auto callback = [this, message](const TopicPublishInfoPtr& ptr) { if (!ptr) { @@ -238,13 +235,13 @@ void DefaultMQProducerImpl::sendOneway(const MQMessage& message) { asyncPublishInfo(message.getTopic(), callback); } -void DefaultMQProducerImpl::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { +void ProducerImpl::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { ensureRunning(); - std::vector list{message_queue}; + std::vector list{withServiceAddress(message_queue)}; send0(message, onewaySendCallback(), list, 1); } -void DefaultMQProducerImpl::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { +void ProducerImpl::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { ensureRunning(); auto callback = [this, message, selector, arg](const TopicPublishInfoPtr& ptr) { @@ -260,12 +257,12 @@ void DefaultMQProducerImpl::sendOneway(const MQMessage& message, MessageQueueSel asyncPublishInfo(message.getTopic(), callback); } -void DefaultMQProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { +void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { transaction_state_checker_ = std::move(checker); } -void DefaultMQProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, - int max_attempt_times) { +void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, + int max_attempt_times) { assert(callback); if (list.empty()) { MQClientException e("Topic route not found", -1, __FILE__, __LINE__); @@ -296,17 +293,17 @@ void DefaultMQProducerImpl::send0(const MQMessage& message, SendCallback* callba Signature::sign(this, metadata); if (max_attempt_times == 1) { - client_instance_->send(target, metadata, request, callback); + client_manager_->send(target, metadata, request, callback); return; } auto retry_callback = - new RetrySendCallback(client_instance_, metadata, request, max_attempt_times, callback, std::move(list)); - client_instance_->send(target, metadata, request, retry_callback); + new RetrySendCallback(client_manager_, metadata, request, max_attempt_times, callback, std::move(list)); + client_manager_->send(target, metadata, request, retry_callback); } -bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, - const std::string& transaction_id, TransactionState resolution) { +bool ProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, + const std::string& transaction_id, TransactionState resolution) { EndTransactionRequest request; request.set_message_id(message_id); @@ -345,7 +342,7 @@ bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std cv.SignalAll(); } }; - client_instance_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); + client_manager_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); { absl::MutexLock lk(&mtx); cv.Wait(&mtx); @@ -353,45 +350,69 @@ bool DefaultMQProducerImpl::endTransaction0(const std::string& target, const std return success; } -void DefaultMQProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints) { +void ProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints) { absl::MutexLock lk(&isolated_endpoints_mtx_); endpoints.insert(isolated_endpoints_.begin(), isolated_endpoints_.end()); } -bool DefaultMQProducerImpl::isEndpointIsolated(const std::string& target) { +MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_queue) { + if (!message_queue.serviceAddress().empty()) { + return message_queue; + } + + if (message_queue.getTopic().empty() || message_queue.getBrokerName().empty() || message_queue.getQueueId() < 0) { + MQClientException e("Message queue is illegal", MESSAGE_QUEUE_ILLEGAL, __FILE__, __LINE__); + throw e; + } + + std::vector list = getTopicMessageQueueInfo(message_queue.getTopic()); + for (const auto& item : list) { + if (item == message_queue) { + return item; + } + } + + if (list.empty()) { + MQClientException e("No topic route available", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); + throw e; + } else { + return *list.begin(); + } +} + +bool ProducerImpl::isEndpointIsolated(const std::string& target) { absl::MutexLock lk(&isolated_endpoints_mtx_); return isolated_endpoints_.contains(target); } -void DefaultMQProducerImpl::isolateEndpoint(const std::string& target) { +void ProducerImpl::isolateEndpoint(const std::string& target) { absl::MutexLock lk(&isolated_endpoints_mtx_); isolated_endpoints_.insert(target); } -std::unique_ptr DefaultMQProducerImpl::prepare(const MQMessage& message) { +std::unique_ptr ProducerImpl::prepare(const MQMessage& message) { try { SendResult send_result = send(message); return std::unique_ptr(new TransactionImpl(send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), - DefaultMQProducerImpl::shared_from_this())); + ProducerImpl::shared_from_this())); } catch (const MQClientException& e) { SPDLOG_ERROR("Failed to send transaction message. Cause: {}", e.what()); return nullptr; } } -bool DefaultMQProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, - const std::string& target) { +bool ProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, const std::string& target) { return endTransaction0(target, message_id, transaction_id, TransactionState::COMMIT); } -bool DefaultMQProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, - const std::string& target) { +bool ProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, + const std::string& target) { return endTransaction0(target, message_id, transaction_id, TransactionState::ROLLBACK); } -void DefaultMQProducerImpl::asyncPublishInfo(const std::string& topic, - const std::function& cb) { +void ProducerImpl::asyncPublishInfo(const std::string& topic, + const std::function& cb) { TopicPublishInfoPtr ptr; { absl::MutexLock lock(&topic_publish_info_mtx_); @@ -419,7 +440,7 @@ void DefaultMQProducerImpl::asyncPublishInfo(const std::string& topic, } } -TopicPublishInfoPtr DefaultMQProducerImpl::getPublishInfo(const std::string& topic) { +TopicPublishInfoPtr ProducerImpl::getPublishInfo(const std::string& topic) { bool complete = false; absl::Mutex mtx; absl::CondVar cv; @@ -440,8 +461,8 @@ TopicPublishInfoPtr DefaultMQProducerImpl::getPublishInfo(const std::string& top return topic_publish_info; } -bool DefaultMQProducerImpl::executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, - void* arg, std::vector& result) { +bool ProducerImpl::executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, void* arg, + std::vector& result) { TopicPublishInfoPtr publish_info; absl::Mutex mtx; absl::CondVar cv; @@ -468,15 +489,15 @@ bool DefaultMQProducerImpl::executeMessageQueueSelector(const MQMessage& message return true; } -void DefaultMQProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, - std::vector& message_queues, int number) { +void ProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, + std::vector& message_queues, int number) { assert(publish_info); absl::flat_hash_set isolated; isolatedEndpoints(isolated); publish_info->takeMessageQueues(isolated, message_queues, number); } -std::vector DefaultMQProducerImpl::getTopicMessageQueueInfo(const std::string& topic) { +std::vector ProducerImpl::getTopicMessageQueueInfo(const std::string& topic) { absl::Mutex mtx; absl::CondVar cv; bool completed = false; @@ -501,15 +522,14 @@ std::vector DefaultMQProducerImpl::getTopicMessageQueueInfo(cons return ptr->getMessageQueueList(); } -void DefaultMQProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { +void ProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { rmq::HeartbeatEntry entry; entry.mutable_producer_group()->mutable_group()->set_arn(arn_); entry.mutable_producer_group()->mutable_group()->set_name(group_name_); request.mutable_heartbeats()->Add(std::move(entry)); } -void DefaultMQProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, - const MQMessageExt& message) { +void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { if (transaction_state_checker_) { TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); const std::string& target_host = MessageAccessor::targetEndpoint(message); diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp similarity index 86% rename from src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp rename to src/main/cpp/rocketmq/PullConsumerImpl.cpp index b19e2571d..229309545 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -1,20 +1,20 @@ -#include "DefaultMQPullConsumerImpl.h" +#include "PullConsumerImpl.h" #include "ClientManagerFactory.h" #include "InvocationContext.h" #include "Signature.h" ROCKETMQ_NAMESPACE_BEGIN -void DefaultMQPullConsumerImpl::start() { +void PullConsumerImpl::start() { ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); return; } - client_instance_->addClientObserver(shared_from_this()); + client_manager_->addClientObserver(shared_from_this()); } -void DefaultMQPullConsumerImpl::shutdown() { +void PullConsumerImpl::shutdown() { // Shutdown services started by current tier // Shutdown services that are started by the parent @@ -25,7 +25,7 @@ void DefaultMQPullConsumerImpl::shutdown() { } } -std::future> DefaultMQPullConsumerImpl::queuesFor(const std::string& topic) { +std::future> PullConsumerImpl::queuesFor(const std::string& topic) { auto promise = std::make_shared>>(); { absl::MutexLock lk(&topic_route_table_mtx_); @@ -54,7 +54,7 @@ std::future> DefaultMQPullConsumerImpl::queuesFor(co return promise->get_future(); } -std::future DefaultMQPullConsumerImpl::queryOffset(const OffsetQuery& query) { +std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { QueryOffsetRequest request; switch (query.policy) { case QueryOffsetPolicy::BEGINNING: @@ -93,12 +93,12 @@ std::future DefaultMQPullConsumerImpl::queryOffset(const OffsetQuery& q promise_ptr->set_exception(std::make_exception_ptr(e)); }; - client_instance_->queryOffset(query.message_queue.serviceAddress(), metadata, request, + client_manager_->queryOffset(query.message_queue.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); return promise_ptr->get_future(); } -void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { +void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { PullMessageRequest request; request.set_offset(query.offset); auto duration = absl::FromChrono(query.await_time); @@ -134,7 +134,7 @@ void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback std::vector messages; for (const auto& item : response.messages()) { MQMessageExt message_ext; - if (client_instance_->wrapMessage(item, message_ext)) { + if (client_manager_->wrapMessage(item, message_ext)) { messages.emplace_back(message_ext); } } @@ -145,10 +145,10 @@ void DefaultMQPullConsumerImpl::pull(const PullMessageQuery& query, PullCallback absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_instance_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), callback); + client_manager_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), callback); } -void DefaultMQPullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { +void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { rmq::HeartbeatEntry entry; entry.mutable_producer_group()->mutable_group()->set_arn(arn_); entry.mutable_producer_group()->mutable_group()->set_name(group_name_); diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp similarity index 76% rename from src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp rename to src/main/cpp/rocketmq/PushConsumerImpl.cpp index 614d7ebf1..ad9443aa7 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -1,4 +1,4 @@ -#include "DefaultMQPushConsumerImpl.h" +#include "PushConsumerImpl.h" #include "AsyncReceiveMessageCallback.h" #include "ClientManagerFactory.h" @@ -11,11 +11,11 @@ ROCKETMQ_NAMESPACE_BEGIN -DefaultMQPushConsumerImpl::DefaultMQPushConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} +PushConsumerImpl::PushConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} -DefaultMQPushConsumerImpl::~DefaultMQPushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } +PushConsumerImpl::~PushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } -void DefaultMQPushConsumerImpl::start() { +void PushConsumerImpl::start() { ClientImpl::start(); if (State::STARTED != state_.load(std::memory_order_relaxed)) { @@ -23,7 +23,7 @@ void DefaultMQPushConsumerImpl::start() { return; } - client_instance_->addClientObserver(shared_from_this()); + client_manager_->addClientObserver(shared_from_this()); fetchRoutes(); heartbeat(); @@ -53,27 +53,27 @@ void DefaultMQPushConsumerImpl::start() { SPDLOG_WARN("Message listener is unexpected nullptr"); } - std::weak_ptr consumer_weak_ptr(shared_from_this()); + std::weak_ptr consumer_weak_ptr(shared_from_this()); auto scan_assignment_functor = [consumer_weak_ptr]() { - std::shared_ptr consumer = consumer_weak_ptr.lock(); + std::shared_ptr consumer = consumer_weak_ptr.lock(); if (consumer) { consumer->scanAssignments(); } }; - scan_assignment_handle_ = client_instance_->getScheduler().schedule( + scan_assignment_handle_ = client_manager_->getScheduler().schedule( scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); state_.store(State::STARTED, std::memory_order_relaxed); - SPDLOG_INFO("DefaultMQPushConsumer started, groupName={}", group_name_); + SPDLOG_INFO("PushConsumer started, groupName={}", group_name_); } -const char* DefaultMQPushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; +const char* PushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; -void DefaultMQPushConsumerImpl::shutdown() { +void PushConsumerImpl::shutdown() { if (scan_assignment_handle_) { - client_instance_->getScheduler().cancel(scan_assignment_handle_); + client_manager_->getScheduler().cancel(scan_assignment_handle_); SPDLOG_DEBUG("Scan assignment periodic task cancelled"); } @@ -90,23 +90,23 @@ void DefaultMQPushConsumerImpl::shutdown() { ClientImpl::shutdown(); State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED)) { - SPDLOG_INFO("DefaultMQPushConsumerImpl stopped"); + SPDLOG_INFO("PushConsumerImpl stopped"); } } -void DefaultMQPushConsumerImpl::subscribe(const std::string& topic, const std::string& expression, - ExpressionType expression_type) { +void PushConsumerImpl::subscribe(const std::string& topic, const std::string& expression, + ExpressionType expression_type) { absl::MutexLock lock(&topic_filter_expression_table_mtx_); FilterExpression filter_expression{expression, expression_type}; topic_filter_expression_table_.emplace(topic, filter_expression); } -void DefaultMQPushConsumerImpl::unsubscribe(const std::string& topic) { +void PushConsumerImpl::unsubscribe(const std::string& topic) { absl::MutexLock lock(&topic_filter_expression_table_mtx_); topic_filter_expression_table_.erase(topic); } -absl::flat_hash_map DefaultMQPushConsumerImpl::getTopicFilterExpressionTable() const { +absl::flat_hash_map PushConsumerImpl::getTopicFilterExpressionTable() const { absl::flat_hash_map topic_filter_expression_table; { absl::MutexLock lock(&topic_filter_expression_table_mtx_); @@ -118,11 +118,11 @@ absl::flat_hash_map DefaultMQPushConsumerImpl::ge return topic_filter_expression_table; } -void DefaultMQPushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { +void PushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { consume_from_where_ = consume_from_where; } -void DefaultMQPushConsumerImpl::scanAssignments() { +void PushConsumerImpl::scanAssignments() { SPDLOG_DEBUG("Start of assignment scanning"); if (!active()) { SPDLOG_INFO("Client has stopped. Abort scanning immediately."); @@ -147,7 +147,7 @@ void DefaultMQPushConsumerImpl::scanAssignments() { SPDLOG_DEBUG("End of assignment scanning."); } -bool DefaultMQPushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_route_data, std::string& broker_host) { +bool PushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_route_data, std::string& broker_host) { if (topic_route_data && !topic_route_data->partitions().empty()) { uint32_t index = TopicAssignment::getAndIncreaseQueryWhichBroker(); for (uint32_t i = index; i < index + topic_route_data->partitions().size(); i++) { @@ -165,10 +165,9 @@ bool DefaultMQPushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_rout return false; } -void DefaultMQPushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, - const std::string& client_id, - const std::string& strategy_name, - QueryAssignmentRequest& request) { +void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, + const std::string& client_id, const std::string& strategy_name, + QueryAssignmentRequest& request) { request.mutable_topic()->set_name(topic); request.mutable_topic()->set_arn(arn()); request.mutable_group()->set_name(consumer_group); @@ -176,8 +175,8 @@ void DefaultMQPushConsumerImpl::wrapQueryAssignmentRequest(const std::string& to request.set_client_id(client_id); } -void DefaultMQPushConsumerImpl::queryAssignment(const std::string& topic, - const std::function& cb) { +void PushConsumerImpl::queryAssignment(const std::string& topic, + const std::function& cb) { auto callback = [this, topic, cb](const TopicRouteDataPtr& topic_route) { TopicAssignmentPtr topic_assignment; @@ -220,8 +219,8 @@ void DefaultMQPushConsumerImpl::queryAssignment(const std::string& topic, } }; - client_instance_->queryAssignment(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - assignment_callback); + client_manager_->queryAssignment(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + assignment_callback); }; getRouteFor(topic, callback); } @@ -232,9 +231,9 @@ void DefaultMQPushConsumerImpl::queryAssignment(const std::string& topic, * @param assignment Latest assignment from load balancer * @param filter_expression Filter expression */ -void DefaultMQPushConsumerImpl::syncProcessQueue(const std::string& topic, - const std::shared_ptr& topic_assignment, - const FilterExpression& filter_expression) { +void PushConsumerImpl::syncProcessQueue(const std::string& topic, + const std::shared_ptr& topic_assignment, + const FilterExpression& filter_expression) { const std::vector& assignment_list = topic_assignment->assignmentList(); std::vector message_queue_list; message_queue_list.reserve(assignment_list.size()); @@ -287,14 +286,14 @@ void DefaultMQPushConsumerImpl::syncProcessQueue(const std::string& topic, } } -ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression, - ConsumeMessageType consume_type) { +ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, + const FilterExpression& filter_expression, + ConsumeMessageType consume_type) { ProcessQueueSharedPtr process_queue; { absl::MutexLock lock(&process_queue_table_mtx_); if (!active()) { - SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop creation of ProcessQueue"); + SPDLOG_INFO("PushConsumer has stopped. Drop creation of ProcessQueue"); return process_queue; } @@ -303,8 +302,8 @@ ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const M } else { SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); // create ProcessQueue - process_queue = std::make_shared(message_queue, filter_expression, consume_type, shared_from_this(), - client_instance_); + process_queue = std::make_shared(message_queue, filter_expression, consume_type, + shared_from_this(), client_manager_); std::shared_ptr receive_callback = std::make_shared(process_queue); process_queue->callback(receive_callback); @@ -314,17 +313,16 @@ ProcessQueueSharedPtr DefaultMQPushConsumerImpl::getOrCreateProcessQueue(const M return process_queue; } -bool DefaultMQPushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression, - ConsumeMessageType consume_type) { +bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, + ConsumeMessageType consume_type) { if (!active()) { - SPDLOG_INFO("DefaultMQPushConsumer has stopped. Drop further receive message request"); + SPDLOG_INFO("PushConsumer has stopped. Drop further receive message request"); return false; } ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression, consume_type); if (!process_queue_ptr) { - SPDLOG_INFO("consumer has stopped. Stop creating processQueue"); + SPDLOG_INFO("Consumer has stopped. Stop creating processQueue"); return false; } @@ -357,7 +355,7 @@ bool DefaultMQPushConsumerImpl::receiveMessage(const MQMessageQueue& message_que message_queue.simpleName(), broker_host); } }; - client_instance_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } break; } @@ -368,11 +366,9 @@ bool DefaultMQPushConsumerImpl::receiveMessage(const MQMessageQueue& message_que return true; } -std::shared_ptr DefaultMQPushConsumerImpl::getConsumeMessageService() { - return consume_message_service_; -} +std::shared_ptr PushConsumerImpl::getConsumeMessageService() { return consume_message_service_; } -void DefaultMQPushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { +void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { const std::string& target_host = MessageAccessor::targetEndpoint(msg); SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, msg.getTopic(), msg.getQueueId(), msg.getMsgId()); @@ -380,10 +376,10 @@ void DefaultMQPushConsumerImpl::ack(const MQMessageExt& msg, const std::function wrapAckMessageRequest(msg, request); absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_instance_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } -void DefaultMQPushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { +void PushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { std::string target_host = MessageAccessor::targetEndpoint(msg); absl::flat_hash_map metadata; @@ -403,12 +399,11 @@ void DefaultMQPushConsumerImpl::nack(const MQMessageExt& msg, const std::functio request.set_delivery_attempt(msg.getDeliveryAttempt() + 1); request.set_max_delivery_attempts(max_delivery_attempts_); - client_instance_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); SPDLOG_DEBUG("Send message nack to broker server[host={}]", target_host); } -void DefaultMQPushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, - const std::function& cb) { +void PushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) { std::string target_host = MessageAccessor::targetEndpoint(message); absl::flat_hash_map metadata; @@ -427,11 +422,11 @@ void DefaultMQPushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& mes request.set_delivery_attempt(message.getDeliveryAttempt()); request.set_max_delivery_attempts(max_delivery_attempts_); - client_instance_->forwardMessageToDeadLetterQueue(target_host, metadata, request, - absl::ToChronoMilliseconds(io_timeout_), cb); + client_manager_->forwardMessageToDeadLetterQueue(target_host, metadata, request, + absl::ToChronoMilliseconds(io_timeout_), cb); } -void DefaultMQPushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { +void PushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { request.mutable_group()->set_arn(arn_); request.mutable_group()->set_name(group_name_); request.mutable_topic()->set_arn(arn_); @@ -441,17 +436,17 @@ void DefaultMQPushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, A request.set_receipt_handle(msg.receiptHandle()); } -uint32_t DefaultMQPushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } +uint32_t PushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } -void DefaultMQPushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { +void PushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { if (thread_pool_size >= 1) { consume_thread_pool_size_ = thread_pool_size; } } -uint32_t DefaultMQPushConsumerImpl::consumeBatchSize() const { return consume_batch_size_; } +uint32_t PushConsumerImpl::consumeBatchSize() const { return consume_batch_size_; } -void DefaultMQPushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { +void PushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { // For FIFO messages, consume batch size should always be 1. if (message_listener_ && message_listener_->listenerType() == MessageListenerType::FIFO) { @@ -463,16 +458,16 @@ void DefaultMQPushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { } } -void DefaultMQPushConsumerImpl::registerMessageListener(MessageListener* message_listener) { +void PushConsumerImpl::registerMessageListener(MessageListener* message_listener) { message_listener_ = message_listener; } -std::size_t DefaultMQPushConsumerImpl::getProcessQueueTableSize() { +std::size_t PushConsumerImpl::getProcessQueueTableSize() { absl::MutexLock lock(&process_queue_table_mtx_); return process_queue_table_.size(); } -void DefaultMQPushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) { +void PushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) { absl::MutexLock lock(&throttle_table_mtx_); throttle_table_.emplace(topic, threshold); // If consumer has started, update it dynamically. @@ -490,7 +485,7 @@ nostd::shared_ptr DefaultMQPushConsumerImpl::getTracer() { } #endif -void DefaultMQPushConsumerImpl::iterateProcessQueue(const std::function& callback) { +void PushConsumerImpl::iterateProcessQueue(const std::function& callback) { absl::MutexLock lock(&process_queue_table_mtx_); for (const auto& item : process_queue_table_) { if (item.second->hasPendingMessages()) { @@ -499,7 +494,7 @@ void DefaultMQPushConsumerImpl::iterateProcessQueue(const std::function topics; { absl::MutexLock lk(&topic_filter_expression_table_mtx_); @@ -536,7 +531,7 @@ void DefaultMQPushConsumerImpl::fetchRoutes() { SPDLOG_INFO("Fetched route for {} out of {} topics", acquired, topics.size()); } -void DefaultMQPushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { +void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { auto heartbeat = new rmq::HeartbeatEntry(); heartbeat->set_client_id(clientId()); auto consumer_group = heartbeat->mutable_consumer_group(); @@ -566,7 +561,7 @@ void DefaultMQPushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) } } -ClientResourceBundle DefaultMQPushConsumerImpl::resourceBundle() { +ClientResourceBundle PushConsumerImpl::resourceBundle() { ClientResourceBundle resource_bundle = ClientImpl::resourceBundle(); { absl::MutexLock lk(&topic_filter_expression_table_mtx_); diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 6e7080dbf..0ba44e89b 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -1,10 +1,10 @@ #include "TransactionImpl.h" -#include "DefaultMQProducerImpl.h" +#include "ProducerImpl.h" ROCKETMQ_NAMESPACE_BEGIN bool TransactionImpl::commit() { - std::shared_ptr producer = producer_.lock(); + std::shared_ptr producer = producer_.lock(); if (!producer) { return false; } @@ -13,7 +13,7 @@ bool TransactionImpl::commit() { } bool TransactionImpl::rollback() { - std::shared_ptr producer = producer_.lock(); + std::shared_ptr producer = producer_.lock(); if (!producer) { return false; } diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index d4a68bc5f..718be51a4 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -5,8 +5,7 @@ ROCKETMQ_NAMESPACE_BEGIN class AwaitPullCallback : public PullCallback { public: - explicit AwaitPullCallback(PullResult& pull_result) - : pull_result_(pull_result), completed_(false), has_failure_(false) {} + explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) {} void onSuccess(const PullResult& pull_result) override; @@ -14,12 +13,16 @@ class AwaitPullCallback : public PullCallback { bool await(); + bool hasFailure() const { return has_failure_; } + + bool isCompleted() const { return completed_; } + private: PullResult& pull_result_; absl::Mutex mtx_; absl::CondVar cv_; - bool completed_; - bool has_failure_; + bool completed_{false}; + bool has_failure_{false}; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 723c8c823..331126536 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -48,7 +48,7 @@ class ClientImpl : virtual public Client, std::chrono::milliseconds delay) override; protected: - ClientManagerPtr client_instance_; + ClientManagerPtr client_manager_; std::atomic state_; absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); diff --git a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h similarity index 94% rename from src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h rename to src/main/cpp/rocketmq/include/ProducerImpl.h index 6e6e47540..d88d2c2d1 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -19,12 +19,11 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQProducerImpl : virtual public ClientImpl, - public std::enable_shared_from_this { +class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_this { public: - explicit DefaultMQProducerImpl(std::string group_name); + explicit ProducerImpl(std::string group_name); - ~DefaultMQProducerImpl() override; + ~ProducerImpl() override; void prepareHeartbeatData(HeartbeatRequest& request) override; @@ -37,7 +36,7 @@ class DefaultMQProducerImpl : virtual public ClientImpl, SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); - SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int auto_retry_times); + SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int max_attempts); void send(const MQMessage& message, SendCallback* callback); void send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* callback); @@ -123,6 +122,8 @@ class DefaultMQProducerImpl : virtual public ClientImpl, TransactionState resolution); void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + MQMessageQueue withServiceAddress(const MQMessageQueue& message_queue); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h similarity index 64% rename from src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h rename to src/main/cpp/rocketmq/include/PullConsumerImpl.h index bf1b2097f..6eb5c6c37 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -11,10 +11,9 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPullConsumerImpl : public ClientImpl, - public std::enable_shared_from_this { +class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this { public: - explicit DefaultMQPullConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} + explicit PullConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} void start() override; @@ -29,9 +28,7 @@ class DefaultMQPullConsumerImpl : public ClientImpl, void prepareHeartbeatData(HeartbeatRequest& request) override; protected: - std::shared_ptr self() override { - return shared_from_this(); - } + std::shared_ptr self() override { return shared_from_this(); } }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h similarity index 95% rename from src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h rename to src/main/cpp/rocketmq/include/PushConsumerImpl.h index a1bdaa148..a4fb81959 100644 --- a/src/main/cpp/rocketmq/include/DefaultMQPushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -26,13 +26,13 @@ class ConsumeMessageService; class ConsumeFifoMessageService; class ConsumeStandardMessageService; -class DefaultMQPushConsumerImpl : virtual public ClientImpl, - virtual public PushConsumer, - public std::enable_shared_from_this { +class PushConsumerImpl : virtual public ClientImpl, + virtual public PushConsumer, + public std::enable_shared_from_this { public: - explicit DefaultMQPushConsumerImpl(std::string group_name); + explicit PushConsumerImpl(std::string group_name); - ~DefaultMQPushConsumerImpl() override; + ~PushConsumerImpl() override; void prepareHeartbeatData(HeartbeatRequest& request) override; diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 1c1b9219d..18f5d102e 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -7,12 +7,12 @@ ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQProducerImpl; +class ProducerImpl; class TransactionImpl : public Transaction { public: TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, - const std::shared_ptr& producer) + const std::shared_ptr& producer) : message_id_(std::move(message_id)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), producer_(producer) {} @@ -26,7 +26,7 @@ class TransactionImpl : public Transaction { std::string message_id_; std::string transaction_id_; std::string endpoint_; - std::weak_ptr producer_; + std::weak_ptr producer_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp new file mode 100644 index 000000000..3739c33ef --- /dev/null +++ b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp @@ -0,0 +1,68 @@ +#include "AwaitPullCallback.h" +#include "MessageAccessor.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/PullResult.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(AwaitPullCallbackTest, testOnSuccess) { + std::vector messages; + PullResult pull_result(0, 32, 32, messages); + AwaitPullCallback callback(pull_result); + EXPECT_FALSE(callback.isCompleted()); + + std::string topic{"Test"}; + int total = 32; + int min = 32; + int max = 128; + int next = 64; + auto task = [&]() { + std::vector msgs; + for (int i = 0; i < total; i++) { + MQMessageExt msg; + msg.setTopic(topic); + MessageAccessor::setQueueOffset(msg, i); + msgs.emplace_back(msg); + } + PullResult result(32, 128, 64, msgs); + callback.onSuccess(result); + }; + + std::thread t(task); + + callback.await(); + EXPECT_TRUE(callback.isCompleted()); + EXPECT_EQ(pull_result.messages().size(), total); + EXPECT_EQ(pull_result.min(), min); + EXPECT_EQ(pull_result.max(), max); + EXPECT_EQ(pull_result.next(), next); + + if (t.joinable()) { + t.join(); + } +} + +TEST(AwaitPullCallbackTest, testOnFailure) { + std::vector messages; + PullResult pull_result(0, 32, 32, messages); + AwaitPullCallback callback(pull_result); + std::string topic{"Test"}; + auto task = [&]() { + MQClientException e("test exception", -1, __FILE__, __LINE__); + callback.onException(e); + }; + + std::thread t(task); + + callback.await(); + EXPECT_TRUE(callback.isCompleted()); + EXPECT_TRUE(callback.hasFailure()); + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index e2a77a6f3..d71349204 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -150,4 +150,39 @@ cc_test( "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "await_pull_callback_test", + srcs = [ + "AwaitPullCallbackTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "push_consumer_impl_test", + srcs = [ + "PushConsumerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "producer_impl_test", + srcs = [ + "ProducerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt index e885e7be3..8b1fa443f 100644 --- a/src/test/cpp/ut/rocketmq/CMakeLists.txt +++ b/src/test/cpp/ut/rocketmq/CMakeLists.txt @@ -83,3 +83,9 @@ target_link_libraries(consumer_test PRIVATE rocketmq_mocks gtest_main) + +add_executable(await_pull_callback_test AwaitPullCallbackTest.cpp) +target_link_libraries(await_pull_callback_test + PRIVATE + rocketmq_library + gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 6991176c5..63e332392 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -1,7 +1,7 @@ #include "rocketmq/DefaultMQProducer.h" -#include "DefaultMQProducerImpl.h" #include "MQClientTest.h" +#include "ProducerImpl.h" #include "rocketmq/MQSelector.h" #include #include @@ -123,7 +123,7 @@ class UnitTestSendCallback : public SendCallback { }; TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { - auto producer = std::make_shared(group_name_); + auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); producer->start(); @@ -145,7 +145,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { } TEST_F(DefaultMQProducerUnitTest, testSendMessage) { - auto producer = std::make_shared(group_name_); + auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); producer->start(); @@ -156,7 +156,7 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { } TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { - auto producer = std::make_shared(group_name_); + auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); producer->start(); diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 7b21b0b4c..3c64730d6 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -1,8 +1,8 @@ #include "rocketmq/DefaultMQPushConsumer.h" #include "ClientManagerFactory.h" -#include "DefaultMQPushConsumerImpl.h" #include "InvocationContext.h" #include "MQClientTest.h" +#include "PushConsumerImpl.h" #include "absl/time/time.h" #include "rocketmq/MQMessageExt.h" #include "spdlog/spdlog.h" @@ -189,7 +189,7 @@ class MessageListenerUnitTests : public StandardMessageListener { TEST_F(DefaultMQPushConsumerUnitTest, testBroadcasting) { spdlog::set_level(spdlog::level::debug); - auto push_consumer = std::make_shared(group_name_); + auto push_consumer = std::make_shared(group_name_); push_consumer->setMessageModel(MessageModel::BROADCASTING); push_consumer->arn(arn_); push_consumer->setNameServerList(name_server_list_); @@ -208,7 +208,7 @@ TEST_F(DefaultMQPushConsumerUnitTest, testBroadcasting) { TEST_F(DefaultMQPushConsumerUnitTest, DISABLED_testClustering) { spdlog::set_level(spdlog::level::debug); - auto push_consumer = std::make_shared(group_name_); + auto push_consumer = std::make_shared(group_name_); push_consumer->setMessageModel(MessageModel::CLUSTERING); push_consumer->arn(arn_); push_consumer->setNameServerList(name_server_list_); diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp new file mode 100644 index 000000000..ef20e87cf --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -0,0 +1,231 @@ +#include "ProducerImpl.h" +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "TopicRouteData.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQSelector.h" +#include "rocketmq/RocketMQ.h" +#include "rocketmq/SendResult.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ProducerImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + client_manager_ = std::make_shared>(); + ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + producer_ = std::make_shared(group_); + producer_->arn(arn_); + producer_->setNameServerList(name_server_list_); + + { + std::vector partitions; + Topic topic(arn_, topic_); + std::vector
broker_addresses{Address(broker_host_, broker_port_)}; + ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); + Broker broker(broker_name_, broker_id_, service_address); + Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); + partitions.emplace_back(partition); + std::string debug_string; + topic_route_data_ = std::make_shared(partitions, debug_string); + } + } + + void TearDown() override { grpc_shutdown(); } + +protected: + std::shared_ptr> client_manager_; + std::shared_ptr producer_; + std::vector name_server_list_{"10.0.0.1:9876"}; + std::string arn_{"arn:mq://test"}; + std::string group_{"CID_test"}; + std::string topic_{"Topic0"}; + int queue_id_{1}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; + std::string message_group_{"group-0"}; + std::string broker_name_{"broker-a"}; + int broker_id_{0}; + std::string message_body_{"Message Body Content"}; + std::string broker_host_{"10.0.0.1"}; + int broker_port_{10911}; + TopicRouteDataPtr topic_route_data_; +}; + +TEST_F(ProducerImplTest, testStartShutdown) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + producer_->start(); + producer_->shutdown(); +} + +TEST_F(ProducerImplTest, testSend) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + producer_->send(message); + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +TEST_F(ProducerImplTest, testSend_WithMessageGroup) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + producer_->send(message, message_group_); + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +class TestMessageQueueSelector : public MessageQueueSelector { +public: + MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) override { + return *mqs.begin(); + } +}; + +TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + producer_->start(); + + auto selector = absl::make_unique(); + + MQMessage message(topic_, tag_, message_body_); + producer_->send(message, selector.get(), nullptr); + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +class TestSendCallback : public SendCallback { +public: + TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) {} + void onSuccess(const SendResult& send_result) override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + void onException(const MQException& e) override { + absl::MutexLock lk(&mtx_); + completed_ = true; + cv_.SignalAll(); + } + +protected: + bool& completed_; + absl::Mutex& mtx_; + absl::CondVar& cv_; +}; + +TEST_F(ProducerImplTest, testAsyncSend) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + bool cb_invoked = false; + SendResult send_result; + auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, + SendCallback* cb) { + cb->onSuccess(send_result); + cb_invoked = true; + return true; + }; + + EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + auto send_callback = absl::make_unique(completed, mtx, cv); + + producer_->start(); + + MQMessage message(topic_, tag_, message_body_); + producer_->send(message, send_callback.get()); + + if (!completed) { + absl::MutexLock lk(&mtx); + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + + EXPECT_TRUE(cb_invoked); + producer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp new file mode 100644 index 000000000..2833833d9 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -0,0 +1,163 @@ +#include "PushConsumerImpl.h" +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "InvocationContext.h" +#include "grpc/grpc.h" +#include "rocketmq/MQMessageExt.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerImplTest : public testing::Test { + void SetUp() override { + grpc_init(); + client_manager_ = std::make_shared>(); + ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + push_consumer_ = std::make_shared(group_); + push_consumer_->arn(arn_); + push_consumer_->setNameServerList(name_server_list_); + } + + void TearDown() override { grpc_shutdown(); } + +protected: + std::vector name_server_list_{"10.0.0.1:9876"}; + std::string arn_{"arn:mq://test"}; + std::string group_{"CID_test"}; + std::string topic_{"Topic0"}; + std::string tag_{"TagA"}; + std::string key_{"key-0"}; + std::string message_body_{"Message Body Content"}; + int delay_level_{1}; + std::shared_ptr> client_manager_; + std::shared_ptr push_consumer_; +}; + +TEST_F(PushConsumerImplTest, testAck) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + + auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { cb(true); }; + + EXPECT_CALL(*client_manager_, ack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(ack_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](bool ok) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + + push_consumer_->ack(message, callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +TEST_F(PushConsumerImplTest, testNack) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + + auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, + std::chrono::milliseconds timeout, const std::function& cb) { cb(true); }; + + EXPECT_CALL(*client_manager_, nack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(nack_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](bool ok) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + + push_consumer_->nack(message, callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +TEST_F(PushConsumerImplTest, testForward) { + Scheduler scheduler; + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); + + InvocationContext invocation_context; + + auto forward_cb = + [&](const std::string& target_host, const Metadata& metadata, + const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(&invocation_context); + }; + + EXPECT_CALL(*client_manager_, forwardMessageToDeadLetterQueue) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(forward_cb)); + + push_consumer_->start(); + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + auto callback = [&](bool ok) { + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + MQMessageExt message; + message.setTopic(topic_); + message.setBody(message_body_); + message.setTags(tag_); + message.setKey(key_); + message.setDelayTimeLevel(delay_level_); + + push_consumer_->forwardToDeadLetterQueue(message, callback); + + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + push_consumer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 941454c4c..9e363d2fa 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -2,11 +2,11 @@ #include "ClientManagerFactory.h" #include "RpcClientMock.h" +#include "ClientManagerImpl.h" #include "grpc/grpc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include -#include #include ROCKETMQ_NAMESPACE_BEGIN @@ -16,6 +16,7 @@ class MQClientTest : public testing::Test { MQClientTest() = default; void SetUp() override { + grpc_init(); name_server_list_.emplace_back(name_server_address_); client_instance_ = std::make_shared(arn_); rpc_client_ns_ = std::make_shared>(); @@ -32,10 +33,10 @@ class MQClientTest : public testing::Test { rpc_client_ns_.reset(); client_instance_->cleanRpcClients(); client_instance_.reset(); + grpc_shutdown(); } protected: - grpc::internal::GrpcLibraryInitializer initializer_; std::shared_ptr client_instance_; std::shared_ptr> rpc_client_ns_; const int16_t port_{10911}; From dd1beccb8e6d54e597d96e618d43dbbb1f54298e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 3 Aug 2021 20:22:07 +0800 Subject: [PATCH 031/133] Add unit test for InvocationContext.h --- src/main/cpp/client/include/RpcClient.h | 10 ++++----- src/test/cpp/ut/base/BUILD.bazel | 11 ++++++++++ .../cpp/ut/base/InvocationContextTest.cpp | 21 +++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/test/cpp/ut/base/InvocationContextTest.cpp diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index cb289b6e2..9d23b35cd 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include "apache/rocketmq/v1/service.pb.h" +#include "grpcpp/grpcpp.h" #include "InvocationContext.h" #include "OrphanTransactionCallback.h" @@ -100,9 +100,9 @@ class RpcClient { virtual void asyncPull(const PullMessageRequest& request, InvocationContext* invocation_context) = 0; - virtual void - asyncForwardMessageToDeadLetterQueue(const ForwardMessageToDeadLetterQueueRequest& request, - InvocationContext* invocation_context) = 0; + virtual void asyncForwardMessageToDeadLetterQueue( + const ForwardMessageToDeadLetterQueueRequest& request, + InvocationContext* invocation_context) = 0; /** * Indicate if heartbeat is required. diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index 686de3184..b6ae3c465 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -111,4 +111,15 @@ cc_test( "//src/main/cpp/base/mocks:base_mocks", "@com_google_googletest//:gtest_main", ] +) + +cc_test( + name = "invocation_context_test", + srcs = [ + "InvocationContextTest.cpp", + ], + deps = [ + "//src/main/cpp/client:client_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp new file mode 100644 index 000000000..9ebd98efd --- /dev/null +++ b/src/test/cpp/ut/base/InvocationContextTest.cpp @@ -0,0 +1,21 @@ +#include "InvocationContext.h" +#include "gtest/gtest.h" +#include "RpcClient.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(InvocationContextTest, testOnCompletion_OK) { + bool cb_invoked = false; + auto callback = [&](const InvocationContext* invocation_context) { + cb_invoked = true; + throw 1; + }; + + auto invocation_context = new InvocationContext(); + invocation_context->status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "Mock deadline exceeded"); + invocation_context->callback = callback; + invocation_context->onCompletion(true); + EXPECT_TRUE(cb_invoked); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 36be701e6746bb22b881d75bcc84c4dc005bc6db Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 3 Aug 2021 20:34:35 +0800 Subject: [PATCH 032/133] Add trival unit test --- src/test/cpp/ut/base/MQMessageExtTest.cpp | 27 +++++++++++++++++++---- src/test/cpp/ut/base/MQMessageTest.cpp | 8 +++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp index 36a62bacc..db99be3ef 100644 --- a/src/test/cpp/ut/base/MQMessageExtTest.cpp +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -1,4 +1,6 @@ #include "rocketmq/MQMessageExt.h" +#include "rocketmq/MQMessage.h" +#include "MessageAccessor.h" #include "gtest/gtest.h" #include @@ -6,17 +8,19 @@ ROCKETMQ_NAMESPACE_BEGIN class MQMessageExtTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + MessageAccessor::setMessageId(message_, msg_id_); + } void TearDown() override {} protected: + std::string msg_id_{"msg-0"}; + std::string topic_{"test"}; MQMessageExt message_; }; -TEST_F(MQMessageExtTest, testGetQueueId) { - EXPECT_EQ(message_.getQueueId(), 0); -} +TEST_F(MQMessageExtTest, testGetQueueId) { EXPECT_EQ(message_.getQueueId(), 0); } TEST_F(MQMessageExtTest, testBornTimestamp) { auto born_timestamp = message_.bornTimestamp(); @@ -25,4 +29,19 @@ TEST_F(MQMessageExtTest, testBornTimestamp) { TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { EXPECT_EQ(message_.getDeliveryAttempt(), 0); } +TEST_F(MQMessageExtTest, testGetBornTimestamp) { + int64_t born_timestamp = message_.getBornTimestamp(); + uint64_t last_second = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() - std::chrono::seconds(1)) + .count(); + EXPECT_TRUE(static_cast(born_timestamp) > last_second); +} + +TEST_F(MQMessageExtTest, testEqual) { + MQMessageExt other; + other.setTopic("test2"); + MessageAccessor::setMessageId(other, msg_id_); + EXPECT_TRUE(message_ == other); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp index d2feaef23..77af10e55 100644 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -32,4 +32,12 @@ TEST_F(MQMessageTest, testAssignment) { EXPECT_EQ(msg.getBody(), body_data_); } +TEST_F(MQMessageTest, testProperty) { + std::string key{"k"}; + std::string value{"value"}; + message.setProperty(key, value); + auto prop_value = message.getProperty(key); + EXPECT_EQ(value, prop_value); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file From a977afacfb3a0cafc4dc97968a2acc33f20fe866 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 3 Aug 2021 21:50:56 +0800 Subject: [PATCH 033/133] Add unit test for PullConsumerImpl (#10) Add unit test for PullConsumerImpl --- src/test/cpp/ut/rocketmq/BUILD.bazel | 12 +++ .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 79 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index d71349204..3d207acc3 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -185,4 +185,16 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "pull_consumer_impl_test", + srcs = [ + "PullConsumerImplTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp new file mode 100644 index 000000000..52a5e19e5 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -0,0 +1,79 @@ +#include "PullConsumerImpl.h" +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "Scheduler.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class PullConsumerImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + client_manager_ = std::make_shared>(); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); + ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + + pull_consumer_ = std::make_shared(group_); + pull_consumer_->setNameServerList(name_server_list_); + pull_consumer_->arn(arn_); + + { + std::vector partitions; + Topic topic(arn_, topic_); + std::vector
broker_addresses{Address(broker_host_, broker_port_)}; + ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); + Broker broker(broker_name_, broker_id_, service_address); + Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); + partitions.emplace_back(partition); + std::string debug_string; + topic_route_data_ = std::make_shared(partitions, debug_string); + } + } + + void TearDown() override { grpc_shutdown(); } + +protected: + std::string arn_{"arn:mq://test"}; + std::vector name_server_list_{"10.0.0.1:9876"}; + std::string group_{"Group-0"}; + std::string topic_{"Test"}; + std::shared_ptr> client_manager_; + std::shared_ptr pull_consumer_; + Scheduler scheduler_; + std::string broker_name_{"broker-a"}; + int broker_id_{0}; + std::string message_body_{"Message Body Content"}; + std::string broker_host_{"10.0.0.1"}; + int broker_port_{10911}; + int queue_id_{1}; + TopicRouteDataPtr topic_route_data_; +}; + +TEST_F(PullConsumerImplTest, testStartShutdown) { + pull_consumer_->start(); + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueuesFor) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + pull_consumer_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 5ec9131b18ab199db1ca76f330da6eb63dbc00ca Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 3 Aug 2021 22:59:58 +0800 Subject: [PATCH 034/133] Add more unit tests for PullConsumerImpl --- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 52a5e19e5..cfc78e4bb 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -1,9 +1,14 @@ #include "PullConsumerImpl.h" #include "ClientManagerFactory.h" #include "ClientManagerMock.h" +#include "InvocationContext.h" #include "Scheduler.h" +#include "rocketmq/AsyncCallback.h" +#include "rocketmq/ConsumeType.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" +#include +#include #include #include @@ -41,6 +46,7 @@ class PullConsumerImplTest : public testing::Test { std::vector name_server_list_{"10.0.0.1:9876"}; std::string group_{"Group-0"}; std::string topic_{"Test"}; + std::string tag_{"TagB"}; std::shared_ptr> client_manager_; std::shared_ptr pull_consumer_; Scheduler scheduler_; @@ -51,6 +57,7 @@ class PullConsumerImplTest : public testing::Test { int broker_port_{10911}; int queue_id_{1}; TopicRouteDataPtr topic_route_data_; + int batch_size_{32}; }; TEST_F(PullConsumerImplTest, testStartShutdown) { @@ -73,6 +80,179 @@ TEST_F(PullConsumerImplTest, testQueuesFor) { std::future> future = pull_consumer_->queuesFor(topic_); auto queues = future.get(); EXPECT_FALSE(queues.empty()); + + pull_consumer_->queuesFor(topic_); + + pull_consumer_->shutdown(); +} + +class TestPullCallback : public PullCallback { +public: + TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) {} + void onSuccess(const PullResult& pull_result) override { + success_ = true; + failure_ = false; + } + + void onException(const MQException& e) override { + failure_ = true; + success_ = false; + } + +private: + bool& success_; + bool& failure_; +}; + +TEST_F(PullConsumerImplTest, testPull) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + auto invocation_context = new InvocationContext(); + + auto messages = invocation_context->response.mutable_messages(); + + for (int i = 0; i < batch_size_; i++) { + auto message = new rmq::Message(); + message->set_body(message_body_); + std::string md5; + EXPECT_TRUE(MixAll::md5(message_body_, md5)); + message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); + message->mutable_system_attribute()->mutable_body_digest()->set_checksum(md5); + + message->mutable_topic()->set_arn(arn_); + message->mutable_topic()->set_name(topic_); + + message->mutable_system_attribute()->set_tag(tag_); + + messages->AddAllocated(message); + } + + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(invocation_context); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_TRUE(success); + EXPECT_FALSE(failure); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testPull_gRPC_error) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + auto invocation_context = new InvocationContext(); + invocation_context->status = grpc::Status::CANCELLED; + + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(invocation_context); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_TRUE(failure); + EXPECT_FALSE(success); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testPull_biz_error) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + auto invocation_context = new InvocationContext(); + invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::NOT_FOUND); + + auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, + const PullMessageRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + cb(invocation_context); + }; + + EXPECT_CALL(*client_manager_, pullMessage) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_pull_message)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + PullMessageQuery query; + query.message_queue = *queues.begin(); + query.offset = 0; + query.await_time = std::chrono::seconds(3); + + bool success = false; + bool failure = false; + auto pull_callback = new TestPullCallback(success, failure); + pull_consumer_->pull(query, pull_callback); + + EXPECT_FALSE(success); + EXPECT_TRUE(failure); + pull_consumer_->shutdown(); } From 72d4f0be4423e5107633c3869a4457ea5310ed92 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 3 Aug 2021 23:18:22 +0800 Subject: [PATCH 035/133] Add more tests for PullConsumerImpl --- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 1 - .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 115 ++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index 229309545..11f526451 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -76,7 +76,6 @@ std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { request.mutable_partition()->mutable_topic()->set_arn(arn_); request.mutable_partition()->set_id(query.message_queue.getQueueId()); - QueryOffsetResponse response; absl::flat_hash_map metadata; diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index cfc78e4bb..a5d562db9 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -256,4 +256,119 @@ TEST_F(PullConsumerImplTest, testPull_biz_error) { pull_consumer_->shutdown(); } +TEST_F(PullConsumerImplTest, testQueryOffset) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { cb(true, response); }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::BEGINNING; + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueryOffset_End) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { cb(true, response); }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::END; + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + +TEST_F(PullConsumerImplTest, testQueryOffset_Timepoint) { + pull_consumer_->start(); + auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, + const QueryRouteRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { + cb(true, topic_route_data_); + }; + + EXPECT_CALL(*client_manager_, resolveRoute) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_resolve_route)); + + std::future> future = pull_consumer_->queuesFor(topic_); + auto queues = future.get(); + EXPECT_FALSE(queues.empty()); + + QueryOffsetResponse response; + int64_t offset = 1; + response.set_offset(offset); + + auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, + const QueryOffsetRequest& request, std::chrono::milliseconds timeout, + const std::function& cb) { cb(true, response); }; + + EXPECT_CALL(*client_manager_, queryOffset) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Invoke(mock_query_offset)); + + OffsetQuery query; + query.policy = QueryOffsetPolicy::TIME_POINT; + query.time_point = std::chrono::system_clock::now(); + query.message_queue = *queues.begin(); + + std::future offset_future = pull_consumer_->queryOffset(query); + EXPECT_EQ(offset, offset_future.get()); + + pull_consumer_->shutdown(); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file From 4174095fb85c6ffcd5fedf6ff3cd9b9bf0979857 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Wed, 4 Aug 2021 00:22:13 +0800 Subject: [PATCH 036/133] Add unit test for ClientImpl (#11) Add unit test for ClientImp --- src/test/cpp/ut/rocketmq/BUILD.bazel | 13 +++ src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 89 +++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/test/cpp/ut/rocketmq/ClientImplTest.cpp diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 3d207acc3..94a8cd69a 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -197,4 +197,17 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ] +) + +cc_test( + name = "client_impl_test", + srcs = [ + "ClientImplTest.cpp", + ], + deps = [ + "//src/main/cpp/base/mocks:base_mocks", + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp new file mode 100644 index 000000000..7d0fe6565 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -0,0 +1,89 @@ +#include "ClientImpl.h" +#include "ClientManagerFactory.h" +#include "ClientManagerMock.h" +#include "HttpClientMock.h" +#include "Scheduler.h" +#include "TopAddressing.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class TestClientImpl : public ClientImpl, public std::enable_shared_from_this { +public: + TestClientImpl(std::string group) : ClientImpl(std::move(group)) {} + + std::shared_ptr self() override { return shared_from_this(); } + + void prepareHeartbeatData(HeartbeatRequest& request) override {} +}; + +class ClientImplTest : public testing::Test { +public: + void SetUp() override { + grpc_init(); + client_manager_ = std::make_shared>(); + ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); + client_ = std::make_shared(group_); + } + + void TearDown() override { grpc_shutdown(); } + +protected: + std::string arn_{"arn:mq://test"}; + std::string group_{"Group-0"}; + std::shared_ptr> client_manager_; + Scheduler scheduler_; + std::shared_ptr client_; +}; + +TEST_F(ClientImplTest, testBasic) { + + TopAddressing top_addressing_; + + auto http_client = absl::make_unique(); + + std::string once{"10.0.0.1:9876"}; + std::string then{"10.0.0.1:9876;10.0.0.2:9876"}; + absl::flat_hash_map header; + + bool completed = false; + absl::Mutex mtx; + absl::CondVar cv; + + int http_status = 200; + auto once_cb = + [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& + cb) { cb(http_status, header, once); }; + auto then_cb = + [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& + cb) { + cb(http_status, header, then); + absl::MutexLock lk(&mtx); + completed = true; + cv.SignalAll(); + }; + + EXPECT_CALL(*http_client, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); + top_addressing_.injectHttpClient(std::move(http_client)); + + ON_CALL(*client_manager_, topAddressing).WillByDefault(testing::ReturnRef(top_addressing_)); + client_->arn(arn_); + client_->start(); + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); + } + } + EXPECT_TRUE(completed); + + client_->shutdown(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 89278aefe6aeed139fe79c4ca5a4d9eb8346f52f Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 4 Aug 2021 14:52:34 +0800 Subject: [PATCH 037/133] BugFix: Fix ProcessQueue#filterExpression issue --- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 63 +++++++++---------- .../cpp/rocketmq/include/ProcessQueueImpl.h | 3 + 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index 6abadada3..3b7de7a0e 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -231,6 +231,31 @@ void ProcessQueueImpl::release(uint64_t body_size, int64_t offset) { } } +void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expression) { + assert(filter_expression); + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + auto&& filter_expression_table = consumer->getTopicFilterExpressionTable(); + if (filter_expression_table.contains(message_queue_.getTopic())) { + FilterExpression expression = filter_expression_table.at(message_queue_.getTopic()); + switch (expression.type_) { + case TAG: + filter_expression->set_type(rmq::FilterType::TAG); + filter_expression->set_expression(expression.content_); + break; + case SQL92: + filter_expression->set_type(rmq::FilterType::SQL); + filter_expression->set_expression(expression.content_); + break; + } + } else { + filter_expression->set_type(rmq::FilterType::TAG); + filter_expression->set_expression("*"); + } +} + void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_map& metadata, rmq::ReceiveMessageRequest& request) { std::shared_ptr consumer = consumer_.lock(); @@ -242,23 +267,7 @@ void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_mapmutable_topic()->set_name(message_queue_.getTopic()); request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); - auto search = consumer->getTopicFilterExpressionTable().find(message_queue_.getTopic()); - if (consumer->getTopicFilterExpressionTable().end() != search) { - FilterExpression expression = search->second; - switch (expression.type_) { - case TAG: - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - case SQL92: - request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - } - } else { - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression("*"); - } + wrapFilterExpression(request.mutable_filter_expression()); // Batch size request.set_batch_size(consumer->receiveBatchSize()); @@ -286,24 +295,8 @@ void ProcessQueueImpl::wrapPullMessageRequest(absl::flat_hash_mapmutable_topic()->set_arn(consumer->arn()); request.set_offset(next_offset_); request.set_batch_size(consumer->receiveBatchSize()); - auto filter_expression_table = consumer->getTopicFilterExpressionTable(); - auto search = filter_expression_table.find(message_queue_.getTopic()); - if (filter_expression_table.end() != search) { - FilterExpression expression = search->second; - switch (expression.type_) { - case TAG: - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - case SQL92: - request.mutable_filter_expression()->set_type(rmq::FilterType::SQL); - request.mutable_filter_expression()->set_expression(expression.content_); - break; - } - } else { - request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - request.mutable_filter_expression()->set_expression("*"); - } + + wrapFilterExpression(request.mutable_filter_expression()); } std::weak_ptr ProcessQueueImpl::getConsumer() { return consumer_; } diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index 094436788..ff6fac962 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -196,6 +197,8 @@ class ProcessQueueImpl : virtual public ProcessQueue { void wrapPullMessageRequest(absl::flat_hash_map& metadata, rmq::PullMessageRequest& request); + void wrapFilterExpression(rmq::FilterExpression* filter_expression); + FRIEND_TEST(ProcessQueueTest, testExpired); }; From bcc418eccc8cd90007d2982db08198ac5b283d78 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 4 Aug 2021 16:26:49 +0800 Subject: [PATCH 038/133] Improvement: use absl::optional where it makes sense --- src/main/cpp/rocketmq/BUILD.bazel | 1 + src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 6 +- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 37 +-- src/main/cpp/rocketmq/include/Consumer.h | 5 +- .../cpp/rocketmq/include/PushConsumerImpl.h | 6 +- .../cpp/rocketmq/mocks/include/ConsumerMock.h | 2 +- src/test/cpp/ut/rocketmq/BUILD.bazel | 12 - .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 229 ------------------ src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 10 +- 9 files changed, 34 insertions(+), 274 deletions(-) delete mode 100644 src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index 858d8262c..cd4a32423 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -11,5 +11,6 @@ cc_library( "//src/main/cpp/client:client_library", "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", + "@com_google_absl//absl/types:optional", ], ) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index 3b7de7a0e..dbce90cf4 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -237,9 +237,9 @@ void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expres if (!consumer) { return; } - auto&& filter_expression_table = consumer->getTopicFilterExpressionTable(); - if (filter_expression_table.contains(message_queue_.getTopic())) { - FilterExpression expression = filter_expression_table.at(message_queue_.getTopic()); + auto&& optional = consumer->getFilterExpression(message_queue_.getTopic()); + if (optional.has_value()) { + auto expression = optional.value(); switch (expression.type_) { case TAG: filter_expression->set_type(rmq::FilterType::TAG); diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index ad9443aa7..d54a98c5a 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -106,16 +106,15 @@ void PushConsumerImpl::unsubscribe(const std::string& topic) { topic_filter_expression_table_.erase(topic); } -absl::flat_hash_map PushConsumerImpl::getTopicFilterExpressionTable() const { - absl::flat_hash_map topic_filter_expression_table; +absl::optional PushConsumerImpl::getFilterExpression(const std::string& topic) const { { absl::MutexLock lock(&topic_filter_expression_table_mtx_); - for (auto item : topic_filter_expression_table_) { - topic_filter_expression_table.emplace(item.first, item.second); + if (topic_filter_expression_table_.contains(topic)) { + return absl::make_optional(topic_filter_expression_table_.at(topic)); + } else { + return absl::optional(); } } - - return topic_filter_expression_table; } void PushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { @@ -128,11 +127,12 @@ void PushConsumerImpl::scanAssignments() { SPDLOG_INFO("Client has stopped. Abort scanning immediately."); return; } - absl::flat_hash_map topic_filter_expression_table = getTopicFilterExpressionTable(); + { - for (auto& filter_entry : topic_filter_expression_table) { - std::string topic = filter_entry.first; - FilterExpression filter_expression = filter_entry.second; + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (auto& entry : topic_filter_expression_table_) { + std::string topic = entry.first; + const auto& filter_expression = entry.second; SPDLOG_DEBUG("Scan assignments for {}", topic); auto callback = [this, topic, filter_expression](const TopicAssignmentPtr& assignments) { if (assignments && !assignments->assignmentList().empty()) { @@ -538,14 +538,15 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { consumer_group->mutable_group()->set_arn(arn()); consumer_group->mutable_group()->set_name(group_name_); consumer_group->set_consume_model(rmq::ConsumeModel::CLUSTERING); + { - absl::flat_hash_map topic_filter_expression_table = getTopicFilterExpressionTable(); - for (auto& filter_entry : topic_filter_expression_table) { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (const auto& entry : topic_filter_expression_table_) { auto subscription = new rmq::SubscriptionEntry; subscription->mutable_topic()->set_arn(arn_); - subscription->mutable_topic()->set_name(filter_entry.first); - subscription->mutable_expression()->set_expression(filter_entry.second.content_); - switch (filter_entry.second.type_) { + subscription->mutable_topic()->set_name(entry.first); + subscription->mutable_expression()->set_expression(entry.second.content_); + switch (entry.second.type_) { case ExpressionType::TAG: subscription->mutable_expression()->set_type(rmq::FilterType::TAG); break; @@ -555,10 +556,10 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { } consumer_group->mutable_subscriptions()->AddAllocated(subscription); } - - // Add heartbeat - request.mutable_heartbeats()->AddAllocated(heartbeat); } + + // Add heartbeat + request.mutable_heartbeats()->AddAllocated(heartbeat); } ClientResourceBundle PushConsumerImpl::resourceBundle() { diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index 59b692511..0b86312eb 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -3,6 +3,7 @@ #include "Client.h" #include "FilterExpression.h" #include "absl/container/flat_hash_map.h" +#include "absl/types/optional.h" ROCKETMQ_NAMESPACE_BEGIN @@ -10,8 +11,8 @@ class Consumer : virtual public Client { public: ~Consumer() override = default; - virtual absl::flat_hash_map getTopicFilterExpressionTable() const = 0; - + virtual absl::optional getFilterExpression(const std::string& topic) const = 0; + virtual uint32_t maxCachedMessageQuantity() const = 0; virtual uint64_t maxCachedMessageMemory() const = 0; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index a4fb81959..6190c354d 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -34,7 +34,7 @@ class PushConsumerImpl : virtual public ClientImpl, ~PushConsumerImpl() override; - void prepareHeartbeatData(HeartbeatRequest& request) override; + void prepareHeartbeatData(HeartbeatRequest& request) override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); void start() override; @@ -46,14 +46,14 @@ class PushConsumerImpl : virtual public ClientImpl, void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); - absl::flat_hash_map getTopicFilterExpressionTable() const override + absl::optional getFilterExpression(const std::string& topic) const override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); void setConsumeFromWhere(ConsumeFromWhere consume_from_where); void registerMessageListener(MessageListener* message_listener); - void scanAssignments() LOCKS_EXCLUDED(process_queue_table_mtx_); + void scanAssignments() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); static bool selectBroker(const TopicRouteDataPtr& route, std::string& broker_host); diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h index 7f5b7c357..da9f4e700 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -7,7 +7,7 @@ ROCKETMQ_NAMESPACE_BEGIN class ConsumerMock : virtual public Consumer, virtual public ClientMock { public: - MOCK_METHOD((absl::flat_hash_map), getTopicFilterExpressionTable, (), (const override)); + MOCK_METHOD((absl::optional), getFilterExpression, (const std::string&), (const override)); MOCK_METHOD(uint32_t, maxCachedMessageQuantity, (), (const override)); diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 94a8cd69a..c70c146b7 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -71,18 +71,6 @@ cc_test( ], ) -cc_test( - name = "default_mq_push_consumer_test", - srcs = [ - "DefaultMQPushConsumerTest.cpp", - ], - deps = [ - ":client_interface", - "//src/main/cpp/admin:admin_server_library", - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) - cc_test( name = "ut_credentials_provider", srcs = [ diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp deleted file mode 100644 index 3c64730d6..000000000 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "rocketmq/DefaultMQPushConsumer.h" -#include "ClientManagerFactory.h" -#include "InvocationContext.h" -#include "MQClientTest.h" -#include "PushConsumerImpl.h" -#include "absl/time/time.h" -#include "rocketmq/MQMessageExt.h" -#include "spdlog/spdlog.h" -#include - -using namespace testing; - -ROCKETMQ_NAMESPACE_BEGIN - -std::mutex mtx_; -std::condition_variable cv; -int max_consumer_message_size = 50; -std::atomic_int message_count_(max_consumer_message_size); -std::atomic_int message_index(0); - -class DefaultMQPushConsumerUnitTest : public MQClientTest { -public: - DefaultMQPushConsumerUnitTest() : MQClientTest() {} - - void SetUp() override { - MQClientTest::SetUp(); - rpc_client_for_broker_ = std::make_shared>(); - - testing::Mock::AllowLeak(&rpc_client_for_broker_); - - ON_CALL(*rpc_client_for_broker_, needHeartbeat()).WillByDefault(testing::Return(true)); - - ON_CALL(*rpc_client_for_broker_, ok()).WillByDefault(Invoke([this]() { - std::cout << "Check ok()" << std::endl; - return client_ok_; - })); - - ON_CALL(*rpc_client_for_broker_, asyncQueryOffset(_, _)) - .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockQueryOffset, this, std::placeholders::_1, - std::placeholders::_2))); - - ON_CALL(*rpc_client_for_broker_, asyncPull(_, _)) - .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockAsyncPull, this, std::placeholders::_1, - std::placeholders::_2))); - - ON_CALL(*rpc_client_for_broker_, asyncHeartbeat(_, _)) - .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockHeartbeat, this, std::placeholders::_1, - std::placeholders::_2))); - - ON_CALL(*rpc_client_for_broker_, asyncQueryAssignment(testing::_, testing::_)) - .WillByDefault(Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockQueryAssignment, this, - std::placeholders::_1, std::placeholders::_2))); - - ON_CALL(*rpc_client_for_broker_, asyncReceive) - .WillByDefault(testing::Invoke(std::bind(&DefaultMQPushConsumerUnitTest::mockReceiveMessage, this, - std::placeholders::_1, std::placeholders::_2))); - - const char* address_format = "ipv4:10.0.0.{}:10911"; - for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { - std::string&& address = fmt::format(address_format, i); - client_instance_->addRpcClient(address, rpc_client_for_broker_); - } - } - - void TearDown() override { - rpc_client_for_broker_.reset(); - MQClientTest::TearDown(); - } - - void mockHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) const { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->onCompletion(true); - } - - void mockQueryOffset(const QueryOffsetRequest& request, - InvocationContext* invocation_context) const { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response.set_offset(max_offset_); - - invocation_context->onCompletion(true); - } - - void mockAsyncPull(const PullMessageRequest& request, - InvocationContext* invocation_context) const { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response.set_next_offset(max_offset_); - for (int i = 0; i < pull_batch_size_; i++) { - auto message = new rmq::Message; - message->mutable_topic()->set_arn(arn_); - message->mutable_topic()->set_name(topic_); - message->mutable_system_attribute()->set_message_id(std::to_string(++message_index_)); - message->set_body(body_); - message->mutable_system_attribute()->set_body_encoding(rmq::Encoding::IDENTITY); - message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); - std::string checksum; - MixAll::md5(body_, checksum); - message->mutable_system_attribute()->mutable_body_digest()->set_checksum(checksum); - invocation_context->response.mutable_messages()->AddAllocated(message); - } - invocation_context->onCompletion(true); - } - - void mockQueryAssignment(const QueryAssignmentRequest& request, - InvocationContext* invocation_context) { - for (int i = 0; i < 16; ++i) { - auto load_assignment = new rmq::Assignment; - load_assignment->set_mode(rmq::ConsumeMessageType::POP); - load_assignment->mutable_partition()->set_id(i % 8); - load_assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); - load_assignment->mutable_partition()->mutable_topic()->set_name(request.topic().name()); - load_assignment->mutable_partition()->mutable_topic()->set_arn(request.topic().arn()); - std::string broker_name{"broker-"}; - broker_name.push_back('a' + i / 8); - load_assignment->mutable_partition()->mutable_broker()->set_name(broker_name); - load_assignment->mutable_partition()->mutable_broker()->set_id(0); - auto endpoints = load_assignment->mutable_partition()->mutable_broker()->mutable_endpoints(); - endpoints->set_scheme(rmq::AddressScheme::IPv4); - auto addresses = endpoints->mutable_addresses(); - auto address = new rmq::Address; - address->set_host(fmt::format("10.0.0.{}", i / 8)); - address->set_port(10911); - addresses->AddAllocated(address); - invocation_context->response.mutable_assignments()->AddAllocated(load_assignment); - } - invocation_context->onCompletion(true); - } - - void mockReceiveMessage(const ReceiveMessageRequest& request, - InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); - - // Set delivery timestamp - auto since_unix_epoch = absl::Now() - absl::UnixEpoch(); - auto seconds = absl::ToInt64Seconds(since_unix_epoch); - invocation_context->response.mutable_delivery_timestamp()->set_seconds(seconds); - invocation_context->response.mutable_delivery_timestamp()->set_nanos( - absl::ToInt64Nanoseconds(since_unix_epoch - absl::Seconds(seconds))); - - // Set invisible duration - invocation_context->response.mutable_invisible_duration()->set_seconds(300); - invocation_context->response.mutable_invisible_duration()->set_nanos(0); - - for (int32_t i = 0; i < request.batch_size(); i++) { - auto message = new rmq::Message; - message->set_body("Sample body"); - } - - invocation_context->onCompletion(true); - } - - void - mockForwardMessageToDeadLetterQueue(const ForwardMessageToDeadLetterQueueRequest& request, - InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); - invocation_context->onCompletion(true); - } - -protected: - std::shared_ptr> rpc_client_for_broker_; - const int64_t max_offset_{100}; - std::string name_server_address_{"127.0.0.1:9876"}; - int32_t pull_batch_size_{32}; - mutable int64_t message_index_{0}; - std::string body_{"Sample message body"}; -}; - -std::atomic_bool completed_{false}; -std::mutex completion_mtx_; -std::condition_variable completion_cv_; - -class MessageListenerUnitTests : public StandardMessageListener { -public: - ConsumeMessageResult consumeMessage(const std::vector& msgs) override { - if (!msgs.empty()) { - std::unique_lock lk(completion_mtx_); - bool expected = false; - if (completed_.compare_exchange_strong(expected, true)) { - completion_cv_.notify_all(); - SPDLOG_INFO("Notify to stop unit test execution."); - } - } - for (const auto& msg : msgs) { - SPDLOG_INFO("Received a message[Topic={}, MessageId={}]", msg.getTopic(), msg.getMsgId()); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - return ConsumeMessageResult::SUCCESS; - } -}; - -TEST_F(DefaultMQPushConsumerUnitTest, testBroadcasting) { - spdlog::set_level(spdlog::level::debug); - auto push_consumer = std::make_shared(group_name_); - push_consumer->setMessageModel(MessageModel::BROADCASTING); - push_consumer->arn(arn_); - push_consumer->setNameServerList(name_server_list_); - push_consumer->subscribe(topic_, "*"); - auto listener = new MessageListenerUnitTests; - push_consumer->registerMessageListener(listener); - push_consumer->start(); - { - std::unique_lock lk(completion_mtx_); - completion_cv_.wait(lk, [&]() { return completed_.load(); }); - } - push_consumer->shutdown(); - absl::SleepFor(absl::Seconds(10)); - delete listener; -} - -TEST_F(DefaultMQPushConsumerUnitTest, DISABLED_testClustering) { - spdlog::set_level(spdlog::level::debug); - auto push_consumer = std::make_shared(group_name_); - push_consumer->setMessageModel(MessageModel::CLUSTERING); - push_consumer->arn(arn_); - push_consumer->setNameServerList(name_server_list_); - push_consumer->subscribe(topic_, "*"); - - auto listener = new MessageListenerUnitTests(); - push_consumer->registerMessageListener(listener); - push_consumer->start(); - - { - std::unique_lock lk(completion_mtx_); - completion_cv_.wait(lk, [&]() { return completed_.load(); }); - } - push_consumer->shutdown(); - delete listener; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 98e3d7427..b9fdc8137 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -277,10 +277,9 @@ TEST_F(ProcessQueueTest, testReceiveMessage_POP) { EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - absl::flat_hash_map filter_table; - filter_table.insert({topic_, filter_expression_}); + auto optional = absl::make_optional(filter_expression_); - EXPECT_CALL(*consumer_, getTopicFilterExpressionTable).WillRepeatedly(testing::Return(filter_table)); + EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, @@ -318,10 +317,9 @@ TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - absl::flat_hash_map filter_table; - filter_table.insert({topic_, filter_expression_}); + auto optional = absl::make_optional(filter_expression_); - EXPECT_CALL(*consumer_, getTopicFilterExpressionTable).WillRepeatedly(testing::Return(filter_table)); + EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); auto invocation_context = new InvocationContext(); From f0a8c6596291fd462db326b47a25d0abafb49f1f Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 4 Aug 2021 16:38:33 +0800 Subject: [PATCH 039/133] Remove unused files --- .gitlab-ci.yml | 16 --------- rpm/rocketmq-cpp.sh | 6 ---- rpm/rocketmq-cpp.spec | 75 ------------------------------------------- 3 files changed, 97 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100644 rpm/rocketmq-cpp.sh delete mode 100644 rpm/rocketmq-cpp.spec diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 006e24622..000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,16 +0,0 @@ -stages: - - cmake_build - - bazel_build - -before_script: - - export http_proxy=http://30.57.177.111:3128; export https_proxy=http://30.57.177.111:3128; export ALL_PROXY=30.57.177.111:3128; export PATH=/usr/local/gcc/bin:$PATH - -job1: - stage: cmake_build - script: - - mkdir _build; cd _build; cmake -DBUILD_TESTS=1 -DBUILD_INTEGRATION_TESTS=1 -DBUILD_EXAMPLES=1 ..; make -j $(nproc) - -job2: - stage: bazel_build - script: - - bazel build //... \ No newline at end of file diff --git a/rpm/rocketmq-cpp.sh b/rpm/rocketmq-cpp.sh deleted file mode 100644 index 41516e14b..000000000 --- a/rpm/rocketmq-cpp.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -export CC=/usr/local/gcc-4.9.2/bin/gcc -export CXX=/usr/local/gcc-4.9.2/bin/g++ -export LD_LIBRARY_PATH=/usr/local/gcc-4.9.2/lib64/ -export http_proxy=http://30.57.177.111:3128 -export https_proxy=http://30.57.177.111:3128 \ No newline at end of file diff --git a/rpm/rocketmq-cpp.spec b/rpm/rocketmq-cpp.spec deleted file mode 100644 index 5c4721042..000000000 --- a/rpm/rocketmq-cpp.spec +++ /dev/null @@ -1,75 +0,0 @@ -Name: rocketmq-cpp -Version: 1.0.0 -Release: %(echo $RELEASE)%{?dist} -Summary: Pop-based RocketMQ C++ SDK -Group: alibaba/application -License: Commercial -%define _prefix /home/admin/rocketmq-cpp - -# uncomment below, if your building depend on other packages. - -BuildRequires: metaq-cmake = 3.18.3 -BuildRequires: gcc49 = 4.9.2 - -# uncomment below, if depend on other packages - -#Requires: package_name = 1.0.0 - -%description -#descript your rpm package here -This SDK implements pop-based message consumption and acknowledgement. Aka, SDK leaves most of the complexities to -brokers to enjoy client simplicity and robustness. - -#%%debug_package -# support debuginfo package, to reduce runtime package size - -# prepare your files -%install -# OLDPWD is the dir of rpm_create running -# _prefix is an inner var of rpmbuild, -# can set by rpm_create, default is "/home/a" -# _lib is an inner var, maybe "lib" or "lib64" depend on OS - -export CC=/usr/local/gcc-4.9.2/bin/gcc -export CXX=/usr/local/gcc-4.9.2/bin/g++ -export http_proxy=http://30.57.177.111:3128 -export https_proxy=http://30.57.177.111:3128 -export ALL_PROXY=30.57.177.111:3128 -export LD_LIBRARY_PATH=/usr/local/gcc-4.9.2/lib64:${LD_LIBRARY_PATH} -# Build -cd $OLDPWD/../; /usr/local/bin/cmake -DCMAKE_INSTALL_PREFIX=${RPM_BUILD_ROOT}/%{_prefix} .; make -j $(nproc); make install - -# create a crontab of the package -#echo " -#* * * * * root /home/a/bin/every_min -#3 * * * * ads /home/a/bin/every_hour -#" > %{_crontab} - -# package infomation -%files -# set file attribute here -%defattr(-,root,root) -# need not list every file here, keep it as this -%{_prefix} -## create an empy dir - -# %dir %{_prefix}/var/log - -## need bakup old config file, so indicate here - -# %config %{_prefix}/etc/sample.conf - -## or need keep old config file, so indicate with "noreplace" - -# %config(noreplace) %{_prefix}/etc/sample.conf - -## indicate the dir for crontab - -#%attr(644,root,root) %{_crondir}/* - -%post -p /sbin/ldconfig -%postun -p /sbin/ldconfig - -%changelog -* Mon Aug 10 2020 Release version 1.0.0 -- add spec of %name From a7d92e2256a62df533f430aa1590ba3860bbee60 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 4 Aug 2021 16:40:59 +0800 Subject: [PATCH 040/133] Specify coverage html directory --- tools/coverage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/coverage.sh b/tools/coverage.sh index b036b6860..423bfef92 100755 --- a/tools/coverage.sh +++ b/tools/coverage.sh @@ -14,4 +14,4 @@ else bazel coverage --config=remote_cache //src/test/cpp/ut/... fi -genhtml bazel-out/_coverage/_coverage_report.dat \ No newline at end of file +genhtml bazel-out/_coverage/_coverage_report.dat --output-directory coverage \ No newline at end of file From f099c0e78d14d9b6a13e9cadfc678b2fa4839a6b Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 4 Aug 2021 16:56:28 +0800 Subject: [PATCH 041/133] Prefix unique ID with version --- src/main/cpp/base/UniqueIdGenerator.cpp | 9 ++++++--- src/main/cpp/base/include/UniqueIdGenerator.h | 1 + src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp index 9bb0f04af..9ce6c8a1f 100644 --- a/src/main/cpp/base/UniqueIdGenerator.cpp +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -8,6 +8,8 @@ ROCKETMQ_NAMESPACE_BEGIN +const uint8_t UniqueIdGenerator::VERSION = 0; + UniqueIdGenerator::UniqueIdGenerator() : prefix_(), since_custom_epoch_(std::chrono::system_clock::now() - customEpoch()), start_time_point_(std::chrono::steady_clock::now()), seconds_(deltaSeconds()), sequence_(0) { @@ -44,9 +46,10 @@ std::string UniqueIdGenerator::next() { slot.seconds = seconds_; slot.sequence = sequence_; } - std::array raw{}; - memcpy(raw.data(), prefix_.data(), prefix_.size()); - memcpy(raw.data() + prefix_.size(), &slot, sizeof(slot)); + std::array raw{}; + raw[0] = VERSION; + memcpy(raw.data() + sizeof(VERSION), prefix_.data(), prefix_.size()); + memcpy(raw.data() + sizeof(VERSION) + prefix_.size(), &slot, sizeof(slot)); return MixAll::hex(raw.data(), raw.size()); } diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h index e7b44fdfb..e72cb7e99 100644 --- a/src/main/cpp/base/include/UniqueIdGenerator.h +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -59,6 +59,7 @@ class UniqueIdGenerator { uint32_t seconds_; uint32_t sequence_; + static const uint8_t VERSION; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp index 227e1c815..449d655b3 100644 --- a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -4,8 +4,13 @@ #include "spdlog/spdlog.h" #include "gtest/gtest.h" +#include ROCKETMQ_NAMESPACE_BEGIN +TEST(UniqueIdGeneratorTest, testOutputSampleId) { +std::cout << "A sample unique ID: " << UniqueIdGenerator::instance().next() << std::endl; +} + TEST(UniqueIdGeneratorTest, testNext) { absl::flat_hash_set id_set; uint32_t total = 500000; From 6c8ade3a2ee0ad2c4f92d6a86e33bbfda8a1800a Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 12 Aug 2021 10:12:49 +0800 Subject: [PATCH 042/133] Add unit tests for log module (#12) Add unit tests for log module --- .bazelrc | 1 + api/rocketmq/Logger.h | 52 +++++------ src/main/cpp/client/ClientManagerImpl.cpp | 11 ++- src/main/cpp/log/BUILD.bazel | 8 +- src/main/cpp/log/Logger.cpp | 100 --------------------- src/main/cpp/log/LoggerImpl.cpp | 94 +++++++++++++++++++ src/main/cpp/log/include/LoggerImpl.h | 78 ++++++++++++++++ src/main/cpp/rocketmq/PushConsumerImpl.cpp | 2 +- src/test/cpp/it/RpcClientTest.cpp | 8 +- src/test/cpp/ut/logger/LoggerTest.cpp | 88 +++++++++++++++--- 10 files changed, 287 insertions(+), 155 deletions(-) delete mode 100644 src/main/cpp/log/Logger.cpp create mode 100644 src/main/cpp/log/LoggerImpl.cpp diff --git a/.bazelrc b/.bazelrc index 7ed0205e8..d0aaed6c7 100644 --- a/.bazelrc +++ b/.bazelrc @@ -31,6 +31,7 @@ build:linux --strip=never # We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace. build --define absl=1 +build --define SPDLOG_FMT_EXTERNAL=1 # Pass PATH, CC, CXX and LLVM_CONFIG variables from the environment. build --action_env=CC diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h index b9fa8aec6..24b0b3484 100644 --- a/api/rocketmq/Logger.h +++ b/api/rocketmq/Logger.h @@ -1,54 +1,46 @@ #pragma once +#include +#include #include #include #include - +#include #include "RocketMQ.h" #ifndef SPDLOG_ACTIVE_LEVEL -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE #endif ROCKETMQ_NAMESPACE_BEGIN -enum Level { Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4 }; +enum class Level : uint8_t { Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4 }; class Logger { public: - Logger(); - - ~Logger(); - - void setLevel(Level level); - - void setFileSize(int32_t file_size) { - file_size_ = file_size; - } - - void setFileCount(int32_t file_count) { - file_count_ = file_count; - } + virtual ~Logger() = default; - void setPattern(std::string pattern) { - pattern_ = std::move(pattern); - } + /** + * @brief Set log level for file sink. Its default log level is: Info. + * + * @param level + */ + virtual void setLevel(Level level) = 0; - void init(); + /** + * @brief Set log level for stdout, aka, console. Its default log level is: Warn. + * + * @param level + */ + virtual void setConsoleLevel(Level level) = 0; -private: - Level level_; - std::string log_home_; - int32_t file_size_; - int32_t file_count_; - std::string pattern_; + virtual void setFileSize(std::size_t file_size) = 0; - std::once_flag init_once_; + virtual void setFileCount(std::size_t file_count) = 0; - void init0(); + virtual void setPattern(std::string pattern) = 0; - static const char* DEFAULT_PATTERN; - static const char* USER_HOME_ENV; + virtual void init() = 0; }; Logger& getLogger(); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 20c52d404..68b710f55 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -727,8 +727,11 @@ void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_conte for (auto& item : response.messages()) { MQMessageExt message_ext; if (wrapMessage(item, message_ext)) { - MessageAccessor::setTargetEndpoint(message_ext, target_host); msg_found_list.emplace_back(message_ext); + MessageAccessor::setTargetEndpoint(message_ext, target_host); + } else { + + // TODO: NACK } } } @@ -792,6 +795,9 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess const auto& system_attributes = item.system_attribute(); + // Receipt-handle + MessageAccessor::setReceiptHandle(message_ext, system_attributes.receipt_handle()); + // Tag message_ext.setTags(system_attributes.tag()); @@ -950,9 +956,6 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess break; } - // Receipt-handle - MessageAccessor::setReceiptHandle(message_ext, system_attributes.receipt_handle()); - // Partition-id MessageAccessor::setQueueId(message_ext, system_attributes.partition_id()); diff --git a/src/main/cpp/log/BUILD.bazel b/src/main/cpp/log/BUILD.bazel index b0d79b491..6a6208d77 100644 --- a/src/main/cpp/log/BUILD.bazel +++ b/src/main/cpp/log/BUILD.bazel @@ -1,12 +1,8 @@ load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "log_library", - hdrs = [ - "include/LoggerImpl.h", - ], - srcs = [ - "Logger.cpp", - ], + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), strip_include_prefix = "//src/main/cpp/log/include", deps = [ "//api:rocketmq_interface", diff --git a/src/main/cpp/log/Logger.cpp b/src/main/cpp/log/Logger.cpp deleted file mode 100644 index cdd317d0e..000000000 --- a/src/main/cpp/log/Logger.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "rocketmq/Logger.h" -#include -#include - -#include "spdlog/sinks/rotating_file_sink.h" -#include "spdlog/sinks/stdout_color_sinks.h" -#include "spdlog/spdlog.h" - -// Check C++17 is used -#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) -#if __has_include() -#define GHC_USE_STD_FS -#include -namespace fs = std::filesystem; -#endif -#endif - -#ifndef GHC_USE_STD_FS -#include "ghc/filesystem.hpp" -namespace fs = ghc::filesystem; -#endif - -ROCKETMQ_NAMESPACE_BEGIN - -Logger::Logger() : level_(Level::Info), file_size_(1048576 * 256), file_count_(16) {} - -Logger::~Logger() {} - -void Logger::setLevel(Level level) { level_ = level; } - -void Logger::init() { - std::call_once(init_once_, [this] { init0(); }); -} - -void Logger::init0() { - if (log_home_.empty()) { - char* home = getenv(USER_HOME_ENV); - log_home_.append(home).append("/logs/rocketmq/client.log"); - } - - // Create directories if necessary - fs::path log_file(log_home_); - fs::path log_dir = log_file.parent_path(); - if (!fs::exists(log_dir)) { - if (!fs::create_directories(log_dir)) { - std::cerr << "Failed to mkdir " << log_dir << std::endl; - abort(); - } - } - - if (pattern_.empty()) { - pattern_ = DEFAULT_PATTERN; - } - - auto console_sink = std::make_shared(); - console_sink->set_level(spdlog::level::info); - console_sink->set_pattern(pattern_); - auto file_sink = std::make_shared(log_home_, file_size_, file_count_); - file_sink->set_pattern(pattern_); - - switch (level_) { - case Trace: - file_sink->set_level(spdlog::level::trace); - break; - case Debug: - file_sink->set_level(spdlog::level::debug); - break; - case Info: - file_sink->set_level(spdlog::level::info); - break; - case Warn: - file_sink->set_level(spdlog::level::warn); - break; - case Error: - file_sink->set_level(spdlog::level::err); - break; - default: - file_sink->set_level(spdlog::level::critical); - break; - } - - // std::make_shared does not with initializer_list. - auto default_logger = - std::make_shared("rocketmq_logger", spdlog::sinks_init_list{console_sink, file_sink}); - - default_logger->set_level(spdlog::level::debug); - spdlog::flush_on(spdlog::level::info); - spdlog::flush_every(std::chrono::seconds(1)); - spdlog::set_default_logger(default_logger); -} - -Logger& getLogger() { - static Logger logger; - return logger; -} - -const char* Logger::USER_HOME_ENV = "HOME"; -const char* Logger::DEFAULT_PATTERN = "[%Y/%m/%d-%H:%M:%S.%e %z] [%n] [%^---%L---%$] [thread %t] %v %@"; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp new file mode 100644 index 000000000..0afa7e97f --- /dev/null +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -0,0 +1,94 @@ +#include "LoggerImpl.h" +#include +#include + +// Check C++17 is used +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif + +#ifndef GHC_USE_STD_FS +#include "ghc/filesystem.hpp" +namespace fs = ghc::filesystem; +#endif + +ROCKETMQ_NAMESPACE_BEGIN + +const char* LoggerImpl::LOG_FILE = "/logs/rocketmq/client.log"; +const char* LoggerImpl::LOGGER_NAME = "rocketmq_logger"; + +void LoggerImpl::setLevel(Level level) { + level_ = level; + auto logger = spdlog::get(LOGGER_NAME); + if (logger) { + setLevel(logger, level_); + } +} + +void LoggerImpl::setConsoleLevel(Level level) { + console_level_ = level; + setLevel(console_sink_, console_level_); +} + +void LoggerImpl::init() { + std::call_once(init_once_, [this] { init0(); }); +} + +void LoggerImpl::init0() { + if (log_home_.empty()) { + char* home = getenv(USER_HOME_ENV); + if (home) { + log_home_.append(home); + } else { + log_home_.append(fs::temp_directory_path().string()); + } + log_home_.append(LOG_FILE); + } + + // Create directories if necessary + fs::path log_file(log_home_); + fs::path log_dir = log_file.parent_path(); + if (!fs::exists(log_dir)) { + if (!fs::create_directories(log_dir)) { + std::cerr << "Failed to mkdir " << log_dir << std::endl; + abort(); + } + } + std::cout << "RocketMQ log files path: " << log_dir.c_str() << std::endl; + + if (pattern_.empty()) { + pattern_ = DEFAULT_PATTERN; + } + + console_sink_ = std::make_shared(); + setLevel(console_sink_, console_level_); + console_sink_->set_pattern(pattern_); + + file_sink_ = std::make_shared(log_home_, file_size_, file_count_, true); + file_sink_->set_pattern(pattern_); + setLevel(file_sink_, level_); + + // std::make_shared does not with initializer_list. + auto default_logger = + std::make_shared(LOGGER_NAME, spdlog::sinks_init_list{console_sink_, file_sink_}); + default_logger->flush_on(spdlog::level::warn); + default_logger->set_level(spdlog::level::trace); + spdlog::flush_every(std::chrono::seconds(1)); + spdlog::set_default_logger(default_logger); +} + +Logger& getLogger() { + static LoggerImpl logger; + return logger; +} + +const std::size_t LoggerImpl::DEFAULT_MAX_LOG_FILE_QUANTITY = 16; +const std::size_t LoggerImpl::DEFAULT_FILE_SIZE = 1048576 * 256; +const char* LoggerImpl::USER_HOME_ENV = "HOME"; +const char* LoggerImpl::DEFAULT_PATTERN = "[%Y/%m/%d-%H:%M:%S.%e %z] [%n] [%^---%L---%$] [thread %t] %v %@"; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/log/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h index a695bd923..f2deb3316 100644 --- a/src/main/cpp/log/include/LoggerImpl.h +++ b/src/main/cpp/log/include/LoggerImpl.h @@ -1,3 +1,81 @@ #pragma once #include "rocketmq/Logger.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class LoggerImpl : public Logger { + +public: + ~LoggerImpl() override = default; + + void setLevel(Level level) override; + + void setConsoleLevel(Level level) override; + + void setFileSize(std::size_t file_size) override { file_size_ = file_size; } + + void setFileCount(std::size_t file_count) override { file_count_ = file_count; } + + void setPattern(std::string pattern) override { + pattern_ = std::move(pattern); + auto logger = spdlog::get(LOGGER_NAME); + if (logger) { + logger->set_pattern(pattern_); + } + } + + void init() override; + + static const char* LOGGER_NAME; + +private: + void init0(); + + template void setLevel(std::shared_ptr& target, Level level) { + if (!target) { + return; + } + + switch (level) { + case Level::Trace: + target->set_level(spdlog::level::trace); + break; + case Level::Debug: + target->set_level(spdlog::level::debug); + break; + case Level::Info: + target->set_level(spdlog::level::info); + break; + case Level::Warn: + target->set_level(spdlog::level::warn); + break; + case Level::Error: + target->set_level(spdlog::level::err); + break; + } + } + + static const char* LOG_FILE; + + Level level_{Level::Info}; + Level console_level_{Level::Warn}; + std::string log_home_; + std::size_t file_size_{DEFAULT_FILE_SIZE}; + std::size_t file_count_{DEFAULT_MAX_LOG_FILE_QUANTITY}; + std::string pattern_; + + std::once_flag init_once_; + + std::shared_ptr file_sink_; + std::shared_ptr console_sink_; + + static const std::size_t DEFAULT_MAX_LOG_FILE_QUANTITY; + static const std::size_t DEFAULT_FILE_SIZE; + static const char* DEFAULT_PATTERN; + static const char* USER_HOME_ENV; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index d54a98c5a..75d4433fc 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -211,7 +211,7 @@ void PushConsumerImpl::queryAssignment(const std::string& topic, if (ok) { SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, group_name_, response.assignments().size()); - SPDLOG_TRACE("Query assignment response for {} is: {}", topic, ptr->debugString()); + SPDLOG_TRACE("Query assignment response for {} is: {}", topic, response.DebugString()); cb(std::make_shared(response)); } else { SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 7af5975ee..aa2f90c07 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -6,6 +6,8 @@ #include "Signature.h" #include "TlsHelper.h" #include "UtilAll.h" +#include "UniqueIdGenerator.h" +#include "rocketmq/Logger.h" #include "absl/container/flat_hash_set.h" #include "apache/rocketmq/v1/service.pb.h" #include "rocketmq/CredentialsProvider.h" @@ -43,7 +45,7 @@ class RpcClientTest : public ::testing::Test { } void SetUp() override { - + getLogger().setLevel(Level::Debug); client_config_.setCredentialsProvider(std::make_shared()); client_config_.arn(arn_); client_config_.region(region_id_); @@ -121,9 +123,8 @@ class RpcClientTest : public ::testing::Test { props["key"] = "value"; props["Jack"] = "Bauer"; send_message_request.mutable_message()->mutable_user_attribute()->insert(props.begin(), props.end()); - const char* msgId = "1EE10C841BF3D007000011480C6ACF07"; auto system_attribute = send_message_request.mutable_message()->mutable_system_attribute(); - system_attribute->set_message_id(msgId); + system_attribute->set_message_id(message_id_); system_attribute->set_message_type(rmq::MessageType::NORMAL); system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); system_attribute->set_born_host(UtilAll::hostname()); @@ -178,6 +179,7 @@ class RpcClientTest : public ::testing::Test { std::shared_ptr server_authorization_check_config_; std::shared_ptr channel_credential_; grpc::ChannelArguments channel_arguments_; + std::string message_id_{UniqueIdGenerator::instance().next()}; }; TEST_F(RpcClientTest, testRouteInfo) { diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp index aadbdac45..e48b7cb15 100644 --- a/src/test/cpp/ut/logger/LoggerTest.cpp +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -1,26 +1,92 @@ #include "rocketmq/Logger.h" +#include "LoggerImpl.h" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN -class LoggerTest : public testing::Test {}; +class LoggerTest : public testing::Test { +protected: + std::size_t log_file_size_{1024 * 4}; +}; -TEST_F(LoggerTest, testDefaultLogger) { +TEST_F(LoggerTest, testLogger_Trace) { Logger& logger = getLogger(); - logger.setFileSize(1024); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Trace); + logger.setConsoleLevel(Level::Trace); + logger.init(); + SPDLOG_ERROR("=========Trace==========="); + SPDLOG_TRACE("Should show up!"); + SPDLOG_DEBUG("Should show up!"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Debug) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Debug); + logger.init(); + SPDLOG_ERROR("=========Debug==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should show up"); + SPDLOG_INFO("Should show up!!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testLogger_Info) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); logger.setLevel(Level::Info); + logger.init(); + SPDLOG_ERROR("=========Info==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} -// TODO: Figure out constraints of file system when building with bazel -#ifndef BAZEL +TEST_F(LoggerTest, testLogger_Warn) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Warn); logger.init(); -#endif + SPDLOG_ERROR("=========Warn==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should NOT get logged"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); +} - for (int i = 0; i < 32; ++i) { - SPDLOG_DEBUG("Should not appear by default"); - SPDLOG_INFO("Should show up!!"); - SPDLOG_WARN("Warn message is here"); - SPDLOG_ERROR("This is an error message"); +TEST_F(LoggerTest, testLogger_Error) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Error); + logger.init(); + SPDLOG_ERROR("=========Error==========="); + SPDLOG_TRACE("Should NOT get logged"); + SPDLOG_DEBUG("Should NOT get logged"); + SPDLOG_INFO("Should NOT get logged"); + SPDLOG_WARN("Should NOT get logged"); + SPDLOG_ERROR("Should show up!"); +} + +TEST_F(LoggerTest, testFileRolling) { + Logger& logger = getLogger(); + logger.setFileSize(log_file_size_); + logger.setLevel(Level::Trace); + logger.init(); + for (size_t i = 0; i < 32; i++) { + SPDLOG_TRACE("Should show up!"); + SPDLOG_DEBUG("Should show up!"); + SPDLOG_INFO("Should show up!"); + SPDLOG_WARN("Should show up!"); + SPDLOG_ERROR("Should show up!"); } } From 77fdd16d54e0f3d42ffb5472e48b7e0fd1d10ad8 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 18 Aug 2021 17:14:36 +0800 Subject: [PATCH 043/133] Update proto files --- proto/apache/rocketmq/v1/definition.proto | 4 +-- proto/apache/rocketmq/v1/service.proto | 31 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index 04af66f1d..2655dfb17 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -257,8 +257,8 @@ message SystemAttribute { // Trace context. string trace_context = 21; - // Correlated transaction - string transaction_id = 22; + // Delay time of first resolve orphaned transaction request from server. + google.protobuf.Duration orphaned_transaction_recovery_period = 22; } message Message { diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 13351e1d7..5d90ca8e7 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -30,6 +30,9 @@ message ResponseCommon { message QueryRouteRequest { Resource topic = 1; + + // Service access point + Endpoints endpoints = 2; } message QueryRouteResponse { @@ -52,6 +55,9 @@ message QueryAssignmentRequest { Resource topic = 1; Resource group = 2; string client_id = 3; + + // Service access point + Endpoints endpoints = 4; } message QueryAssignmentResponse { @@ -190,6 +196,16 @@ message PullMessageResponse { repeated Message messages = 5; } +message UpdateOffsetRequest { + Resource group = 1; + Partition partition = 2; + int64 partition_offset = 3; +} + +message UpdateOffsetResponse { + ResponseCommon common = 1; +} + message ClientResourceBundle { string client_id = 1; repeated Resource topics = 2; @@ -208,20 +224,24 @@ message GenericPollingResponse { } message PrintThreadStackRequest { + string mid = 1; } message PrintThreadStackResponse { ResponseCommon common = 1; - string stack_trace = 2; + string mid = 2; + string stack_trace = 3; } message VerifyMessageConsumptionRequest { - Partition Partition = 1; - Message message = 2; + string mid = 1; + Partition Partition = 2; + Message message = 3; } message VerifyMessageConsumptionResponse { - ResponseCommon common = 1; + string mid = 1; + ResponseCommon common = 2; } message ResolveOrphanedTransactionRequest { @@ -283,6 +303,9 @@ service MessagingService { rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { } + rpc UpdateOffset(UpdateOffsetRequest) returns (UpdateOffsetResponse) { + } + rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) { } } \ No newline at end of file From eded8ea3c17b28c169e0ad6c9d91a2add1683694 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 18 Aug 2021 19:29:09 +0800 Subject: [PATCH 044/133] Fix CMakeLists.txt --- src/main/cpp/log/CMakeLists.txt | 4 +++- src/test/cpp/ut/rocketmq/CMakeLists.txt | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt index af3122fd7..e5f8f0462 100644 --- a/src/main/cpp/log/CMakeLists.txt +++ b/src/main/cpp/log/CMakeLists.txt @@ -1,5 +1,7 @@ add_library(logger_obj OBJECT) -target_sources(logger_obj PRIVATE Logger.cpp) +target_sources(logger_obj + PRIVATE + LoggerImpl.cpp) target_link_libraries(logger_obj PUBLIC rocketmq_interface diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt index 8b1fa443f..5c0c44cab 100644 --- a/src/test/cpp/ut/rocketmq/CMakeLists.txt +++ b/src/test/cpp/ut/rocketmq/CMakeLists.txt @@ -14,11 +14,6 @@ target_link_libraries(default_mq_producer_test rocketmq_library) add_test(ut_default_mq_producer_test default_mq_producer_test) -add_executable(default_mq_push_consumer_test DefaultMQPushConsumerTest.cpp) -target_link_libraries(default_mq_push_consumer_test - rocketmq_library - client_test_interface) - add_executable(executor_test ExecutorTest.cpp) target_link_libraries(executor_test PRIVATE From 22ce81ade874ca0ff053157d46b274045ae91892 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Wed, 18 Aug 2021 22:19:30 +0800 Subject: [PATCH 045/133] Fix CMakeLists.txt and bazel 3rd party deps (#13) * Fix CMakeLists.txt and bazel 3rd party deps --- CMakeLists.txt | 11 +- src/main/cpp/CMakeLists.txt | 2 - src/main/cpp/base/BUILD.bazel | 1 + src/main/cpp/base/CMakeLists.txt | 4 +- src/main/cpp/base/MixAll.cpp | 9 + src/main/cpp/base/include/MixAll.h | 2 + src/main/cpp/client/BUILD.bazel | 1 - src/main/cpp/client/CMakeLists.txt | 3 - src/main/cpp/client/ClientManagerImpl.cpp | 27 +- src/main/cpp/concurrent/CMakeLists.txt | 6 +- src/main/cpp/log/CMakeLists.txt | 3 + src/main/cpp/rocketmq/CMakeLists.txt | 9 +- src/test/cpp/it/CMakeLists.txt | 32 +- src/test/cpp/ut/base/MixAllTest.cpp | 8 + third_party/CMakeLists.txt | 3 - third_party/ThreadPool.BUILD | 8 - third_party/crc/BUILD.bazel | 9 - third_party/crc/CMakeLists.txt | 4 - third_party/crc/include/CRC.h | 2066 ----- third_party/date/CMakeLists.txt | 4 - third_party/date/include/date/chrono_io.h | 34 - third_party/date/include/date/date.h | 8200 ------------------ third_party/date/include/date/ios.h | 50 - third_party/date/include/date/islamic.h | 3031 ------- third_party/date/include/date/iso_week.h | 1751 ---- third_party/date/include/date/julian.h | 3052 ------- third_party/date/include/date/ptz.h | 861 -- third_party/date/include/date/solar_hijri.h | 3151 ------- third_party/date/include/date/tz.h | 2792 ------ third_party/date/include/date/tz_private.h | 316 - third_party/thread_pool/CMakeLists.txt | 2 - third_party/thread_pool/include/ThreadPool.h | 92 - 32 files changed, 69 insertions(+), 25475 deletions(-) delete mode 100644 third_party/CMakeLists.txt delete mode 100644 third_party/ThreadPool.BUILD delete mode 100644 third_party/crc/BUILD.bazel delete mode 100644 third_party/crc/CMakeLists.txt delete mode 100644 third_party/crc/include/CRC.h delete mode 100644 third_party/date/CMakeLists.txt delete mode 100644 third_party/date/include/date/chrono_io.h delete mode 100644 third_party/date/include/date/date.h delete mode 100644 third_party/date/include/date/ios.h delete mode 100644 third_party/date/include/date/islamic.h delete mode 100644 third_party/date/include/date/iso_week.h delete mode 100644 third_party/date/include/date/julian.h delete mode 100644 third_party/date/include/date/ptz.h delete mode 100644 third_party/date/include/date/solar_hijri.h delete mode 100644 third_party/date/include/date/tz.h delete mode 100644 third_party/date/include/date/tz_private.h delete mode 100644 third_party/thread_pool/CMakeLists.txt delete mode 100644 third_party/thread_pool/include/ThreadPool.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e79e0ff07..8e18cd5b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,8 @@ FetchContent_Declare( FetchContent_Declare( opentelemetry - GIT_REPOSITORY https://github.com/aaron-ai/opentelemetry-cpp.git - GIT_TAG v0.3.0-lingchu-fix-01 + GIT_REPOSITORY https://github.com/open-telemetry/opentelemetry-cpp.git + GIT_TAG v1.0.0-rc4 ) set(FETCHCONTENT_QUIET OFF) @@ -60,16 +60,13 @@ if (BUILD_TESTS) FetchContent_MakeAvailable(googletest) endif () -FetchContent_MakeAvailable(fmt gRPC spdlog ghc) +FetchContent_MakeAvailable(fmt gRPC spdlog ghc opentelemetry) option(ENABLE_TRACING OFF) if (ENABLE_TRACING) FetchContent_MakeAvailable(opentelemetry) endif () -find_package(CURL REQUIRED) -message("CURL ${CURL_LIBRARIES}") - # Since FetchContent uses add_subdirectory under the hood, we can use # the grpc targets directly from this build. set(_PROTOBUF_LIBPROTOBUF libprotobuf) @@ -127,8 +124,6 @@ target_link_libraries(mq_proto_library enable_testing() -add_subdirectory(third_party) - option(ENABLE_TLS ON) if (ENABLE_TLS) message("TLS enabled") diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt index eef284298..df43e76b8 100644 --- a/src/main/cpp/CMakeLists.txt +++ b/src/main/cpp/CMakeLists.txt @@ -22,7 +22,6 @@ target_link_libraries(rocketmq_static absl::random_random absl::synchronization absl::strings - CURL::libcurl fmt mq_proto_library re2::re2 @@ -52,7 +51,6 @@ target_link_libraries(rocketmq_shared absl::random_random absl::synchronization absl::strings - CURL::libcurl fmt mq_proto_library re2::re2 diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index e8d97809b..a5a3fdd52 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -63,5 +63,6 @@ cc_library( "@zlib//:zlib", "@boringssl//:crypto", "@boringssl//:ssl", + "//external:madler_zlib", ], ) \ No newline at end of file diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt index 3c4504c04..6a1f7495b 100644 --- a/src/main/cpp/base/CMakeLists.txt +++ b/src/main/cpp/base/CMakeLists.txt @@ -24,7 +24,6 @@ target_link_libraries(base_obj absl::synchronization absl::strings absl::time - CURL::libcurl crypto fmt grpc++ @@ -36,7 +35,7 @@ target_link_libraries(base_obj add_library(base_library STATIC - $ include/MessageAccessor.h) + $) target_include_directories(base_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -47,7 +46,6 @@ target_link_libraries(base_library absl::random_random absl::synchronization absl::strings - CURL::libcurl fmt grpc++ logger_library diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 524a0b0b5..7d0316165 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -12,6 +12,8 @@ #include #include +#include "zlib.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -70,6 +72,13 @@ uint32_t MixAll::random(uint32_t left, uint32_t right) { return absl::Uniform(gen, left, right); } +bool MixAll::crc32(const std::string& data, std::string& digest) { + uLong crc = ::crc32(0L, reinterpret_cast(data.c_str()), data.length()); + uint32_t network_byte_order = htonl(crc); + digest = hex(&network_byte_order, sizeof(network_byte_order)); + return true; +} + bool MixAll::md5(const std::string& data, std::string& digest) { MD5_CTX ctx; MD5_Init(&ctx); diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 36d348bd1..1b0029f7a 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -57,6 +57,8 @@ class MixAll { static uint32_t random(uint32_t left, uint32_t right); + static bool crc32(const std::string& data, std::string& digest); + static bool md5(const std::string& data, std::string& digest); static bool sha1(const std::string& data, std::string& digest); diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index 909cfb12d..ab5eae81e 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -20,7 +20,6 @@ cc_library( "@com_github_opentelemetry//api", "@boringssl//:ssl", "//external:gtest", - "//third_party/crc:crc_interface", ], defines = [ ], diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt index 923818dc4..79037efdc 100644 --- a/src/main/cpp/client/CMakeLists.txt +++ b/src/main/cpp/client/CMakeLists.txt @@ -21,7 +21,6 @@ target_link_libraries(client_obj admin_library crc_library crypto - date::date scheduler_library ssl PRIVATE @@ -47,8 +46,6 @@ target_link_libraries(client_library admin_library crc_library crypto - CURL::libcurl - date::date mq_proto_library scheduler_library ssl) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 68b710f55..c7d4b103e 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1,11 +1,5 @@ #include "ClientManagerImpl.h" -#include -#include -#include -#include - -#include "CRC.h" #include "InvocationContext.h" #include "LogInterceptor.h" #include "LogInterceptorFactory.h" @@ -21,6 +15,10 @@ #include "grpcpp/create_channel.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQMessageExt.h" +#include +#include +#include +#include #ifdef ENABLE_TRACING #include "TracingUtility.h" @@ -730,7 +728,7 @@ void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_conte msg_found_list.emplace_back(message_ext); MessageAccessor::setTargetEndpoint(message_ext, target_host); } else { - + // TODO: NACK } } @@ -821,9 +819,18 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess } else { switch (digest.type()) { case rmq::DigestType::CRC32: { - std::uint32_t crc = CRC::Calculate(item.body().data(), item.body().length(), CRC::CRC_32()); - SPDLOG_DEBUG("Compute and compare CRC32 checksum. Actual: {}, expect: {}", crc, digest.checksum()); - body_digest_match = (std::stoul(digest.checksum()) == crc); + std::string checksum; + bool success = MixAll::crc32(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("Message body CRC32 checksum validation passed."); + } else { + SPDLOG_WARN("Body CRC32 checksum validation failed. Actual: {}, expect: {}", checksum, digest.checksum()); + } + } else { + SPDLOG_WARN("Failed to calculate CRC32 checksum. Skip."); + } break; } case rmq::DigestType::MD5: { diff --git a/src/main/cpp/concurrent/CMakeLists.txt b/src/main/cpp/concurrent/CMakeLists.txt index f3394e0a6..d2c11baaf 100644 --- a/src/main/cpp/concurrent/CMakeLists.txt +++ b/src/main/cpp/concurrent/CMakeLists.txt @@ -1,7 +1,8 @@ add_library(countdown_latch_obj OBJECT) target_sources(countdown_latch_obj PRIVATE CountdownLatch.cpp) target_include_directories(countdown_latch_obj - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(countdown_latch_obj PUBLIC absl::strings PUBLIC absl::base @@ -9,5 +10,4 @@ target_link_libraries(countdown_latch_obj PUBLIC base_library PRIVATE spdlog::spdlog_header_only) -add_library(countdown_latch_library STATIC) -target_link_libraries(countdown_latch_library PUBLIC countdown_latch_obj) \ No newline at end of file +add_library(countdown_latch_library STATIC $) \ No newline at end of file diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt index e5f8f0462..95351ceaa 100644 --- a/src/main/cpp/log/CMakeLists.txt +++ b/src/main/cpp/log/CMakeLists.txt @@ -7,6 +7,9 @@ target_link_libraries(logger_obj rocketmq_interface ghc_filesystem spdlog::spdlog_header_only) +target_include_directories(logger_obj + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) add_library(logger_library STATIC diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt index 13e578007..e451fa1b1 100644 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ b/src/main/cpp/rocketmq/CMakeLists.txt @@ -10,14 +10,14 @@ target_sources(rocketmq_obj ConsumeFifoMessageService.cpp CredentialsProvider.cpp DefaultMQProducer.cpp - ProducerImpl.cpp + ProducerImpl.cpp DefaultMQPushConsumer.cpp - PushConsumerImpl.cpp + PushConsumerImpl.cpp DefaultMQPullConsumer.cpp - PullConsumerImpl.cpp + PullConsumerImpl.cpp FilterExpression.cpp MessageGroupQueueSelector.cpp - ProcessQueueImpl.cpp + ProcessQueueImpl.cpp TransactionImpl.cpp) target_include_directories(rocketmq_obj PUBLIC @@ -37,7 +37,6 @@ target_include_directories(rocketmq_library target_link_libraries(rocketmq_library PUBLIC client_library - CURL::libcurl logger_library) add_subdirectory(mocks) \ No newline at end of file diff --git a/src/test/cpp/it/CMakeLists.txt b/src/test/cpp/it/CMakeLists.txt index 51df5ba4b..cd534ac23 100644 --- a/src/test/cpp/it/CMakeLists.txt +++ b/src/test/cpp/it/CMakeLists.txt @@ -1,29 +1,32 @@ add_executable(client_instance_test ClientInstanceTest.cpp) target_link_libraries(client_instance_test - PRIVATE client_library - PRIVATE gtest_main) + PRIVATE + client_library + gtest_main) add_test(it_client_instance client_instance_test) add_executable(rpc_client_integration_test RpcClientIntegrationTest.cpp) target_link_libraries(rpc_client_integration_test - client_library - rpc_client_mock - gtest_main) + PRIVATE + client_library + rpc_client_mock + gtest_main) add_test(it_rpc_client rpc_client_integration_test) add_executable(rpc_client_test RpcClientTest.cpp) target_link_libraries(rpc_client_test - client_library - CURL::libcurl - credentials_observable_mock - rpc_client_mock - gtest_main) + PRIVATE + client_library + credentials_observable_mock + rpc_client_mock + gtest_main) add_test(it_rpc_client_2 rpc_client_test) add_executable(top_addressing_test TopAddressingTest.cpp) target_link_libraries(top_addressing_test - base_library - gtest_main) + PRIVATE + base_library + gtest_main) add_test(it_top_addressing top_addressing_test) @@ -37,6 +40,7 @@ add_test(it_topic_publish_info topic_publish_info_test) add_executable(default_push_consumer_test DefaultMQPushConsumerIntegrationTest.cpp) target_link_libraries(default_push_consumer_test - rocketmq_library - gtest_main) + PRIVATE + rocketmq_library + gtest_main) add_test(it_default_push_consumer default_push_consumer_test) \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index 33f16fff5..95f5d8416 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -86,4 +86,12 @@ TEST_F(MixAllTest, testSHA1) { EXPECT_EQ(digest, toUpperCase(expect)); } +TEST_F(MixAllTest, testCrc32) { + std::string data("abc"); + std::string digest; + bool success = MixAll::crc32(data, digest); + EXPECT_TRUE(success); + std::cout << digest << std::endl; +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt deleted file mode 100644 index d5704891e..000000000 --- a/third_party/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(date) -add_subdirectory(thread_pool) -add_subdirectory(crc) \ No newline at end of file diff --git a/third_party/ThreadPool.BUILD b/third_party/ThreadPool.BUILD deleted file mode 100644 index 451d193f0..000000000 --- a/third_party/ThreadPool.BUILD +++ /dev/null @@ -1,8 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") -cc_library( - name = "ThreadPool", - hdrs = [ - "ThreadPool.h", - ], - visibility = ["//visibility:public"], -) \ No newline at end of file diff --git a/third_party/crc/BUILD.bazel b/third_party/crc/BUILD.bazel deleted file mode 100644 index 4eba5cb86..000000000 --- a/third_party/crc/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") -cc_library( - name = "crc_interface", - hdrs = [ - "include/CRC.h" - ], - strip_include_prefix = "//third_party/crc/include", - visibility = ["//visibility:public"], -) \ No newline at end of file diff --git a/third_party/crc/CMakeLists.txt b/third_party/crc/CMakeLists.txt deleted file mode 100644 index 3f97aebd4..000000000 --- a/third_party/crc/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(crc_library INTERFACE) -target_include_directories(crc_library - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/third_party/crc/include/CRC.h b/third_party/crc/include/CRC.h deleted file mode 100644 index e20db354a..000000000 --- a/third_party/crc/include/CRC.h +++ /dev/null @@ -1,2066 +0,0 @@ -/** - @file CRC.h - @author Daniel Bahr - @version 1.1.0.0 - @copyright - @parblock - CRC++ - Copyright (c) 2021, Daniel Bahr - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of CRC++ nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - @endparblock -*/ - -/* - CRC++ can be configured by setting various #defines before #including this header file: - - #define crcpp_uint8 - Specifies the type used to store CRCs that have a width of 8 bits or less. - This type is not used in CRC calculations. Defaults to ::std::uint8_t. - #define crcpp_uint16 - Specifies the type used to store CRCs that have a width between 9 and 16 bits (inclusive). - This type is not used in CRC calculations. Defaults to ::std::uint16_t. - #define crcpp_uint32 - Specifies the type used to store CRCs that have a width between 17 and 32 bits (inclusive). - This type is not used in CRC calculations. Defaults to ::std::uint32_t. - #define crcpp_uint64 - Specifies the type used to store CRCs that have a width between 33 and 64 bits (inclusive). - This type is not used in CRC calculations. Defaults to ::std::uint64_t. - #define crcpp_size - This type is used for loop iteration and function signatures only. Defaults to ::std::size_t. - #define CRCPP_USE_NAMESPACE - Define to place all CRC++ code within the ::CRCPP namespace. - #define CRCPP_BRANCHLESS - Define to enable a branchless CRC implementation. The branchless implementation uses a single integer - multiplication in the bit-by-bit calculation instead of a small conditional. The branchless implementation - may be faster on processor architectures which support single-instruction integer multiplication. - #define CRCPP_USE_CPP11 - Define to enables C++11 features (move semantics, constexpr, static_assert, etc.). - #define CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - Define to include definitions for little-used CRCs. -*/ - -#ifndef CRCPP_CRC_H_ -#define CRCPP_CRC_H_ - -#include // Includes CHAR_BIT -#ifdef CRCPP_USE_CPP11 -#include // Includes ::std::size_t -#include // Includes ::std::uint8_t, ::std::uint16_t, ::std::uint32_t, ::std::uint64_t -#else -#include // Includes size_t -#include // Includes uint8_t, uint16_t, uint32_t, uint64_t -#endif -#include // Includes ::std::numeric_limits -#include // Includes ::std::move - -#ifndef crcpp_uint8 -# ifdef CRCPP_USE_CPP11 - /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint8 ::std::uint8_t -# else - /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint8 uint8_t -# endif -#endif - -#ifndef crcpp_uint16 -# ifdef CRCPP_USE_CPP11 - /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint16 ::std::uint16_t -# else - /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint16 uint16_t -# endif -#endif - -#ifndef crcpp_uint32 -# ifdef CRCPP_USE_CPP11 - /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint32 ::std::uint32_t -# else - /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint32 uint32_t -# endif -#endif - -#ifndef crcpp_uint64 -# ifdef CRCPP_USE_CPP11 - /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint64 ::std::uint64_t -# else - /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. -# define crcpp_uint64 uint64_t -# endif -#endif - -#ifndef crcpp_size -# ifdef CRCPP_USE_CPP11 - /// @brief Unsigned size definition, used for specifying data sizes. -# define crcpp_size ::std::size_t -# else - /// @brief Unsigned size definition, used for specifying data sizes. -# define crcpp_size size_t -# endif -#endif - -#ifdef CRCPP_USE_CPP11 - /// @brief Compile-time expression definition. -# define crcpp_constexpr constexpr -#else - /// @brief Compile-time expression definition. -# define crcpp_constexpr const -#endif - -#ifdef CRCPP_USE_NAMESPACE -namespace CRCPP -{ -#endif - -/** - @brief Static class for computing CRCs. - @note This class supports computation of full and multi-part CRCs, using a bit-by-bit algorithm or a - byte-by-byte lookup table. The CRCs are calculated using as many optimizations as is reasonable. - If compiling with C++11, the constexpr keyword is used liberally so that many calculations are - performed at compile-time instead of at runtime. -*/ -class CRC -{ -public: - // Forward declaration - template - struct Table; - - /** - @brief CRC parameters. - */ - template - struct Parameters - { - CRCType polynomial; ///< CRC polynomial - CRCType initialValue; ///< Initial CRC value - CRCType finalXOR; ///< Value to XOR with the final CRC - bool reflectInput; ///< true to reflect all input bytes - bool reflectOutput; ///< true to reflect the output CRC (reflection occurs before the final XOR) - - Table MakeTable() const; - }; - - /** - @brief CRC lookup table. After construction, the CRC parameters are fixed. - @note A CRC table can be used for multiple CRC calculations. - */ - template - struct Table - { - // Constructors are intentionally NOT marked explicit. - Table(const Parameters & parameters); - -#ifdef CRCPP_USE_CPP11 - Table(Parameters && parameters); -#endif - - const Parameters & GetParameters() const; - - const CRCType * GetTable() const; - - CRCType operator[](unsigned char index) const; - - private: - void InitTable(); - - Parameters parameters; ///< CRC parameters used to construct the table - CRCType table[1 << CHAR_BIT]; ///< CRC lookup table - }; - - // The number of bits in CRCType must be at least as large as CRCWidth. - // CRCType must be an unsigned integer type or a custom type with operator overloads. - template - static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters); - - template - static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); - - template - static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable); - - template - static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); - - template - static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters); - - template - static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); - - template - static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable); - - template - static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); - - // Common CRCs up to 64 bits. - // Note: Check values are the computed CRCs when given an ASCII input of "123456789" (without null terminator) -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters< crcpp_uint8, 4> & CRC_4_ITU(); - static const Parameters< crcpp_uint8, 5> & CRC_5_EPC(); - static const Parameters< crcpp_uint8, 5> & CRC_5_ITU(); - static const Parameters< crcpp_uint8, 5> & CRC_5_USB(); - static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000A(); - static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000B(); - static const Parameters< crcpp_uint8, 6> & CRC_6_ITU(); - static const Parameters< crcpp_uint8, 6> & CRC_6_NR(); - static const Parameters< crcpp_uint8, 7> & CRC_7(); -#endif - static const Parameters< crcpp_uint8, 8> & CRC_8(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters< crcpp_uint8, 8> & CRC_8_EBU(); - static const Parameters< crcpp_uint8, 8> & CRC_8_MAXIM(); - static const Parameters< crcpp_uint8, 8> & CRC_8_WCDMA(); - static const Parameters< crcpp_uint8, 8> & CRC_8_LTE(); - static const Parameters & CRC_10(); - static const Parameters & CRC_10_CDMA2000(); - static const Parameters & CRC_11(); - static const Parameters & CRC_11_NR(); - static const Parameters & CRC_12_CDMA2000(); - static const Parameters & CRC_12_DECT(); - static const Parameters & CRC_12_UMTS(); - static const Parameters & CRC_13_BBC(); - static const Parameters & CRC_15(); - static const Parameters & CRC_15_MPT1327(); -#endif - static const Parameters & CRC_16_ARC(); - static const Parameters & CRC_16_BUYPASS(); - static const Parameters & CRC_16_CCITTFALSE(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters & CRC_16_CDMA2000(); - static const Parameters & CRC_16_CMS(); - static const Parameters & CRC_16_DECTR(); - static const Parameters & CRC_16_DECTX(); - static const Parameters & CRC_16_DNP(); -#endif - static const Parameters & CRC_16_GENIBUS(); - static const Parameters & CRC_16_KERMIT(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters & CRC_16_MAXIM(); - static const Parameters & CRC_16_MODBUS(); - static const Parameters & CRC_16_T10DIF(); - static const Parameters & CRC_16_USB(); -#endif - static const Parameters & CRC_16_X25(); - static const Parameters & CRC_16_XMODEM(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters & CRC_17_CAN(); - static const Parameters & CRC_21_CAN(); - static const Parameters & CRC_24(); - static const Parameters & CRC_24_FLEXRAYA(); - static const Parameters & CRC_24_FLEXRAYB(); - static const Parameters & CRC_24_LTEA(); - static const Parameters & CRC_24_LTEB(); - static const Parameters & CRC_24_NRC(); - static const Parameters & CRC_30(); -#endif - static const Parameters & CRC_32(); - static const Parameters & CRC_32_BZIP2(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters & CRC_32_C(); -#endif - static const Parameters & CRC_32_MPEG2(); - static const Parameters & CRC_32_POSIX(); -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - static const Parameters & CRC_32_Q(); - static const Parameters & CRC_40_GSM(); - static const Parameters & CRC_64(); -#endif - -#ifdef CRCPP_USE_CPP11 - CRC() = delete; - CRC(const CRC & other) = delete; - CRC & operator=(const CRC & other) = delete; - CRC(CRC && other) = delete; - CRC & operator=(CRC && other) = delete; -#endif - -private: -#ifndef CRCPP_USE_CPP11 - CRC(); - CRC(const CRC & other); - CRC & operator=(const CRC & other); -#endif - - template - static IntegerType Reflect(IntegerType value, crcpp_uint16 numBits); - - template - static CRCType Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); - - template - static CRCType UndoFinalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); - - template - static CRCType CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder); - - template - static CRCType CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder); - - template - static CRCType CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder); -}; - -/** - @brief Returns a CRC lookup table construct using these CRC parameters. - @note This function primarily exists to allow use of the auto keyword instead of instantiating - a table directly, since template parameters are not inferred in constructors. - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC lookup table -*/ -template -inline CRC::Table CRC::Parameters::MakeTable() const -{ - // This should take advantage of RVO and optimize out the copy. - return CRC::Table(*this); -} - -/** - @brief Constructs a CRC table from a set of CRC parameters - @param[in] params CRC parameters - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC -*/ -template -inline CRC::Table::Table(const Parameters & params) : - parameters(params) -{ - InitTable(); -} - -#ifdef CRCPP_USE_CPP11 -/** - @brief Constructs a CRC table from a set of CRC parameters - @param[in] params CRC parameters - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC -*/ -template -inline CRC::Table::Table(Parameters && params) : - parameters(::std::move(params)) -{ - InitTable(); -} -#endif - -/** - @brief Gets the CRC parameters used to construct the CRC table - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC parameters -*/ -template -inline const CRC::Parameters & CRC::Table::GetParameters() const -{ - return parameters; -} - -/** - @brief Gets the CRC table - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC table -*/ -template -inline const CRCType * CRC::Table::GetTable() const -{ - return table; -} - -/** - @brief Gets an entry in the CRC table - @param[in] index Index into the CRC table - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC table entry -*/ -template -inline CRCType CRC::Table::operator[](unsigned char index) const -{ - return table[index]; -} - -/** - @brief Initializes a CRC table. - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC -*/ -template -inline void CRC::Table::InitTable() -{ - // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) - static crcpp_constexpr CRCType BIT_MASK((CRCType(1) << (CRCWidth - CRCType(1))) | - ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1))); - - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); - - CRCType crc; - unsigned char byte = 0; - - // Loop over each dividend (each possible number storable in an unsigned char) - do - { - crc = CRC::CalculateRemainder(&byte, sizeof(byte), parameters, CRCType(0)); - - // This mask might not be necessary; all unit tests pass with this line commented out, - // but that might just be a coincidence based on the CRC parameters used for testing. - // In any case, this is harmless to leave in and only adds a single machine instruction per loop iteration. - crc &= BIT_MASK; - - if (!parameters.reflectInput && CRCWidth < CHAR_BIT) - { - // Undo the special operation at the end of the CalculateRemainder() - // function for non-reflected CRCs < CHAR_BIT. - crc = static_cast(crc << SHIFT); - } - - table[byte] = crc; - } - while (++byte); -} - -/** - @brief Computes a CRC. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bytes - @param[in] parameters CRC parameters - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters) -{ - CRCType remainder = CalculateRemainder(data, size, parameters, parameters.initialValue); - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} -/** - @brief Appends additional data to a previous CRC calculation. - @note This function can be used to compute multi-part CRCs. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bytes - @param[in] parameters CRC parameters - @param[in] crc CRC from a previous calculation - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) -{ - CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); - - remainder = CalculateRemainder(data, size, parameters, remainder); - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Computes a CRC via a lookup table. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bytes - @param[in] lookupTable CRC lookup table - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable) -{ - const Parameters & parameters = lookupTable.GetParameters(); - - CRCType remainder = CalculateRemainder(data, size, lookupTable, parameters.initialValue); - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Appends additional data to a previous CRC calculation using a lookup table. - @note This function can be used to compute multi-part CRCs. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bytes - @param[in] lookupTable CRC lookup table - @param[in] crc CRC from a previous calculation - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) -{ - const Parameters & parameters = lookupTable.GetParameters(); - - CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); - - remainder = CalculateRemainder(data, size, lookupTable, remainder); - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Computes a CRC. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bits - @param[in] parameters CRC parameters - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters) -{ - CRCType remainder = parameters.initialValue; - - // Calculate the remainder on a whole number of bytes first, then call - // a special-case function for the remaining bits. - crcpp_size wholeNumberOfBytes = size / CHAR_BIT; - if (wholeNumberOfBytes > 0) - { - remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, remainder); - } - - crcpp_size remainingNumberOfBits = size % CHAR_BIT; - if (remainingNumberOfBits != 0) - { - unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); - remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); - } - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} -/** - @brief Appends additional data to a previous CRC calculation. - @note This function can be used to compute multi-part CRCs. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bits - @param[in] parameters CRC parameters - @param[in] crc CRC from a previous calculation - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) -{ - CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); - - // Calculate the remainder on a whole number of bytes first, then call - // a special-case function for the remaining bits. - crcpp_size wholeNumberOfBytes = size / CHAR_BIT; - if (wholeNumberOfBytes > 0) - { - remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, parameters.initialValue); - } - - crcpp_size remainingNumberOfBits = size % CHAR_BIT; - if (remainingNumberOfBits != 0) - { - unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); - remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); - } - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Computes a CRC via a lookup table. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bits - @param[in] lookupTable CRC lookup table - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable) -{ - const Parameters & parameters = lookupTable.GetParameters(); - - CRCType remainder = parameters.initialValue; - - // Calculate the remainder on a whole number of bytes first, then call - // a special-case function for the remaining bits. - crcpp_size wholeNumberOfBytes = size / CHAR_BIT; - if (wholeNumberOfBytes > 0) - { - remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, remainder); - } - - crcpp_size remainingNumberOfBits = size % CHAR_BIT; - if (remainingNumberOfBits != 0) - { - unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); - remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); - } - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Appends additional data to a previous CRC calculation using a lookup table. - @note This function can be used to compute multi-part CRCs. - @param[in] data Data over which CRC will be computed - @param[in] size Size of the data, in bits - @param[in] lookupTable CRC lookup table - @param[in] crc CRC from a previous calculation - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC -*/ -template -inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) -{ - const Parameters & parameters = lookupTable.GetParameters(); - - CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); - - // Calculate the remainder on a whole number of bytes first, then call - // a special-case function for the remaining bits. - crcpp_size wholeNumberOfBytes = size / CHAR_BIT; - if (wholeNumberOfBytes > 0) - { - remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, parameters.initialValue); - } - - crcpp_size remainingNumberOfBits = size % CHAR_BIT; - if (remainingNumberOfBits > 0) - { - unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); - remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); - } - - // No need to mask the remainder here; the mask will be applied in the Finalize() function. - - return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); -} - -/** - @brief Reflects (i.e. reverses the bits within) an integer value. - @param[in] value Value to reflect - @param[in] numBits Number of bits in the integer which will be reflected - @tparam IntegerType Integer type of the value being reflected - @return Reflected value -*/ -template -inline IntegerType CRC::Reflect(IntegerType value, crcpp_uint16 numBits) -{ - IntegerType reversedValue(0); - - for (crcpp_uint16 i = 0; i < numBits; ++i) - { - reversedValue = static_cast((reversedValue << 1) | (value & 1)); - value = static_cast(value >> 1); - } - - return reversedValue; -} - -/** - @brief Computes the final reflection and XOR of a CRC remainder. - @param[in] remainder CRC remainder to reflect and XOR - @param[in] finalXOR Final value to XOR with the remainder - @param[in] reflectOutput true to reflect each byte of the remainder before the XOR - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return Final CRC -*/ -template -inline CRCType CRC::Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput) -{ - // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) - static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | - ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); - - if (reflectOutput) - { - remainder = Reflect(remainder, CRCWidth); - } - - return (remainder ^ finalXOR) & BIT_MASK; -} - -/** - @brief Undoes the process of computing the final reflection and XOR of a CRC remainder. - @note This function allows for computation of multi-part CRCs - @note Calling UndoFinalize() followed by Finalize() (or vice versa) will always return the original remainder value: - - CRCType x = ...; - CRCType y = Finalize(x, finalXOR, reflectOutput); - CRCType z = UndoFinalize(y, finalXOR, reflectOutput); - assert(x == z); - - @param[in] crc Reflected and XORed CRC - @param[in] finalXOR Final value XORed with the remainder - @param[in] reflectOutput true if the remainder is to be reflected - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return Un-finalized CRC remainder -*/ -template -inline CRCType CRC::UndoFinalize(CRCType crc, CRCType finalXOR, bool reflectOutput) -{ - // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) - static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | - ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); - - crc = (crc & BIT_MASK) ^ finalXOR; - - if (reflectOutput) - { - crc = Reflect(crc, CRCWidth); - } - - return crc; -} - -/** - @brief Computes a CRC remainder. - @param[in] data Data over which the remainder will be computed - @param[in] size Size of the data, in bytes - @param[in] parameters CRC parameters - @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC remainder -*/ -template -inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder) -{ -#ifdef CRCPP_USE_CPP11 - // This static_assert is put here because this function will always be compiled in no matter what - // the template parameters are and whether or not a table lookup or bit-by-bit algorithm is used. - static_assert(::std::numeric_limits::digits >= CRCWidth, "CRCType is too small to contain a CRC of width CRCWidth."); -#else - // Catching this compile-time error is very important. Sadly, the compiler error will be very cryptic, but it's - // better than nothing. - enum { static_assert_failed_CRCType_is_too_small_to_contain_a_CRC_of_width_CRCWidth = 1 / (::std::numeric_limits::digits >= CRCWidth ? 1 : 0) }; -#endif - - const unsigned char * current = reinterpret_cast(data); - - // Slightly different implementations based on the parameters. The current implementations try to eliminate as much - // computation from the inner loop (looping over each bit) as possible. - if (parameters.reflectInput) - { - CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); - while (size--) - { - remainder = static_cast(remainder ^ *current++); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < CHAR_BIT; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & 1) - // remainder = (remainder >> 1) ^ polynomial; - // else - // remainder >>= 1; - remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); -#else - remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); -#endif - } - } - } - else if (CRCWidth >= CHAR_BIT) - { - static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); -#ifndef CRCPP_BRANCHLESS - static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); -#endif - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); - - while (size--) - { - remainder = static_cast(remainder ^ (static_cast(*current++) << SHIFT)); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < CHAR_BIT; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & CRC_HIGHEST_BIT_MASK) - // remainder = (remainder << 1) ^ parameters.polynomial; - // else - // remainder <<= 1; - remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); -#else - remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); -#endif - } - } - } - else - { - static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); -#ifndef CRCPP_BRANCHLESS - static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); -#endif - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); - - CRCType polynomial = static_cast(parameters.polynomial << SHIFT); - remainder = static_cast(remainder << SHIFT); - - while (size--) - { - remainder = static_cast(remainder ^ *current++); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < CHAR_BIT; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) - // remainder = (remainder << 1) ^ polynomial; - // else - // remainder <<= 1; - remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); -#else - remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); -#endif - } - } - - remainder = static_cast(remainder >> SHIFT); - } - - return remainder; -} - -/** - @brief Computes a CRC remainder using lookup table. - @param[in] data Data over which the remainder will be computed - @param[in] size Size of the data, in bytes - @param[in] lookupTable CRC lookup table - @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. - @tparam CRCType Integer type for storing the CRC result - @tparam CRCWidth Number of bits in the CRC - @return CRC remainder -*/ -template -inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder) -{ - const unsigned char * current = reinterpret_cast(data); - - if (lookupTable.GetParameters().reflectInput) - { - while (size--) - { -#if defined(WIN32) || defined(_WIN32) || defined(WINCE) - // Disable warning about data loss when doing (remainder >> CHAR_BIT) when - // remainder is one byte long. The algorithm is still correct in this case, - // though it's possible that one additional machine instruction will be executed. -# pragma warning (push) -# pragma warning (disable : 4333) -#endif - remainder = static_cast((remainder >> CHAR_BIT) ^ lookupTable[static_cast(remainder ^ *current++)]); -#if defined(WIN32) || defined(_WIN32) || defined(WINCE) -# pragma warning (pop) -#endif - } - } - else if (CRCWidth >= CHAR_BIT) - { - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); - - while (size--) - { - remainder = static_cast((remainder << CHAR_BIT) ^ lookupTable[static_cast((remainder >> SHIFT) ^ *current++)]); - } - } - else - { - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); - - remainder = static_cast(remainder << SHIFT); - - while (size--) - { - // Note: no need to mask here since remainder is guaranteed to fit in a single byte. - remainder = lookupTable[static_cast(remainder ^ *current++)]; - } - - remainder = static_cast(remainder >> SHIFT); - } - - return remainder; -} - -template -inline CRCType CRC::CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder) -{ - // Slightly different implementations based on the parameters. The current implementations try to eliminate as much - // computation from the inner loop (looping over each bit) as possible. - if (parameters.reflectInput) - { - CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); - remainder = static_cast(remainder ^ byte); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < numBits; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & 1) - // remainder = (remainder >> 1) ^ polynomial; - // else - // remainder >>= 1; - remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); -#else - remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); -#endif - } - } - else if (CRCWidth >= CHAR_BIT) - { - static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); -#ifndef CRCPP_BRANCHLESS - static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); -#endif - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); - - remainder = static_cast(remainder ^ (static_cast(byte) << SHIFT)); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < numBits; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & CRC_HIGHEST_BIT_MASK) - // remainder = (remainder << 1) ^ parameters.polynomial; - // else - // remainder <<= 1; - remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); -#else - remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); -#endif - } - } - else - { - static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); -#ifndef CRCPP_BRANCHLESS - static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); -#endif - // The conditional expression is used to avoid a -Wshift-count-overflow warning. - static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); - - CRCType polynomial = static_cast(parameters.polynomial << SHIFT); - remainder = static_cast((remainder << SHIFT) ^ byte); - - // An optimizing compiler might choose to unroll this loop. - for (crcpp_size i = 0; i < numBits; ++i) - { -#ifdef CRCPP_BRANCHLESS - // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: - // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) - // remainder = (remainder << 1) ^ polynomial; - // else - // remainder <<= 1; - remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); -#else - remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); -#endif - } - - remainder = static_cast(remainder >> SHIFT); - } - - return remainder; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-4 ITU. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-4 ITU has the following parameters and check value: - - polynomial = 0x3 - - initial value = 0x0 - - final XOR = 0x0 - - reflect input = true - - reflect output = true - - check value = 0x7 - @return CRC-4 ITU parameters -*/ -inline const CRC::Parameters & CRC::CRC_4_ITU() -{ - static const Parameters parameters = { 0x3, 0x0, 0x0, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-5 EPC. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-5 EPC has the following parameters and check value: - - polynomial = 0x09 - - initial value = 0x09 - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0x00 - @return CRC-5 EPC parameters -*/ -inline const CRC::Parameters & CRC::CRC_5_EPC() -{ - static const Parameters parameters = { 0x09, 0x09, 0x00, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-5 ITU. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-5 ITU has the following parameters and check value: - - polynomial = 0x15 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = true - - reflect output = true - - check value = 0x07 - @return CRC-5 ITU parameters -*/ -inline const CRC::Parameters & CRC::CRC_5_ITU() -{ - static const Parameters parameters = { 0x15, 0x00, 0x00, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-5 USB. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-5 USB has the following parameters and check value: - - polynomial = 0x05 - - initial value = 0x1F - - final XOR = 0x1F - - reflect input = true - - reflect output = true - - check value = 0x19 - @return CRC-5 USB parameters -*/ -inline const CRC::Parameters & CRC::CRC_5_USB() -{ - static const Parameters parameters = { 0x05, 0x1F, 0x1F, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-6 CDMA2000-A. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-6 CDMA2000-A has the following parameters and check value: - - polynomial = 0x27 - - initial value = 0x3F - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0x0D - @return CRC-6 CDMA2000-A parameters -*/ -inline const CRC::Parameters & CRC::CRC_6_CDMA2000A() -{ - static const Parameters parameters = { 0x27, 0x3F, 0x00, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-6 CDMA2000-B. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-6 CDMA2000-A has the following parameters and check value: - - polynomial = 0x07 - - initial value = 0x3F - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0x3B - @return CRC-6 CDMA2000-B parameters -*/ -inline const CRC::Parameters & CRC::CRC_6_CDMA2000B() -{ - static const Parameters parameters = { 0x07, 0x3F, 0x00, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-6 ITU. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-6 ITU has the following parameters and check value: - - polynomial = 0x03 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = true - - reflect output = true - - check value = 0x06 - @return CRC-6 ITU parameters -*/ -inline const CRC::Parameters & CRC::CRC_6_ITU() -{ - static const Parameters parameters = { 0x03, 0x00, 0x00, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-6 NR. - @note The parameters are static and are delayed-constructed to reduce memory - footprint. - @note CRC-6 NR has the following parameters and check value: - - polynomial = 0x21 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0x15 - @return CRC-6 NR parameters -*/ -inline const CRC::Parameters & CRC::CRC_6_NR() -{ - static const Parameters parameters = { 0x21, 0x00, 0x00, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-7 JEDEC. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-7 JEDEC has the following parameters and check value: - - polynomial = 0x09 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0x75 - @return CRC-7 JEDEC parameters -*/ -inline const CRC::Parameters & CRC::CRC_7() -{ - static const Parameters parameters = { 0x09, 0x00, 0x00, false, false }; - return parameters; -} -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -/** - @brief Returns a set of parameters for CRC-8 SMBus. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-8 SMBus has the following parameters and check value: - - polynomial = 0x07 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0xF4 - @return CRC-8 SMBus parameters -*/ -inline const CRC::Parameters & CRC::CRC_8() -{ - static const Parameters parameters = { 0x07, 0x00, 0x00, false, false }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-8 EBU (aka CRC-8 AES). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-8 EBU has the following parameters and check value: - - polynomial = 0x1D - - initial value = 0xFF - - final XOR = 0x00 - - reflect input = true - - reflect output = true - - check value = 0x97 - @return CRC-8 EBU parameters -*/ -inline const CRC::Parameters & CRC::CRC_8_EBU() -{ - static const Parameters parameters = { 0x1D, 0xFF, 0x00, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-8 MAXIM (aka CRC-8 DOW-CRC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-8 MAXIM has the following parameters and check value: - - polynomial = 0x31 - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = true - - reflect output = true - - check value = 0xA1 - @return CRC-8 MAXIM parameters -*/ -inline const CRC::Parameters & CRC::CRC_8_MAXIM() -{ - static const Parameters parameters = { 0x31, 0x00, 0x00, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-8 WCDMA. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-8 WCDMA has the following parameters and check value: - - polynomial = 0x9B - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = true - - reflect output = true - - check value = 0x25 - @return CRC-8 WCDMA parameters -*/ -inline const CRC::Parameters & CRC::CRC_8_WCDMA() -{ - static const Parameters parameters = { 0x9B, 0x00, 0x00, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-8 LTE. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-8 LTE has the following parameters and check value: - - polynomial = 0x9B - - initial value = 0x00 - - final XOR = 0x00 - - reflect input = false - - reflect output = false - - check value = 0xEA - @return CRC-8 LTE parameters -*/ -inline const CRC::Parameters & CRC::CRC_8_LTE() -{ - static const Parameters parameters = { 0x9B, 0x00, 0x00, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-10 ITU. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-10 ITU has the following parameters and check value: - - polynomial = 0x233 - - initial value = 0x000 - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0x199 - @return CRC-10 ITU parameters -*/ -inline const CRC::Parameters & CRC::CRC_10() -{ - static const Parameters parameters = { 0x233, 0x000, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-10 CDMA2000. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-10 CDMA2000 has the following parameters and check value: - - polynomial = 0x3D9 - - initial value = 0x3FF - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0x233 - @return CRC-10 CDMA2000 parameters -*/ -inline const CRC::Parameters & CRC::CRC_10_CDMA2000() -{ - static const Parameters parameters = { 0x3D9, 0x3FF, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-11 FlexRay. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-11 FlexRay has the following parameters and check value: - - polynomial = 0x385 - - initial value = 0x01A - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0x5A3 - @return CRC-11 FlexRay parameters -*/ -inline const CRC::Parameters & CRC::CRC_11() -{ - static const Parameters parameters = { 0x385, 0x01A, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-11 NR. - @note The parameters are static and are delayed-constructed to reduce memory - footprint. - @note CRC-11 NR has the following parameters and check value: - - polynomial = 0x621 - - initial value = 0x000 - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0x5CA - @return CRC-11 NR parameters -*/ -inline const CRC::Parameters & CRC::CRC_11_NR() -{ - static const Parameters parameters = { 0x621, 0x000, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-12 CDMA2000. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-12 CDMA2000 has the following parameters and check value: - - polynomial = 0xF13 - - initial value = 0xFFF - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0xD4D - @return CRC-12 CDMA2000 parameters -*/ -inline const CRC::Parameters & CRC::CRC_12_CDMA2000() -{ - static const Parameters parameters = { 0xF13, 0xFFF, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-12 DECT (aka CRC-12 X-CRC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-12 DECT has the following parameters and check value: - - polynomial = 0x80F - - initial value = 0x000 - - final XOR = 0x000 - - reflect input = false - - reflect output = false - - check value = 0xF5B - @return CRC-12 DECT parameters -*/ -inline const CRC::Parameters & CRC::CRC_12_DECT() -{ - static const Parameters parameters = { 0x80F, 0x000, 0x000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-12 UMTS (aka CRC-12 3GPP). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-12 UMTS has the following parameters and check value: - - polynomial = 0x80F - - initial value = 0x000 - - final XOR = 0x000 - - reflect input = false - - reflect output = true - - check value = 0xDAF - @return CRC-12 UMTS parameters -*/ -inline const CRC::Parameters & CRC::CRC_12_UMTS() -{ - static const Parameters parameters = { 0x80F, 0x000, 0x000, false, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-13 BBC. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-13 BBC has the following parameters and check value: - - polynomial = 0x1CF5 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x04FA - @return CRC-13 BBC parameters -*/ -inline const CRC::Parameters & CRC::CRC_13_BBC() -{ - static const Parameters parameters = { 0x1CF5, 0x0000, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-15 CAN. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-15 CAN has the following parameters and check value: - - polynomial = 0x4599 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x059E - @return CRC-15 CAN parameters -*/ -inline const CRC::Parameters & CRC::CRC_15() -{ - static const Parameters parameters = { 0x4599, 0x0000, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-15 MPT1327. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-15 MPT1327 has the following parameters and check value: - - polynomial = 0x6815 - - initial value = 0x0000 - - final XOR = 0x0001 - - reflect input = false - - reflect output = false - - check value = 0x2566 - @return CRC-15 MPT1327 parameters -*/ -inline const CRC::Parameters & CRC::CRC_15_MPT1327() -{ - static const Parameters parameters = { 0x6815, 0x0000, 0x0001, false, false }; - return parameters; -} -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -/** - @brief Returns a set of parameters for CRC-16 ARC (aka CRC-16 IBM, CRC-16 LHA). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 ARC has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = true - - reflect output = true - - check value = 0xBB3D - @return CRC-16 ARC parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_ARC() -{ - static const Parameters parameters = { 0x8005, 0x0000, 0x0000, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 BUYPASS (aka CRC-16 VERIFONE, CRC-16 UMTS). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 BUYPASS has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0xFEE8 - @return CRC-16 BUYPASS parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_BUYPASS() -{ - static const Parameters parameters = { 0x8005, 0x0000, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 CCITT FALSE. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 CCITT FALSE has the following parameters and check value: - - polynomial = 0x1021 - - initial value = 0xFFFF - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x29B1 - @return CRC-16 CCITT FALSE parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_CCITTFALSE() -{ - static const Parameters parameters = { 0x1021, 0xFFFF, 0x0000, false, false }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-16 CDMA2000. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 CDMA2000 has the following parameters and check value: - - polynomial = 0xC867 - - initial value = 0xFFFF - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x4C06 - @return CRC-16 CDMA2000 parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_CDMA2000() -{ - static const Parameters parameters = { 0xC867, 0xFFFF, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 CMS. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 CMS has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0xFFFF - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0xAEE7 - @return CRC-16 CMS parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_CMS() -{ - static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 DECT-R (aka CRC-16 R-CRC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 DECT-R has the following parameters and check value: - - polynomial = 0x0589 - - initial value = 0x0000 - - final XOR = 0x0001 - - reflect input = false - - reflect output = false - - check value = 0x007E - @return CRC-16 DECT-R parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_DECTR() -{ - static const Parameters parameters = { 0x0589, 0x0000, 0x0001, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 DECT-X (aka CRC-16 X-CRC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 DECT-X has the following parameters and check value: - - polynomial = 0x0589 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x007F - @return CRC-16 DECT-X parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_DECTX() -{ - static const Parameters parameters = { 0x0589, 0x0000, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 DNP. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 DNP has the following parameters and check value: - - polynomial = 0x3D65 - - initial value = 0x0000 - - final XOR = 0xFFFF - - reflect input = true - - reflect output = true - - check value = 0xEA82 - @return CRC-16 DNP parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_DNP() -{ - static const Parameters parameters = { 0x3D65, 0x0000, 0xFFFF, true, true }; - return parameters; -} -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -/** - @brief Returns a set of parameters for CRC-16 GENIBUS (aka CRC-16 EPC, CRC-16 I-CODE, CRC-16 DARC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 GENIBUS has the following parameters and check value: - - polynomial = 0x1021 - - initial value = 0xFFFF - - final XOR = 0xFFFF - - reflect input = false - - reflect output = false - - check value = 0xD64E - @return CRC-16 GENIBUS parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_GENIBUS() -{ - static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 KERMIT (aka CRC-16 CCITT, CRC-16 CCITT-TRUE). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 KERMIT has the following parameters and check value: - - polynomial = 0x1021 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = true - - reflect output = true - - check value = 0x2189 - @return CRC-16 KERMIT parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_KERMIT() -{ - static const Parameters parameters = { 0x1021, 0x0000, 0x0000, true, true }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-16 MAXIM. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 MAXIM has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0x0000 - - final XOR = 0xFFFF - - reflect input = true - - reflect output = true - - check value = 0x44C2 - @return CRC-16 MAXIM parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_MAXIM() -{ - static const Parameters parameters = { 0x8005, 0x0000, 0xFFFF, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 MODBUS. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 MODBUS has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0xFFFF - - final XOR = 0x0000 - - reflect input = true - - reflect output = true - - check value = 0x4B37 - @return CRC-16 MODBUS parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_MODBUS() -{ - static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 T10-DIF. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 T10-DIF has the following parameters and check value: - - polynomial = 0x8BB7 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0xD0DB - @return CRC-16 T10-DIF parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_T10DIF() -{ - static const Parameters parameters = { 0x8BB7, 0x0000, 0x0000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 USB. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 USB has the following parameters and check value: - - polynomial = 0x8005 - - initial value = 0xFFFF - - final XOR = 0xFFFF - - reflect input = true - - reflect output = true - - check value = 0xB4C8 - @return CRC-16 USB parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_USB() -{ - static const Parameters parameters = { 0x8005, 0xFFFF, 0xFFFF, true, true }; - return parameters; -} - -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -/** - @brief Returns a set of parameters for CRC-16 X-25 (aka CRC-16 IBM-SDLC, CRC-16 ISO-HDLC, CRC-16 B). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 X-25 has the following parameters and check value: - - polynomial = 0x1021 - - initial value = 0xFFFF - - final XOR = 0xFFFF - - reflect input = true - - reflect output = true - - check value = 0x906E - @return CRC-16 X-25 parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_X25() -{ - static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-16 XMODEM (aka CRC-16 ZMODEM, CRC-16 ACORN, CRC-16 LTE). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-16 XMODEM has the following parameters and check value: - - polynomial = 0x1021 - - initial value = 0x0000 - - final XOR = 0x0000 - - reflect input = false - - reflect output = false - - check value = 0x31C3 - @return CRC-16 XMODEM parameters -*/ -inline const CRC::Parameters & CRC::CRC_16_XMODEM() -{ - static const Parameters parameters = { 0x1021, 0x0000, 0x0000, false, false }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-17 CAN. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-17 CAN has the following parameters and check value: - - polynomial = 0x1685B - - initial value = 0x00000 - - final XOR = 0x00000 - - reflect input = false - - reflect output = false - - check value = 0x04F03 - @return CRC-17 CAN parameters -*/ -inline const CRC::Parameters & CRC::CRC_17_CAN() -{ - static const Parameters parameters = { 0x1685B, 0x00000, 0x00000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-21 CAN. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-21 CAN has the following parameters and check value: - - polynomial = 0x102899 - - initial value = 0x000000 - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0x0ED841 - @return CRC-21 CAN parameters -*/ -inline const CRC::Parameters & CRC::CRC_21_CAN() -{ - static const Parameters parameters = { 0x102899, 0x000000, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 OPENPGP. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-24 OPENPGP has the following parameters and check value: - - polynomial = 0x864CFB - - initial value = 0xB704CE - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0x21CF02 - @return CRC-24 OPENPGP parameters -*/ -inline const CRC::Parameters & CRC::CRC_24() -{ - static const Parameters parameters = { 0x864CFB, 0xB704CE, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 FlexRay-A. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-24 FlexRay-A has the following parameters and check value: - - polynomial = 0x5D6DCB - - initial value = 0xFEDCBA - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0x7979BD - @return CRC-24 FlexRay-A parameters -*/ -inline const CRC::Parameters & CRC::CRC_24_FLEXRAYA() -{ - static const Parameters parameters = { 0x5D6DCB, 0xFEDCBA, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 FlexRay-B. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-24 FlexRay-B has the following parameters and check value: - - polynomial = 0x5D6DCB - - initial value = 0xABCDEF - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0x1F23B8 - @return CRC-24 FlexRay-B parameters -*/ -inline const CRC::Parameters & CRC::CRC_24_FLEXRAYB() -{ - static const Parameters parameters = { 0x5D6DCB, 0xABCDEF, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 LTE-A/NR-A. - @note The parameters are static and are delayed-constructed to reduce memory - footprint. - @note CRC-24 LTE-A has the following parameters and check value: - - polynomial = 0x864CFB - - initial value = 0x000000 - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0xCDE703 - @return CRC-24 LTE-A parameters -*/ -inline const CRC::Parameters & CRC::CRC_24_LTEA() -{ - static const Parameters parameters = { 0x864CFB, 0x000000, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 LTE-B/NR-B. - @note The parameters are static and are delayed-constructed to reduce memory - footprint. - @note CRC-24 LTE-B has the following parameters and check value: - - polynomial = 0x800063 - - initial value = 0x000000 - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0x23EF52 - @return CRC-24 LTE-B parameters -*/ -inline const CRC::Parameters & CRC::CRC_24_LTEB() -{ - static const Parameters parameters = { 0x800063, 0x000000, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-24 NR-C. - @note The parameters are static and are delayed-constructed to reduce memory - footprint. - @note CRC-24 NR-C has the following parameters and check value: - - polynomial = 0xB2B117 - - initial value = 0x000000 - - final XOR = 0x000000 - - reflect input = false - - reflect output = false - - check value = 0xF48279 - @return CRC-24 NR-C parameters -*/ -inline const CRC::Parameters & CRC::CRC_24_NRC() -{ - static const Parameters parameters = { 0xB2B117, 0x000000, 0x000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-30 CDMA. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-30 CDMA has the following parameters and check value: - - polynomial = 0x2030B9C7 - - initial value = 0x3FFFFFFF - - final XOR = 0x00000000 - - reflect input = false - - reflect output = false - - check value = 0x3B3CB540 - @return CRC-30 CDMA parameters -*/ -inline const CRC::Parameters & CRC::CRC_30() -{ - static const Parameters parameters = { 0x2030B9C7, 0x3FFFFFFF, 0x00000000, false, false }; - return parameters; -} -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -/** - @brief Returns a set of parameters for CRC-32 (aka CRC-32 ADCCP, CRC-32 PKZip). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 has the following parameters and check value: - - polynomial = 0x04C11DB7 - - initial value = 0xFFFFFFFF - - final XOR = 0xFFFFFFFF - - reflect input = true - - reflect output = true - - check value = 0xCBF43926 - @return CRC-32 parameters -*/ -inline const CRC::Parameters & CRC::CRC_32() -{ - static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-32 BZIP2 (aka CRC-32 AAL5, CRC-32 DECT-B, CRC-32 B-CRC). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 BZIP2 has the following parameters and check value: - - polynomial = 0x04C11DB7 - - initial value = 0xFFFFFFFF - - final XOR = 0xFFFFFFFF - - reflect input = false - - reflect output = false - - check value = 0xFC891918 - @return CRC-32 BZIP2 parameters -*/ -inline const CRC::Parameters & CRC::CRC_32_BZIP2() -{ - static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, false, false }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-32 C (aka CRC-32 ISCSI, CRC-32 Castagnoli, CRC-32 Interlaken). - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 C has the following parameters and check value: - - polynomial = 0x1EDC6F41 - - initial value = 0xFFFFFFFF - - final XOR = 0xFFFFFFFF - - reflect input = true - - reflect output = true - - check value = 0xE3069283 - @return CRC-32 C parameters -*/ -inline const CRC::Parameters & CRC::CRC_32_C() -{ - static const Parameters parameters = { 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; - return parameters; -} -#endif - -/** - @brief Returns a set of parameters for CRC-32 MPEG-2. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 MPEG-2 has the following parameters and check value: - - polynomial = 0x04C11DB7 - - initial value = 0xFFFFFFFF - - final XOR = 0x00000000 - - reflect input = false - - reflect output = false - - check value = 0x0376E6E7 - @return CRC-32 MPEG-2 parameters -*/ -inline const CRC::Parameters & CRC::CRC_32_MPEG2() -{ - static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-32 POSIX. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 POSIX has the following parameters and check value: - - polynomial = 0x04C11DB7 - - initial value = 0x00000000 - - final XOR = 0xFFFFFFFF - - reflect input = false - - reflect output = false - - check value = 0x765E7680 - @return CRC-32 POSIX parameters -*/ -inline const CRC::Parameters & CRC::CRC_32_POSIX() -{ - static const Parameters parameters = { 0x04C11DB7, 0x00000000, 0xFFFFFFFF, false, false }; - return parameters; -} - -#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS -/** - @brief Returns a set of parameters for CRC-32 Q. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-32 Q has the following parameters and check value: - - polynomial = 0x814141AB - - initial value = 0x00000000 - - final XOR = 0x00000000 - - reflect input = false - - reflect output = false - - check value = 0x3010BF7F - @return CRC-32 Q parameters -*/ -inline const CRC::Parameters & CRC::CRC_32_Q() -{ - static const Parameters parameters = { 0x814141AB, 0x00000000, 0x00000000, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-40 GSM. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-40 GSM has the following parameters and check value: - - polynomial = 0x0004820009 - - initial value = 0x0000000000 - - final XOR = 0xFFFFFFFFFF - - reflect input = false - - reflect output = false - - check value = 0xD4164FC646 - @return CRC-40 GSM parameters -*/ -inline const CRC::Parameters & CRC::CRC_40_GSM() -{ - static const Parameters parameters = { 0x0004820009, 0x0000000000, 0xFFFFFFFFFF, false, false }; - return parameters; -} - -/** - @brief Returns a set of parameters for CRC-64 ECMA. - @note The parameters are static and are delayed-constructed to reduce memory footprint. - @note CRC-64 ECMA has the following parameters and check value: - - polynomial = 0x42F0E1EBA9EA3693 - - initial value = 0x0000000000000000 - - final XOR = 0x0000000000000000 - - reflect input = false - - reflect output = false - - check value = 0x6C40DF5F0B497347 - @return CRC-64 ECMA parameters -*/ -inline const CRC::Parameters & CRC::CRC_64() -{ - static const Parameters parameters = { 0x42F0E1EBA9EA3693, 0x0000000000000000, 0x0000000000000000, false, false }; - return parameters; -} -#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - -#ifdef CRCPP_USE_NAMESPACE -} -#endif - -#endif // CRCPP_CRC_H_ diff --git a/third_party/date/CMakeLists.txt b/third_party/date/CMakeLists.txt deleted file mode 100644 index 3e9b809ee..000000000 --- a/third_party/date/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(date_library INTERFACE) -target_include_directories(date_library - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) -add_library(date::date ALIAS date_library) \ No newline at end of file diff --git a/third_party/date/include/date/chrono_io.h b/third_party/date/include/date/chrono_io.h deleted file mode 100644 index 21be40409..000000000 --- a/third_party/date/include/date/chrono_io.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CHRONO_IO_H -#define CHRONO_IO_H - -// The MIT License (MIT) -// -// Copyright (c) 2016, 2017 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -// This functionality has moved to "date.h" - -#include "date.h" - -#endif // CHRONO_IO_H diff --git a/third_party/date/include/date/date.h b/third_party/date/include/date/date.h deleted file mode 100644 index 7b6b4e4cb..000000000 --- a/third_party/date/include/date/date.h +++ /dev/null @@ -1,8200 +0,0 @@ -#ifndef DATE_H -#define DATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2016 Adrian Colomitchi -// Copyright (c) 2017 Florian Dang -// Copyright (c) 2017 Paul Thompson -// Copyright (c) 2018, 2019 Tomasz Kamiński -// Copyright (c) 2019 Jiangang Zhuang -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#ifndef HAS_STRING_VIEW -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_STRING_VIEW 1 -# else -# define HAS_STRING_VIEW 0 -# endif -#endif // HAS_STRING_VIEW - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAS_STRING_VIEW -# include -#endif -#include -#include - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) -# pragma GCC diagnostic ignored "-Wpedantic" -# endif -# if __GNUC__ < 5 - // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -// warning C4127: conditional expression is constant -# pragma warning(disable : 4127) -#endif - -namespace date -{ - -//---------------+ -// Configuration | -//---------------+ - -#ifndef ONLY_C_LOCALE -# define ONLY_C_LOCALE 0 -#endif - -#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) -// MSVC -# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# endif -# if _MSC_VER < 1910 -// before VS2017 -# define CONSTDATA const -# define CONSTCD11 -# define CONSTCD14 -# define NOEXCEPT _NOEXCEPT -# else -// VS2017 and later -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -# endif - -#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 -// Oracle Developer Studio 12.6 and earlier -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept - -#elif __cplusplus >= 201402 -// C++14 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -#else -// C++11 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept -#endif - -#ifndef HAS_UNCAUGHT_EXCEPTIONS -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_UNCAUGHT_EXCEPTIONS 1 -# else -# define HAS_UNCAUGHT_EXCEPTIONS 0 -# endif -#endif // HAS_UNCAUGHT_EXCEPTIONS - -#ifndef HAS_VOID_T -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_VOID_T 1 -# else -# define HAS_VOID_T 0 -# endif -#endif // HAS_VOID_T - -// Protect from Oracle sun macro -#ifdef sun -# undef sun -#endif - -// Work around for a NVCC compiler bug which causes it to fail -// to compile std::ratio_{multiply,divide} when used directly -// in the std::chrono::duration template instantiations below -namespace detail { -template -using ratio_multiply = decltype(std::ratio_multiply{}); - -template -using ratio_divide = decltype(std::ratio_divide{}); -} // namespace detail - -//-----------+ -// Interface | -//-----------+ - -// durations - -using days = std::chrono::duration - , std::chrono::hours::period>>; - -using weeks = std::chrono::duration - , days::period>>; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -template - using sys_time = std::chrono::time_point; - -using sys_days = sys_time; -using sys_seconds = sys_time; - -struct local_t {}; - -template - using local_time = std::chrono::time_point; - -using local_seconds = local_time; -using local_days = local_time; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - day() = default; - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - month() = default; - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - year() = default; - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 year operator-() const NOEXCEPT; - CONSTCD11 year operator+() const NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } - static CONSTCD11 year max() NOEXCEPT { return year{32767}; } -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - weekday() = default; - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - CONSTCD14 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 unsigned c_encoding() const NOEXCEPT; - CONSTCD11 unsigned iso_encoding() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT; - - friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; - template - friend std::basic_ostream& - operator<<(std::basic_ostream& os, const weekday& wd); - friend class weekday_indexed; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - weekday_indexed() = default; - CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - date::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -namespace detail -{ - -struct unspecified_month_disambiguator {}; - -} // namespace detail - -// year_month - -class year_month -{ - date::year y_; - date::month m_; - -public: - year_month() = default; - CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - - template - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - template - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -template -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -template -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - date::month m_; - date::day d_; - -public: - month_day() = default; - CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - date::month m_; - -public: - CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - date::month m_; - date::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const date::month& m, - const date::weekday_last& wd) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - date::year y_; - date::month m_; - date::day d_; - -public: - year_month_day() = default; - CONSTCD11 year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - template - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -template -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -template -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - date::year y_; - date::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT; - - template - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - date::year y_; - date::month m_; - date::weekday_indexed wdi_; - -public: - year_month_weekday() = default; - CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - template - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - date::year y_; - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, - const date::weekday_last& wdl) NOEXCEPT; - - template - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -// CONSTDATA date::month January{1}; -// CONSTDATA date::month February{2}; -// CONSTDATA date::month March{3}; -// CONSTDATA date::month April{4}; -// CONSTDATA date::month May{5}; -// CONSTDATA date::month June{6}; -// CONSTDATA date::month July{7}; -// CONSTDATA date::month August{8}; -// CONSTDATA date::month September{9}; -// CONSTDATA date::month October{10}; -// CONSTDATA date::month November{11}; -// CONSTDATA date::month December{12}; -// -// CONSTDATA date::weekday Sunday{0u}; -// CONSTDATA date::weekday Monday{1u}; -// CONSTDATA date::weekday Tuesday{2u}; -// CONSTDATA date::weekday Wednesday{3u}; -// CONSTDATA date::weekday Thursday{4u}; -// CONSTDATA date::weekday Friday{5u}; -// CONSTDATA date::weekday Saturday{6u}; - -#if HAS_VOID_T - -template > -struct is_clock - : std::false_type -{}; - -template -struct is_clock> - : std::true_type -{}; - -template inline constexpr bool is_clock_v = is_clock::value; - -#endif // HAS_VOID_T - -//----------------+ -// Implementation | -//----------------+ - -// utilities -namespace detail { - -template> -class save_istream -{ -protected: - std::basic_ios& is_; - CharT fill_; - std::ios::fmtflags flags_; - std::streamsize precision_; - std::streamsize width_; - std::basic_ostream* tie_; - std::locale loc_; - -public: - ~save_istream() - { - is_.fill(fill_); - is_.flags(flags_); - is_.precision(precision_); - is_.width(width_); - is_.imbue(loc_); - is_.tie(tie_); - } - - save_istream(const save_istream&) = delete; - save_istream& operator=(const save_istream&) = delete; - - explicit save_istream(std::basic_ios& is) - : is_(is) - , fill_(is.fill()) - , flags_(is.flags()) - , precision_(is.precision()) - , width_(is.width(0)) - , tie_(is.tie(nullptr)) - , loc_(is.getloc()) - { - if (tie_ != nullptr) - tie_->flush(); - } -}; - -template> -class save_ostream - : private save_istream -{ -public: - ~save_ostream() - { - if ((this->flags_ & std::ios::unitbuf) && -#if HAS_UNCAUGHT_EXCEPTIONS - std::uncaught_exceptions() == 0 && -#else - !std::uncaught_exception() && -#endif - this->is_.good()) - this->is_.rdbuf()->pubsync(); - } - - save_ostream(const save_ostream&) = delete; - save_ostream& operator=(const save_ostream&) = delete; - - explicit save_ostream(std::basic_ios& os) - : save_istream(os) - { - } -}; - -template -struct choose_trunc_type -{ - static const int digits = std::numeric_limits::digits; - using type = typename std::conditional - < - digits < 32, - std::int32_t, - typename std::conditional - < - digits < 64, - std::int64_t, -#ifdef __SIZEOF_INT128__ - __int128 -#else - std::int64_t -#endif - >::type - >::type; -}; - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - using std::numeric_limits; - using I = typename choose_trunc_type::type; - CONSTDATA auto digits = numeric_limits::digits; - static_assert(digits < numeric_limits::digits, ""); - CONSTDATA auto max = I{1} << (digits-1); - CONSTDATA auto min = -max; - const auto negative = t < T{0}; - if (min <= t && t <= max && t != 0 && t == t) - { - t = static_cast(static_cast(t)); - if (t == 0 && negative) - t = -t; - } - return t; -} - -template -struct static_gcd -{ - static const std::intmax_t value = static_gcd::value; -}; - -template -struct static_gcd -{ - static const std::intmax_t value = Xp; -}; - -template <> -struct static_gcd<0, 0> -{ - static const std::intmax_t value = 1; -}; - -template -struct no_overflow -{ -private: - static const std::intmax_t gcd_n1_n2 = static_gcd::value; - static const std::intmax_t gcd_d1_d2 = static_gcd::value; - static const std::intmax_t n1 = R1::num / gcd_n1_n2; - static const std::intmax_t d1 = R1::den / gcd_d1_d2; - static const std::intmax_t n2 = R2::num / gcd_n1_n2; - static const std::intmax_t d2 = R2::den / gcd_d1_d2; -#ifdef __cpp_constexpr - static const std::intmax_t max = std::numeric_limits::max(); -#else - static const std::intmax_t max = LLONG_MAX; -#endif - - template - struct mul // overflow == false - { - static const std::intmax_t value = Xp * Yp; - }; - - template - struct mul - { - static const std::intmax_t value = 1; - }; - -public: - static const bool value = (n1 <= max / d2) && (n2 <= max / d1); - typedef std::ratio::value, - mul::value> type; -}; - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - return To{detail::trunc(std::chrono::duration_cast(d).count())}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - using std::chrono::duration_cast; - using std::chrono::duration; - using rep = typename std::common_type::type; - return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; -} - -#ifndef HAS_CHRONO_ROUNDING -# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) -# define HAS_CHRONO_ROUNDING 1 -# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 -# define HAS_CHRONO_ROUNDING 1 -# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 -# define HAS_CHRONO_ROUNDING 1 -# else -# define HAS_CHRONO_ROUNDING 0 -# endif -#endif // HAS_CHRONO_ROUNDING - -#if HAS_CHRONO_ROUNDING == 0 - -// round down -template -CONSTCD14 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t > d) - return t - To{1}; - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - using rep = typename std::common_type::type; - return floor(floor>(d)); -} - -// round to nearest, to even on tie -template -CONSTCD14 -inline -To -round(const std::chrono::duration& d) -{ - auto t0 = floor(d); - auto t1 = t0 + To{1}; - if (t1 == To{0} && t0 < To{0}) - t1 = -t1; - auto diff0 = d - t0; - auto diff1 = t1 - d; - if (diff0 == diff1) - { - if (t0 - trunc(t0/2)*2 == To{0}) - return t0; - return t1; - } - if (diff0 < diff1) - return t0; - return t1; -} - -// round up -template -CONSTCD14 -inline -To -ceil(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t < d) - return t + To{1}; - return t; -} - -template ::is_signed - >::type> -CONSTCD11 -std::chrono::duration -abs(std::chrono::duration d) -{ - return d >= d.zero() ? d : -d; -} - -// round down -template -CONSTCD11 -inline -std::chrono::time_point -floor(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{date::floor(tp.time_since_epoch())}; -} - -// round to nearest, to even on tie -template -CONSTCD11 -inline -std::chrono::time_point -round(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round(tp.time_since_epoch())}; -} - -// round up -template -CONSTCD11 -inline -std::chrono::time_point -ceil(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{ceil(tp.time_since_epoch())}; -} - -#else // HAS_CHRONO_ROUNDING == 1 - -using std::chrono::floor; -using std::chrono::ceil; -using std::chrono::round; -using std::chrono::abs; - -#endif // HAS_CHRONO_ROUNDING - -namespace detail -{ - -template -CONSTCD14 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return round(d); -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return d; -} - -template -CONSTCD11 -inline -std::chrono::time_point -round_i(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round_i(tp.time_since_epoch())}; -} - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -std::chrono::time_point -trunc(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{trunc(tp.time_since_epoch())}; -} - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const day& d) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - detail::low_level_fmt(os, d); - if (!d.ok()) - os << " is not a valid day"; - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) + y.count() - 1; - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month& m) -{ - if (m.ok()) - { - CharT fmt[] = {'%', 'b', 0}; - os << format(os.getloc(), fmt, m); - } - else - os << static_cast(m); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - detail::low_level_fmt(os, m); - if (!m.ok()) - os << " is not a valid month"; - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} -CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} -CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} - -CONSTCD11 -inline -bool -year::ok() const NOEXCEPT -{ - return y_ != std::numeric_limits::min(); -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year& y) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os.imbue(std::locale::classic()); - os << static_cast(y); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - detail::low_level_fmt(os, y); - if (!y.ok()) - os << " is not a valid year"; - return os; -} - -// weekday - -CONSTCD14 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - auto u = static_cast(z); - return static_cast(z >= -4 ? (u+4) % 7 : u % 7); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd != 7 ? wd : 0)) - {} - -CONSTCD14 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -unsigned weekday::c_encoding() const NOEXCEPT -{ - return unsigned{wd_}; -} - -CONSTCD11 -inline -unsigned weekday::iso_encoding() const NOEXCEPT -{ - return unsigned{((wd_ == 0u) ? 7u : wd_)}; -} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return x.wd_ == y.wd_; -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const wdu = x.wd_ - y.wd_; - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return days{wdu - wk * 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x.wd_)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday& wd) -{ - if (wd.ok()) - { - CharT fmt[] = {'%', 'a', 0}; - os << format(fmt, wd); - } - else - os << wd.c_encoding(); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - detail::low_level_fmt(os, wd); - if (!wd.ok()) - os << " is not a valid weekday"; - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -date::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return date::day{static_cast(d)}; -} - -CONSTCD11 -inline -date::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return date::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA date::last_spec last{}; - -CONSTDATA date::month jan{1}; -CONSTDATA date::month feb{2}; -CONSTDATA date::month mar{3}; -CONSTDATA date::month apr{4}; -CONSTDATA date::month may{5}; -CONSTDATA date::month jun{6}; -CONSTDATA date::month jul{7}; -CONSTDATA date::month aug{8}; -CONSTDATA date::month sep{9}; -CONSTDATA date::month oct{10}; -CONSTDATA date::month nov{11}; -CONSTDATA date::month dec{12}; - -CONSTDATA date::weekday sun{0u}; -CONSTDATA date::weekday mon{1u}; -CONSTDATA date::weekday tue{2u}; -CONSTDATA date::weekday wed{3u}; -CONSTDATA date::weekday thu{4u}; -CONSTDATA date::weekday fri{5u}; -CONSTDATA date::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -CONSTDATA date::month January{1}; -CONSTDATA date::month February{2}; -CONSTDATA date::month March{3}; -CONSTDATA date::month April{4}; -CONSTDATA date::month May{5}; -CONSTDATA date::month June{6}; -CONSTDATA date::month July{7}; -CONSTDATA date::month August{8}; -CONSTDATA date::month September{9}; -CONSTDATA date::month October{10}; -CONSTDATA date::month November{11}; -CONSTDATA date::month December{12}; - -CONSTDATA date::weekday Monday{1}; -CONSTDATA date::weekday Tuesday{2}; -CONSTDATA date::weekday Wednesday{3}; -CONSTDATA date::weekday Thursday{4}; -CONSTDATA date::weekday Friday{5}; -CONSTDATA date::weekday Saturday{6}; -CONSTDATA date::weekday Sunday{7}; - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return date::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -#endif // __GNUC__ - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd.wd_))) - , index_(static_cast(index)) - {} - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif // __GNUC__ - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']'; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - detail::low_level_fmt(os, wdi); - if (!wdi.ok()) - os << " is not a valid weekday_indexed"; - return os; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_last& wdl) -{ - return low_level_fmt(os, wdl.weekday()) << "[last]"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - detail::low_level_fmt(os, wdl); - if (!wdl.ok()) - os << " is not a valid weekday_last"; - return os; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -template -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -template -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -template -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -template -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -template -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month& ym) -{ - low_level_fmt(os, ym.year()) << '/'; - return low_level_fmt(os, ym.month()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - detail::low_level_fmt(os, ym); - if (!ym.ok()) - os << " is not a valid year_month"; - return os; -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(29), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day& md) -{ - low_level_fmt(os, md.month()) << '/'; - return low_level_fmt(os, md.day()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - detail::low_level_fmt(os, md); - if (!md.ok()) - os << " is not a valid month_day"; - return os; -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day_last& mdl) -{ - return low_level_fmt(os, mdl.month()) << "/last"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - detail::low_level_fmt(os, mdl); - if (!mdl.ok()) - os << " is not a valid month_day_last"; - return os; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday& mwd) -{ - low_level_fmt(os, mwd.month()) << '/'; - return low_level_fmt(os, mwd.weekday_indexed()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - detail::low_level_fmt(os, mwd); - if (!mwd.ok()) - os << " is not a valid month_weekday"; - return os; -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - low_level_fmt(os, mwdl.month()) << '/'; - return low_level_fmt(os, mwdl.weekday_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - detail::low_level_fmt(os, mwdl); - if (!mwdl.ok()) - os << " is not a valid month_weekday_last"; - return os; -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(28), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return (month() != February || !y_.is_leap()) && mdl_.ok() ? - d[static_cast(month()) - 1] : date::day{29}; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - low_level_fmt(os, ymdl.year()) << '/'; - return low_level_fmt(os, ymdl.month_day_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - detail::low_level_fmt(os, ymdl); - if (!ymdl.ok()) - os << " is not a valid year_month_day_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -template -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= February); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-399) / 400; - auto const yoe = static_cast(y - era * 400); // [0, 399] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] - return days{era * 146097 + static_cast(doe) - 719468}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.imbue(std::locale::classic()); - os << static_cast(ymd.year()) << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os.width(2); - os << static_cast(ymd.day()); - if (!ymd.ok()) - os << " is not a valid year_month_day"; - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719468; - auto const era = (z >= 0 ? z : z - 146096) / 146097; - auto const doe = static_cast(z - era * 146097); // [0, 146096] - auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] - auto const y = static_cast(yoe) + era * 400; - auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; -} - -template -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -template -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -template -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + - days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = date::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - detail::low_level_fmt(os, ymwdi.year()) << '/'; - detail::low_level_fmt(os, ymwdi.month()) << '/'; - detail::low_level_fmt(os, ymwdi.weekday_indexed()); - if (!ymwdi.ok()) - os << " is not a valid year_month_weekday"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -template -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const date::year& y, - const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - detail::low_level_fmt(os, ymwdl.year()) << '/'; - detail::low_level_fmt(os, ymwdl.month()) << '/'; - detail::low_level_fmt(os, ymwdl.weekday_last()); - if (!ymwdl.ok()) - os << " is not a valid year_month_weekday_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -template -struct fields; - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr); - -template -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr); - -// hh_mm_ss - -namespace detail -{ - -struct undocumented {explicit undocumented() = default;}; - -// width::value is the number of fractional decimal digits in 1/n -// width<0>::value and width<1>::value are defined to be 0 -// If 1/n takes more than 18 fractional decimal digits, -// the result is truncated to 19. -// Example: width<2>::value == 1 -// Example: width<3>::value == 19 -// Example: width<4>::value == 2 -// Example: width<10>::value == 1 -// Example: width<1000>::value == 3 -template -struct width -{ - static_assert(d > 0, "width called with zero denominator"); - static CONSTDATA unsigned value = 1 + width::value; -}; - -template -struct width -{ - static CONSTDATA unsigned value = 0; -}; - -template -struct static_pow10 -{ -private: - static CONSTDATA std::uint64_t h = static_pow10::value; -public: - static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); -}; - -template <> -struct static_pow10<0> -{ - static CONSTDATA std::uint64_t value = 1; -}; - -template -class decimal_format_seconds -{ - using CT = typename std::common_type::type; - using rep = typename CT::rep; - static unsigned CONSTDATA trial_width = - detail::width::value; -public: - static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u; - using precision = std::chrono::duration::value>>; - -private: - std::chrono::seconds s_; - precision sub_s_; - -public: - CONSTCD11 decimal_format_seconds() - : s_() - , sub_s_() - {} - - CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT - : s_(std::chrono::duration_cast(d)) - , sub_s_(std::chrono::duration_cast(d - s_)) - {} - - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} - CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return s_ + sub_s_; - } - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1}; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const decimal_format_seconds& x) - { - return x.print(os, std::chrono::treat_as_floating_point{}); - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::true_type) const - { - date::detail::save_ostream _(os); - std::chrono::duration d = s_ + sub_s_; - if (d < std::chrono::seconds{10}) - os << '0'; - os.precision(width+6); - os << std::fixed << d.count(); - return os; - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::false_type) const - { - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << s_.count(); - if (width > 0) - { -#if !ONLY_C_LOCALE - os << std::use_facet>(os.getloc()).decimal_point(); -#else - os << '.'; -#endif - date::detail::save_ostream _s(os); - os.imbue(std::locale::classic()); - os.width(width); - os << sub_s_.count(); - } - return os; - } -}; - -template -inline -CONSTCD11 -typename std::enable_if - < - std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d >= d.zero() ? +d : -d; -} - -template -inline -CONSTCD11 -typename std::enable_if - < - !std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d; -} - -} // namespace detail - -template -class hh_mm_ss -{ - using dfs = detail::decimal_format_seconds::type>; - - std::chrono::hours h_; - std::chrono::minutes m_; - dfs s_; - bool neg_; - -public: - static unsigned CONSTDATA fractional_width = dfs::width; - using precision = typename dfs::precision; - - CONSTCD11 hh_mm_ss() NOEXCEPT - : hh_mm_ss(Duration::zero()) - {} - - CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT - : h_(std::chrono::duration_cast(detail::abs(d))) - , m_(std::chrono::duration_cast(detail::abs(d)) - h_) - , s_(detail::abs(d) - h_ - m_) - , neg_(d < Duration::zero()) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} - CONSTCD14 std::chrono::seconds& - seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} - CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} - CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} - - CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} - CONSTCD11 precision to_duration() const NOEXCEPT - {return (s_.to_duration() + m_ + h_) * (1-2*neg_);} - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} && - s_.in_conventional_range(); - } - -private: - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, hh_mm_ss const& tod) - { - if (tod.is_negative()) - os << '-'; - if (tod.h_ < std::chrono::hours{10}) - os << '0'; - os << tod.h_.count() << ':'; - if (tod.m_ < std::chrono::minutes{10}) - os << '0'; - os << tod.m_.count() << ':' << tod.s_; - return os; - } - - template - friend - std::basic_ostream& - date::to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec); - - template - friend - std::basic_istream& - date::from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, - std::basic_string* abbrev, std::chrono::minutes* offset); -}; - -inline -CONSTCD14 -bool -is_am(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{0} <= h && h < hours{12}; -} - -inline -CONSTCD14 -bool -is_pm(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{12} <= h && h < hours{24}; -} - -inline -CONSTCD14 -std::chrono::hours -make12(std::chrono::hours h) NOEXCEPT -{ - using std::chrono::hours; - if (h < hours{12}) - { - if (h == hours{0}) - h = hours{12}; - } - else - { - if (h != hours{12}) - h = h - hours{12}; - } - return h; -} - -inline -CONSTCD14 -std::chrono::hours -make24(std::chrono::hours h, bool is_pm) NOEXCEPT -{ - using std::chrono::hours; - if (is_pm) - { - if (h != hours{12}) - h = h + hours{12}; - } - else if (h == hours{12}) - h = hours{0}; - return h; -} - -template -using time_of_day = hh_mm_ss; - -template -CONSTCD11 -inline -hh_mm_ss> -make_time(const std::chrono::duration& d) -{ - return hh_mm_ss>(d); -} - -template -inline -typename std::enable_if -< - std::ratio_less::value - , std::basic_ostream& ->::type -operator<<(std::basic_ostream& os, const sys_time& tp) -{ - auto const dp = date::floor(tp); - return os << year_month_day(dp) << ' ' << make_time(tp-dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_days& dp) -{ - return os << year_month_day(dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& ut) -{ - return (os << sys_time{ut.time_since_epoch()}); -} - -namespace detail -{ - -template -class string_literal; - -template -inline -CONSTCD14 -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT; - -template -class string_literal -{ - CharT p_[N]; - - CONSTCD11 string_literal() NOEXCEPT - : p_{} - {} - -public: - using const_iterator = const CharT*; - - string_literal(string_literal const&) = default; - string_literal& operator=(string_literal const&) = delete; - - template ::type> - CONSTCD11 string_literal(CharT c) NOEXCEPT - : p_{c} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT - : p_{c1, c2} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT - : p_{c1, c2, c3} - { - } - - CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::type> - CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::value>::type> - CONSTCD14 string_literal(string_literal const& a) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} - CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} - - CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} - CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} - - CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT - { - return p_[n]; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const string_literal& s) - { - return os << s.p_; - } - - template - friend - CONSTCD14 - string_literal::type, - N1 + N2 - 1> - operator+(const string_literal& x, const string_literal& y) NOEXCEPT; -}; - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], y[0]); -} - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], x[1], y[0]); -} - -template -CONSTCD14 -inline -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - using CT = typename std::conditional::type; - - string_literal r; - std::size_t i = 0; - for (; i < N1-1; ++i) - r.p_[i] = CT(x.p_[i]); - for (std::size_t j = 0; j < N2; ++j, ++i) - r.p_[i] = CT(y.p_[j]); - - return r; -} - - -template -inline -std::basic_string -operator+(std::basic_string x, const string_literal& y) -{ - x.append(y.data(), y.size()); - return x; -} - -#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ - && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) - -template ::value || - std::is_same::value || - std::is_same::value || - std::is_same::value>> -CONSTCD14 -inline -string_literal -msl(CharT c) NOEXCEPT -{ - return string_literal{c}; -} - -CONSTCD14 -inline -std::size_t -to_string_len(std::intmax_t i) -{ - std::size_t r = 0; - do - { - i /= 10; - ++r; - } while (i > 0); - return r; -} - -template -CONSTCD14 -inline -std::enable_if_t -< - N < 10, - string_literal -> -msl() NOEXCEPT -{ - return msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - 10 <= N, - string_literal -> -msl() NOEXCEPT -{ - return msl() + msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den != 1, - string_literal::type::num) + - to_string_len(std::ratio::type::den) + 4> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + - msl() + msl(CharT{']'}); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den == 1, - string_literal::type::num) + 3> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{']'}); -} - - -#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -inline -std::string -to_string(std::uint64_t x) -{ - return std::to_string(x); -} - -template -inline -std::basic_string -to_string(std::uint64_t x) -{ - auto y = std::to_string(x); - return std::basic_string(y.begin(), y.end()); -} - -template -inline -typename std::enable_if -< - std::ratio::type::den != 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + - to_string(R::den) + CharT{']'}; -} - -template -inline -typename std::enable_if -< - std::ratio::type::den == 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; -} - -#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -template -CONSTCD11 -inline -string_literal -msl(std::atto) NOEXCEPT -{ - return string_literal{'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::femto) NOEXCEPT -{ - return string_literal{'f'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::pico) NOEXCEPT -{ - return string_literal{'p'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::nano) NOEXCEPT -{ - return string_literal{'n'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{'\xC2', '\xB5'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{CharT{static_cast('\xB5')}}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::milli) NOEXCEPT -{ - return string_literal{'m'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::centi) NOEXCEPT -{ - return string_literal{'c'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deca) NOEXCEPT -{ - return string_literal{'d', 'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deci) NOEXCEPT -{ - return string_literal{'d'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::hecto) NOEXCEPT -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::kilo) NOEXCEPT -{ - return string_literal{'k'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::mega) NOEXCEPT -{ - return string_literal{'M'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::giga) NOEXCEPT -{ - return string_literal{'G'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::tera) NOEXCEPT -{ - return string_literal{'T'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::peta) NOEXCEPT -{ - return string_literal{'P'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::exa) NOEXCEPT -{ - return string_literal{'E'}; -} - -template -CONSTCD11 -inline -auto -get_units(Period p) - -> decltype(msl(p) + string_literal{'s'}) -{ - return msl(p) + string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<1>) -{ - return string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<3600>) -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<60>) -{ - return string_literal{'m', 'i', 'n'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<86400>) -{ - return string_literal{'d'}; -} - -template > -struct make_string; - -template <> -struct make_string -{ - template - static - std::string - from(Rep n) - { - return std::to_string(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_string(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -template <> -struct make_string -{ - template - static - std::wstring - from(Rep n) - { - return std::to_wstring(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_wstring(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -} // namespace detail - -// to_stream - -CONSTDATA year nanyear{-32768}; - -template -struct fields -{ - year_month_day ymd{nanyear/0/0}; - weekday wd{8u}; - hh_mm_ss tod{}; - bool has_tod = false; - - fields() = default; - - fields(year_month_day ymd_) : ymd(ymd_) {} - fields(weekday wd_) : wd(wd_) {} - fields(hh_mm_ss tod_) : tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} - fields(year_month_day ymd_, hh_mm_ss tod_) : ymd(ymd_), tod(tod_), - has_tod(true) {} - - fields(weekday wd_, hh_mm_ss tod_) : wd(wd_), tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_, hh_mm_ss tod_) - : ymd(ymd_) - , wd(wd_) - , tod(tod_) - , has_tod(true) - {} -}; - -namespace detail -{ - -template -unsigned -extract_weekday(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.ok() && !fds.wd.ok()) - { - // fds does not contain a valid weekday - os.setstate(std::ios::failbit); - return 8; - } - weekday wd; - if (fds.ymd.ok()) - { - wd = weekday{sys_days(fds.ymd)}; - if (fds.wd.ok() && wd != fds.wd) - { - // fds.ymd and fds.wd are inconsistent - os.setstate(std::ios::failbit); - return 8; - } - } - else - wd = fds.wd; - return static_cast((wd - Sunday).count()); -} - -template -unsigned -extract_month(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.month().ok()) - { - // fds does not contain a valid month - os.setstate(std::ios::failbit); - return 0; - } - return static_cast(fds.ymd.month()); -} - -} // namespace detail - -#if ONLY_C_LOCALE - -namespace detail -{ - -inline -std::pair -weekday_names() -{ - static const std::string nm[] = - { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -month_names() -{ - static const std::string nm[] = - { - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -ampm_names() -{ - static const std::string nm[] = - { - "AM", - "PM" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -template -FwdIter -scan_keyword(std::basic_istream& is, FwdIter kb, FwdIter ke) -{ - size_t nkw = static_cast(std::distance(kb, ke)); - const unsigned char doesnt_match = '\0'; - const unsigned char might_match = '\1'; - const unsigned char does_match = '\2'; - unsigned char statbuf[100]; - unsigned char* status = statbuf; - std::unique_ptr stat_hold(0, free); - if (nkw > sizeof(statbuf)) - { - status = (unsigned char*)std::malloc(nkw); - if (status == nullptr) - throw std::bad_alloc(); - stat_hold.reset(status); - } - size_t n_might_match = nkw; // At this point, any keyword might match - size_t n_does_match = 0; // but none of them definitely do - // Initialize all statuses to might_match, except for "" keywords are does_match - unsigned char* st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (!ky->empty()) - *st = might_match; - else - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - // While there might be a match, test keywords against the next CharT - for (size_t indx = 0; is && n_might_match > 0; ++indx) - { - // Peek at the next CharT but don't consume it - auto ic = is.peek(); - if (ic == EOF) - { - is.setstate(std::ios::eofbit); - break; - } - auto c = static_cast(toupper(static_cast(ic))); - bool consume = false; - // For each keyword which might match, see if the indx character is c - // If a match if found, consume c - // If a match is found, and that is the last character in the keyword, - // then that keyword matches. - // If the keyword doesn't match this character, then change the keyword - // to doesn't match - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == might_match) - { - if (c == static_cast(toupper(static_cast((*ky)[indx])))) - { - consume = true; - if (ky->size() == indx+1) - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - else - { - *st = doesnt_match; - --n_might_match; - } - } - } - // consume if we matched a character - if (consume) - { - (void)is.get(); - // If we consumed a character and there might be a matched keyword that - // was marked matched on a previous iteration, then such keywords - // are now marked as not matching. - if (n_might_match + n_does_match > 1) - { - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == does_match && ky->size() != indx+1) - { - *st = doesnt_match; - --n_does_match; - } - } - } - } - } - // We've exited the loop because we hit eof and/or we have no more "might matches". - // Return the first matching result - for (st = status; kb != ke; ++kb, ++st) - if (*st == does_match) - break; - if (kb == ke) - is.setstate(std::ios::failbit); - return kb; -} - -} // namespace detail - -#endif // ONLY_C_LOCALE - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec) -{ -#if ONLY_C_LOCALE - using detail::weekday_names; - using detail::month_names; - using detail::ampm_names; -#endif - using detail::save_ostream; - using detail::get_units; - using detail::extract_weekday; - using detail::extract_month; - using std::ios; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - date::detail::save_ostream ss(os); - os.fill(' '); - os.flags(std::ios::skipws | std::ios::dec); - os.width(0); - tm tm{}; - bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); -#if !ONLY_C_LOCALE - auto& facet = std::use_facet>(os.getloc()); -#endif - const CharT* command = nullptr; - CharT modified = CharT{}; - for (; *fmt; ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - if (command) - { - if (modified == CharT{}) - { - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - tm.tm_mon = static_cast(extract_month(os, fds)) - 1; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'c': - case 'x': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - if (*fmt == 'c' && !fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - auto const& ymd = fds.ymd; - auto ld = local_days(ymd); - if (*fmt == 'c') - { - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - } - tm.tm_mday = static_cast(static_cast(ymd.day())); - tm.tm_mon = static_cast(extract_month(os, fds) - 1); - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else // ONLY_C_LOCALE - if (*fmt == 'c') - { - auto wd = static_cast(extract_weekday(os, fds)); - os << weekday_names().first[static_cast(wd)+7] - << ' '; - os << month_names().first[extract_month(os, fds)-1+12] << ' '; - auto d = static_cast(static_cast(fds.ymd.day())); - if (d < 10) - os << ' '; - os << d << ' ' - << make_time(duration_cast(fds.tod.to_duration())) - << ' ' << fds.ymd.year(); - - } - else // *fmt == 'x' - { - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } -#endif // ONLY_C_LOCALE - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'C': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (y >= 0) - { - os.width(2); - os << y/100; - } - else - { - os << CharT{'-'}; - os.width(2); - os << -(y-99)/100; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - tm.tm_year = y - 1900; - CharT f[3] = {'%', 'E', 'C'}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'd': - case 'e': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.day().ok()) - os.setstate(std::ios::failbit); - auto d = static_cast(static_cast(fds.ymd.day())); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - if (*fmt == CharT{'d'}) - os.fill('0'); - else - os.fill(' '); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << d; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - tm.tm_mday = d; - CharT f[3] = {'%', 'O', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.imbue(std::locale::classic()); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(4); - os << static_cast(ymd.year()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.month()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.day()); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'g': - case 'G': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); - auto y = year_month_day{ld + days{3}}.year(); - auto start = local_days((y-years{1})/December/Thursday[last]) + - (Monday-Thursday); - if (ld < start) - --y; - if (*fmt == CharT{'G'}) - os << y; - else - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << std::abs(static_cast(y)) % 100; - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'H': - case 'I': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } - auto hms = fds.tod; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_hour = static_cast(hms.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - if (fds.ymd.ok() || fds.has_tod) - { - days doy; - if (fds.ymd.ok()) - { - auto ld = local_days(fds.ymd); - auto y = fds.ymd.year(); - doy = ld - local_days(y/January/1) + days{1}; - } - else - { - doy = duration_cast(fds.tod.to_duration()); - } - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(3); - os << doy.count(); - } - else - { - os.setstate(std::ios::failbit); - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'm': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.month().ok()) - os.setstate(std::ios::failbit); - auto m = static_cast(fds.ymd.month()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (m < 10) - os << CharT{'0'}; - os << m; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_mon = static_cast(m-1); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'M': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_min = static_cast(fds.tod.minutes().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'n': - if (command) - { - if (modified == CharT{}) - os << CharT{'\n'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - if (date::is_am(fds.tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Q': - case 'q': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - auto d = fds.tod.to_duration(); - if (*fmt == 'q') - os << get_units(typename decltype(d)::period::type{}); - else - os << d.count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_sec = static_cast(fds.tod.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - hh_mm_ss tod(duration_cast(fds.tod.to_duration())); - save_ostream _(os); - os.fill('0'); - os.width(2); - os << date::make12(tod.hours()).count() << CharT{':'}; - os.width(2); - os << tod.minutes().count() << CharT{':'}; - os.width(2); - os << tod.seconds().count() << CharT{' '}; - if (date::is_am(tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (fds.tod.hours() < hours{10}) - os << CharT{'0'}; - os << fds.tod.hours().count() << CharT{':'}; - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'S': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << fds.tod.s_; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 't': - if (command) - { - if (modified == CharT{}) - os << CharT{'\t'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - os << fds.tod; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'u': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto wd = extract_weekday(os, fds); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << (wd != 0 ? wd : 7u); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'U': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Sunday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } - #if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'V': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto y = year_month_day{ld + days{3}}.year(); - auto st = local_days((y-years{1})/12/Thursday[last]) + - (Monday-Thursday); - if (ld < st) - { - --y; - st = local_days((y - years{1})/12/Thursday[last]) + - (Monday-Thursday); - } - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - auto const& ymd = fds.ymd; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'w': - if (command) - { - auto wd = extract_weekday(os, fds); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - os << wd; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'W': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Monday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'X': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else - os << fds.tod; -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'y': - if (command) - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - y = std::abs(y) % 100; - if (y < 10) - os << CharT{'0'}; - os << y; -#if !ONLY_C_LOCALE - } - else - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = y - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Y': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = fds.ymd.year(); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.imbue(std::locale::classic()); - os << y; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(y) - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'z': - if (command) - { - if (offset_sec == nullptr) - { - // Can not format %z with unknown offset - os.setstate(ios::failbit); - return os; - } - auto m = duration_cast(*offset_sec); - auto neg = m < minutes{0}; - m = date::abs(m); - auto h = duration_cast(m); - m -= h; - if (neg) - os << CharT{'-'}; - else - os << CharT{'+'}; - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - if (modified != CharT{}) - os << CharT{':'}; - if (m < minutes{10}) - os << CharT{'0'}; - os << m.count(); - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - if (abbrev == nullptr) - { - // Can not format %Z with unknown time_zone - os.setstate(ios::failbit); - return os; - } - for (auto c : *abbrev) - os << CharT(c); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - os << CharT{'%'} << modified << *fmt; - command = nullptr; - modified = CharT{}; - } - } - else - os << *fmt; - break; - case '%': - if (command) - { - if (modified == CharT{}) - { - os << CharT{'%'}; - command = nullptr; - } - else - { - os << CharT{'%'} << modified << CharT{'%'}; - command = nullptr; - modified = CharT{}; - } - } - else - command = fmt; - break; - default: - if (command) - { - os << CharT{'%'}; - command = nullptr; - } - if (modified != CharT{}) - { - os << modified; - modified = CharT{}; - } - os << *fmt; - break; - } - } - if (command) - os << CharT{'%'}; - if (modified != CharT{}) - os << modified; - return os; -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year& y) -{ - using CT = std::chrono::seconds; - fields fds{y/0/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month& m) -{ - using CT = std::chrono::seconds; - fields fds{m/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const day& d) -{ - using CT = std::chrono::seconds; - fields fds{d/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const weekday& wd) -{ - using CT = std::chrono::seconds; - fields fds{wd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year_month& ym) -{ - using CT = std::chrono::seconds; - fields fds{ym/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month_day& md) -{ - using CT = std::chrono::seconds; - fields fds{md/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const year_month_day& ymd) -{ - using CT = std::chrono::seconds; - fields fds{ymd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const std::chrono::duration& d) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - fields fds{hh_mm_ss{d}}; - return to_stream(os, fmt, fds); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const local_time& tp, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr) -{ - using CT = typename std::common_type::type; - auto ld = floor(tp); - fields fds{year_month_day{ld}, hh_mm_ss{tp-local_seconds{ld}}}; - return to_stream(os, fmt, fds, abbrev, offset_sec); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const sys_time& tp) -{ - using std::chrono::seconds; - using CT = typename std::common_type::type; - const std::string abbrev("UTC"); - CONSTDATA seconds offset{0}; - auto sd = floor(tp); - fields fds{year_month_day{sd}, hh_mm_ss{tp-sys_seconds{sd}}}; - return to_stream(os, fmt, fds, &abbrev, &offset); -} - -// format - -template -auto -format(const std::locale& loc, const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const std::locale& loc, const std::basic_string& fmt, - const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -template -auto -format(const std::basic_string& fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -// parse - -namespace detail -{ - -template -bool -read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) -{ - auto ic = is.get(); - if (Traits::eq_int_type(ic, Traits::eof()) || - !Traits::eq(Traits::to_char_type(ic), fmt)) - { - err |= std::ios::failbit; - is.setstate(std::ios::failbit); - return false; - } - return true; -} - -template -unsigned -read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned x = 0; - unsigned count = 0; - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - (void)is.get(); - ++count; - x = 10*x + static_cast(c - '0'); - if (count == M) - break; - } - if (count < m) - is.setstate(std::ios::failbit); - return x; -} - -template -int -read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (('0' <= c && c <= '9') || c == '-' || c == '+') - { - if (c == '-' || c == '+') - (void)is.get(); - auto x = static_cast(read_unsigned(is, std::max(m, 1u), M)); - if (!is.fail()) - { - if (c == '-') - x = -x; - return x; - } - } - } - if (m > 0) - is.setstate(std::ios::failbit); - return 0; -} - -template -long double -read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned count = 0; - unsigned fcount = 0; - unsigned long long i = 0; - unsigned long long f = 0; - bool parsing_fraction = false; -#if ONLY_C_LOCALE - typename Traits::int_type decimal_point = '.'; -#else - auto decimal_point = Traits::to_int_type( - std::use_facet>(is.getloc()).decimal_point()); -#endif - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - if (Traits::eq_int_type(ic, decimal_point)) - { - decimal_point = Traits::eof(); - parsing_fraction = true; - } - else - { - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - if (!parsing_fraction) - { - i = 10*i + static_cast(c - '0'); - } - else - { - f = 10*f + static_cast(c - '0'); - ++fcount; - } - } - (void)is.get(); - if (++count == M) - break; - } - if (count < m) - { - is.setstate(std::ios::failbit); - return 0; - } - return i + f/std::pow(10.L, fcount); -} - -struct rs -{ - int& i; - unsigned m; - unsigned M; -}; - -struct ru -{ - int& i; - unsigned m; - unsigned M; -}; - -struct rld -{ - long double& i; - unsigned m; - unsigned M; -}; - -template -void -read(std::basic_istream&) -{ -} - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args); - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args); - -template -void -read(std::basic_istream& is, int a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args); - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args) -{ - // No-op if a0 == CharT{} - if (a0 != CharT{}) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - is.setstate(std::ios::failbit | std::ios::eofbit); - return; - } - if (!Traits::eq(Traits::to_char_type(ic), a0)) - { - is.setstate(std::ios::failbit); - return; - } - (void)is.get(); - } - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args) -{ - auto x = read_signed(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args) -{ - auto x = read_unsigned(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = static_cast(x); - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, int a0, Args&& ...args) -{ - if (a0 != -1) - { - auto u = static_cast(a0); - CharT buf[std::numeric_limits::digits10+2u] = {}; - auto e = buf; - do - { - *e++ = static_cast(CharT(u % 10) + CharT{'0'}); - u /= 10; - } while (u > 0); - std::reverse(buf, e); - for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) - read(is, *p); - } - if (is.rdstate() == std::ios::goodbit) - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args) -{ - auto x = read_long_double(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -inline -void -checked_set(T& value, T from, T not_a_value, std::basic_ios& is) -{ - if (!is.fail()) - { - if (value == not_a_value) - value = std::move(from); - else if (value != from) - is.setstate(std::ios::failbit); - } -} - -} // namespace detail; - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev, - std::chrono::minutes* offset) -{ - using std::numeric_limits; - using std::ios; - using std::chrono::duration; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - using detail::round_i; - typename std::basic_istream::sentry ok{is, true}; - if (ok) - { - date::detail::save_istream ss(is); - is.fill(' '); - is.flags(std::ios::skipws | std::ios::dec); - is.width(0); -#if !ONLY_C_LOCALE - auto& f = std::use_facet>(is.getloc()); - std::tm tm{}; -#endif - const CharT* command = nullptr; - auto modified = CharT{}; - auto width = -1; - - CONSTDATA int not_a_year = numeric_limits::min(); - CONSTDATA int not_a_2digit_year = 100; - CONSTDATA int not_a_century = not_a_year / 100; - CONSTDATA int not_a_month = 0; - CONSTDATA int not_a_day = 0; - CONSTDATA int not_a_hour = numeric_limits::min(); - CONSTDATA int not_a_hour_12_value = 0; - CONSTDATA int not_a_minute = not_a_hour; - CONSTDATA Duration not_a_second = Duration::min(); - CONSTDATA int not_a_doy = -1; - CONSTDATA int not_a_weekday = 8; - CONSTDATA int not_a_week_num = 100; - CONSTDATA int not_a_ampm = -1; - CONSTDATA minutes not_a_offset = minutes::min(); - - int Y = not_a_year; // c, F, Y * - int y = not_a_2digit_year; // D, x, y * - int g = not_a_2digit_year; // g * - int G = not_a_year; // G * - int C = not_a_century; // C * - int m = not_a_month; // b, B, h, m, c, D, F, x * - int d = not_a_day; // c, d, D, e, F, x * - int j = not_a_doy; // j * - int wd = not_a_weekday; // a, A, u, w * - int H = not_a_hour; // c, H, R, T, X * - int I = not_a_hour_12_value; // I, r * - int p = not_a_ampm; // p, r * - int M = not_a_minute; // c, M, r, R, T, X * - Duration s = not_a_second; // c, r, S, T, X * - int U = not_a_week_num; // U * - int V = not_a_week_num; // V * - int W = not_a_week_num; // W * - std::basic_string temp_abbrev; // Z * - minutes temp_offset = not_a_offset; // z * - - using detail::read; - using detail::rs; - using detail::ru; - using detail::rld; - using detail::checked_set; - for (; *fmt != CharT{} && !is.fail(); ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - case 'u': - case 'w': // wd: a, A, u, w - if (command) - { - int trial_wd = not_a_weekday; - if (*fmt == 'a' || *fmt == 'A') - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; -#else - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - trial_wd = i % 7; -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - } - else // *fmt == 'u' || *fmt == 'w' - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - read(is, ru{trial_wd, 1, width == -1 ? - 1u : static_cast(width)}); - if (!is.fail()) - { - if (*fmt == 'u') - { - if (!(1 <= trial_wd && trial_wd <= 7)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - else if (trial_wd == 7) - trial_wd = 0; - } - else // *fmt == 'w' - { - if (!(0 <= trial_wd && trial_wd <= 6)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - } - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - } - if (trial_wd != not_a_weekday) - checked_set(wd, trial_wd, not_a_weekday, is); - } - else // !command - read(is, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - int ttm = not_a_month; -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - ttm = tm.tm_mon + 1; - is.setstate(err); -#else - auto nm = detail::month_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - ttm = i % 12 + 1; -#endif - checked_set(m, ttm, not_a_month, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'c': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%a %b %e %T %Y" - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(wd, static_cast(i % 7), not_a_weekday, is); - ws(is); - nm = detail::month_names(); - i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(m, static_cast(i % 12 + 1), not_a_month, is); - ws(is); - int td = not_a_day; - read(is, rs{td, 1, 2}); - checked_set(d, td, not_a_day, is); - ws(is); - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH; - int tM; - long double S; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - int tY = not_a_year; - read(is, rs{tY, 1, 4u}); - checked_set(Y, tY, not_a_year, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'x': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - } - is.setstate(err); -#else - // "%m/%d/%y" - int ty = not_a_2digit_year; - int tm = not_a_month; - int td = not_a_day; - read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tm, not_a_month, is); - checked_set(d, td, not_a_day, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'X': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%T" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'C': - if (command) - { - int tC = not_a_century; -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); -#if !ONLY_C_LOCALE - } - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - auto tY = tm.tm_year + 1900; - tC = (tY >= 0 ? tY : tY-99) / 100; - } - is.setstate(err); - } -#endif - checked_set(C, tC, not_a_century, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - int tn = not_a_month; - int td = not_a_day; - int ty = not_a_2digit_year; - read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - int tY = not_a_year; - int tn = not_a_month; - int td = not_a_day; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, - CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); - checked_set(Y, tY, not_a_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'd': - case 'e': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int td = not_a_day; - read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(d, td, not_a_day, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - command = nullptr; - width = -1; - modified = CharT{}; - if ((err & ios::failbit) == 0) - checked_set(d, tm.tm_mday, not_a_day, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'H': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tH = not_a_hour; - read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(H, tH, not_a_hour, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(H, tm.tm_hour, not_a_hour, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'I': - if (command) - { - if (modified == CharT{}) - { - int tI = not_a_hour_12_value; - // reads in an hour into I, but most be in [1, 12] - read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); - if (!(1 <= tI && tI <= 12)) - is.setstate(ios::failbit); - checked_set(I, tI, not_a_hour_12_value, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - int tj = not_a_doy; - read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); - checked_set(j, tj, not_a_doy, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'M': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tM = not_a_minute; - read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(M, tM, not_a_minute, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(M, tm.tm_min, not_a_minute, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'm': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tn = not_a_month; - read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(m, tn, not_a_month, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(m, tm.tm_mon + 1, not_a_month, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'n': - case 't': - if (command) - { - if (modified == CharT{}) - { - // %n matches a single white space character - // %t matches 0 or 1 white space characters - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - ios::iostate err = ios::eofbit; - if (*fmt == 'n') - err |= ios::failbit; - is.setstate(err); - break; - } - if (isspace(ic)) - { - (void)is.get(); - } - else if (*fmt == 'n') - is.setstate(ios::failbit); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - int tp = not_a_ampm; -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_hour = 1; - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (tm.tm_hour == 1) - tp = 0; - else if (tm.tm_hour == 13) - tp = 1; - else - is.setstate(err); -#else - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - tp = static_cast(i); -#endif - checked_set(p, tp, not_a_ampm, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_hour, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%I:%M:%S %p" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S; - int tI = not_a_hour_12_value; - int tM = not_a_minute; - read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(I, tI, not_a_hour_12_value, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(p, static_cast(i), not_a_ampm, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - int tH = not_a_hour; - int tM = not_a_minute; - read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, - ru{tM, 1, 2}, CharT{'\0'}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'S': - if (command) - { - #if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S; - read(is, rld{S, 1, width == -1 ? w : static_cast(width)}); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'O'}) -#endif - { - int tY = not_a_year; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(Y, tY, not_a_year, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - int ty = not_a_2digit_year; - read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(y, ty, not_a_2digit_year, is); - } -#if !ONLY_C_LOCALE - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'g': - if (command) - { - if (modified == CharT{}) - { - int tg = not_a_2digit_year; - read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(g, tg, not_a_2digit_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'G': - if (command) - { - if (modified == CharT{}) - { - int tG = not_a_year; - read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(G, tG, not_a_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'U': - if (command) - { - if (modified == CharT{}) - { - int tU = not_a_week_num; - read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(U, tU, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'V': - if (command) - { - if (modified == CharT{}) - { - int tV = not_a_week_num; - read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(V, tV, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'W': - if (command) - { - if (modified == CharT{}) - { - int tW = not_a_week_num; - read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(W, tW, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else - read(is, *fmt); - break; - case '%': - if (command) - { - if (modified == CharT{}) - read(is, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - command = fmt; - break; - case 'z': - if (command) - { - int tH, tM; - minutes toff = not_a_offset; - bool neg = false; - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == '-') - neg = true; - } - if (modified == CharT{}) - { - read(is, rs{tH, 2, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if ('0' <= c && c <= '9') - { - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - else - { - read(is, rs{tH, 1, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == ':') - { - (void)is.get(); - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - if (neg) - toff = -toff; - checked_set(temp_offset, toff, not_a_offset, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - std::basic_string buf; - while (is.rdstate() == std::ios::goodbit) - { - auto i = is.rdbuf()->sgetc(); - if (Traits::eq_int_type(i, Traits::eof())) - { - is.setstate(ios::eofbit); - break; - } - auto wc = Traits::to_char_type(i); - auto c = static_cast(wc); - // is c a valid time zone name or abbreviation character? - if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || - c == '_' || c == '/' || c == '-' || c == '+')) - break; - buf.push_back(c); - is.rdbuf()->sbumpc(); - } - if (buf.empty()) - is.setstate(ios::failbit); - checked_set(temp_abbrev, buf, {}, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - default: - if (command) - { - if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') - { - width = static_cast(*fmt) - '0'; - while ('0' <= fmt[1] && fmt[1] <= '9') - width = 10*width + static_cast(*++fmt) - '0'; - } - else - { - if (modified == CharT{}) - read(is, CharT{'%'}, width, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else // !command - { - if (isspace(static_cast(*fmt))) - { - // space matches 0 or more white space characters - if (is.good()) - ws(is); - } - else - read(is, *fmt); - } - break; - } - } - // is.fail() || *fmt == CharT{} - if (is.rdstate() == ios::goodbit && command) - { - if (modified == CharT{}) - read(is, CharT{'%'}, width); - else - read(is, CharT{'%'}, width, modified); - } - if (!is.fail()) - { - if (y != not_a_2digit_year) - { - // Convert y and an optional C to Y - if (!(0 <= y && y <= 99)) - goto broken; - if (C == not_a_century) - { - if (Y == not_a_year) - { - if (y >= 69) - C = 19; - else - C = 20; - } - else - { - C = (Y >= 0 ? Y : Y-100) / 100; - } - } - int tY; - if (C >= 0) - tY = 100*C + y; - else - tY = 100*(C+1) - (y == 0 ? 100 : y); - if (Y != not_a_year && Y != tY) - goto broken; - Y = tY; - } - if (g != not_a_2digit_year) - { - // Convert g and an optional C to G - if (!(0 <= g && g <= 99)) - goto broken; - if (C == not_a_century) - { - if (G == not_a_year) - { - if (g >= 69) - C = 19; - else - C = 20; - } - else - { - C = (G >= 0 ? G : G-100) / 100; - } - } - int tG; - if (C >= 0) - tG = 100*C + g; - else - tG = 100*(C+1) - (g == 0 ? 100 : g); - if (G != not_a_year && G != tG) - goto broken; - G = tG; - } - if (Y < static_cast(year::min()) || Y > static_cast(year::max())) - Y = not_a_year; - bool computed = false; - if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + - (Monday-Thursday) + weeks{V-1} + - (weekday{static_cast(wd)}-Monday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + - weeks{U-1} + - (weekday{static_cast(wd)} - Sunday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + - weeks{W-1} + - (weekday{static_cast(wd)} - Monday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (j != not_a_doy && Y != not_a_year) - { - auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; - if (m == 0) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == 0) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - j = not_a_doy; - } - auto ymd = year{Y}/m/d; - if (ymd.ok()) - { - if (wd == not_a_weekday) - wd = static_cast((weekday(sys_days(ymd)) - Sunday).count()); - else if (wd != static_cast((weekday(sys_days(ymd)) - Sunday).count())) - goto broken; - if (!computed) - { - if (G != not_a_year || V != not_a_week_num) - { - sys_days sd = ymd; - auto G_trial = year_month_day{sd + days{3}}.year(); - auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + - (Monday - Thursday); - if (sd < start) - { - --G_trial; - if (V != not_a_week_num) - start = sys_days((G_trial - years{1})/December/Thursday[last]) - + (Monday - Thursday); - } - if (G != not_a_year && G != static_cast(G_trial)) - goto broken; - if (V != not_a_week_num) - { - auto V_trial = duration_cast(sd - start).count() + 1; - if (V != V_trial) - goto broken; - } - } - if (U != not_a_week_num) - { - auto start = sys_days(Sunday[1]/January/ymd.year()); - auto U_trial = floor(sys_days(ymd) - start).count() + 1; - if (U != U_trial) - goto broken; - } - if (W != not_a_week_num) - { - auto start = sys_days(Monday[1]/January/ymd.year()); - auto W_trial = floor(sys_days(ymd) - start).count() + 1; - if (W != W_trial) - goto broken; - } - } - } - fds.ymd = ymd; - if (I != not_a_hour_12_value) - { - if (!(1 <= I && I <= 12)) - goto broken; - if (p != not_a_ampm) - { - // p is in [0, 1] == [AM, PM] - // Store trial H in I - if (I == 12) - --p; - I += p*12; - // Either set H from I or make sure H and I are consistent - if (H == not_a_hour) - H = I; - else if (I != H) - goto broken; - } - else // p == not_a_ampm - { - // if H, make sure H and I could be consistent - if (H != not_a_hour) - { - if (I == 12) - { - if (H != 0 && H != 12) - goto broken; - } - else if (!(I == H || I == H+12)) - { - goto broken; - } - } - else // I is ambiguous, AM or PM? - goto broken; - } - } - if (H != not_a_hour) - { - fds.has_tod = true; - fds.tod = hh_mm_ss{hours{H}}; - } - if (M != not_a_minute) - { - fds.has_tod = true; - fds.tod.m_ = minutes{M}; - } - if (s != not_a_second) - { - fds.has_tod = true; - fds.tod.s_ = detail::decimal_format_seconds{s}; - } - if (j != not_a_doy) - { - fds.has_tod = true; - fds.tod.h_ += hours{days{j}}; - } - if (wd != not_a_weekday) - fds.wd = weekday{static_cast(wd)}; - if (abbrev != nullptr) - *abbrev = std::move(temp_abbrev); - if (offset != nullptr && temp_offset != not_a_offset) - *offset = temp_offset; - } - return is; - } -broken: - is.setstate(ios::failbit); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year& y, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.year().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - y = fds.ymd.year(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month& m, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - m = fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, day& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, weekday& wd, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.wd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - wd = fds.wd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year_month& ym, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ym = fds.ymd.year()/fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month_day& md, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - md = fds.ymd.month()/fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - year_month_day& ymd, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ymd = fds.ymd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - sys_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - std::chrono::minutes offset_local{}; - auto offptr = offset ? offset : &offset_local; - fields fds{}; - fds.has_tod = true; - from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - local_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - fields fds{}; - fds.has_tod = true; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - std::chrono::duration& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - fields fds{}; - from_stream(is, fmt, fds, abbrev, offset); - if (!fds.has_tod) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = std::chrono::duration_cast(fds.tod.to_duration()); - return is; -} - -template , - class Alloc = std::allocator> -struct parse_manip -{ - const std::basic_string format_; - Parsable& tp_; - std::basic_string* abbrev_; - std::chrono::minutes* offset_; - -public: - parse_manip(std::basic_string format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(std::move(format)) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - -}; - -template -std::basic_istream& -operator>>(std::basic_istream& is, - const parse_manip& x) -{ - return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp) - -> decltype(from_stream(std::declval&>(), - format.c_str(), tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev) - -> decltype(from_stream(std::declval&>(), - format.c_str(), tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::chrono::minutes& offset) - -> decltype(from_stream(std::declval&>(), - format.c_str(), tp, - std::declval*>(), - &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(from_stream(std::declval&>(), - format.c_str(), tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// const CharT* formats - -template -inline -auto -parse(const CharT* format, Parsable& tp) - -> decltype(from_stream(std::declval&>(), format, tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::basic_string& abbrev) - -> decltype(from_stream(std::declval&>(), format, - tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) - -> decltype(from_stream(std::declval&>(), format, - tp, std::declval*>(), &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(from_stream(std::declval&>(), format, - tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// duration streaming - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, - const std::chrono::duration& d) -{ - return os << detail::make_string::from(d.count()) + - detail::get_units(typename Period::type{}); -} - -} // namespace date - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - -#endif // DATE_H diff --git a/third_party/date/include/date/ios.h b/third_party/date/include/date/ios.h deleted file mode 100644 index ee54b9d08..000000000 --- a/third_party/date/include/date/ios.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// ios.h -// DateTimeLib -// -// The MIT License (MIT) -// -// Copyright (c) 2016 Alexander Kormanovsky -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef ios_hpp -#define ios_hpp - -#if __APPLE__ -# include -# if TARGET_OS_IPHONE -# include - - namespace date - { - namespace iOSUtils - { - - std::string get_tzdata_path(); - std::string get_current_timezone(); - - } // namespace iOSUtils - } // namespace date - -# endif // TARGET_OS_IPHONE -#else // !__APPLE__ -# define TARGET_OS_IPHONE 0 -#endif // !__APPLE__ -#endif // ios_hpp diff --git a/third_party/date/include/date/islamic.h b/third_party/date/include/date/islamic.h deleted file mode 100644 index 82ed6597b..000000000 --- a/third_party/date/include/date/islamic.h +++ /dev/null @@ -1,3031 +0,0 @@ -#ifndef ISLAMIC_H -#define ISLAMIC_H - -// The MIT License (MIT) -// -// Copyright (c) 2016 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#include "date.h" - -namespace islamic -{ - -// durations - -using days = date::days; - -using weeks = date::weeks; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -using sys_days = date::sys_days; -using local_days = date::local_days; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD14 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - CONSTCD11 weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 islamic::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - islamic::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const islamic::weekday& wd) NOEXCEPT; - - CONSTCD11 islamic::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -// year_month - -class year_month -{ - islamic::year y_; - islamic::month m_; - -public: - CONSTCD11 year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT; - - CONSTCD11 islamic::year year() const NOEXCEPT; - CONSTCD11 islamic::month month() const NOEXCEPT; - - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - islamic::month m_; - islamic::day d_; - -public: - CONSTCD11 month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT; - - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - islamic::month m_; - -public: - CONSTCD11 explicit month_day_last(const islamic::month& m) NOEXCEPT; - - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - islamic::month m_; - islamic::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const islamic::month& m, - const islamic::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - islamic::month m_; - islamic::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const islamic::month& m, - const islamic::weekday_last& wd) NOEXCEPT; - - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - islamic::year y_; - islamic::month m_; - islamic::day d_; - -public: - CONSTCD11 year_month_day(const islamic::year& y, const islamic::month& m, - const islamic::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 islamic::year year() const NOEXCEPT; - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - islamic::year y_; - islamic::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const islamic::year& y, - const islamic::month_day_last& mdl) NOEXCEPT; - - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 islamic::year year() const NOEXCEPT; - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 islamic::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - islamic::year y_; - islamic::month m_; - islamic::weekday_indexed wdi_; - -public: - CONSTCD11 year_month_weekday(const islamic::year& y, const islamic::month& m, - const islamic::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 islamic::year year() const NOEXCEPT; - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - islamic::year y_; - islamic::month m_; - islamic::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const islamic::year& y, const islamic::month& m, - const islamic::weekday_last& wdl) NOEXCEPT; - - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 islamic::year year() const NOEXCEPT; - CONSTCD11 islamic::month month() const NOEXCEPT; - CONSTCD11 islamic::weekday weekday() const NOEXCEPT; - CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 islamic::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 islamic::year operator "" _y(unsigned long long y) NOEXCEPT; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -//----------------+ -// Implementation | -//----------------+ - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) - 1 + y.count(); - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - switch (static_cast(m)) - { - case 1: - os << "Muharram"; - break; - case 2: - os << "Safar"; - break; - case 3: - os << "Rabi' al-awwal"; - break; - case 4: - os << "Rabi' al-thani"; - break; - case 5: - os << "Jumada al-awwal"; - break; - case 6: - os << "Jumada al-Thani"; - break; - case 7: - os << "Rajab"; - break; - case 8: - os << "Sha'ban"; - break; - case 9: - os << "Ramadan"; - break; - case 10: - os << "Shawwal"; - break; - case 11: - os << "Dhu al-Qi'dah"; - break; - case 12: - os << "Dhu al-Hijjah"; - break; - default: - os << static_cast(m) << " is not a valid month"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} - -CONSTCD14 -inline -bool -year::is_leap() const NOEXCEPT -{ - int y = y_ - 1; - const int era = (y >= 0 ? y : y-29) / 30; - const unsigned yoe = static_cast(y - era * 30); - switch (yoe) - { - case 1: - case 4: - case 6: - case 9: - case 12: - case 15: - case 17: - case 20: - case 23: - case 25: - case 28: - return true; - default: - return false; - } -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - return year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - return year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - return static_cast(static_cast( - z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd)) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return static_cast(wd_); -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 0: - os << "al-Aḥad"; - break; - case 1: - os << "al-Ithnayn"; - break; - case 2: - os << "ath-Thulāthā’"; - break; - case 3: - os << "al-Arba‘ā’"; - break; - case 4: - os << "al-Khamīs"; - break; - case 5: - os << "al-Jum‘ah"; - break; - case 6: - os << "as-Sabt"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -islamic::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return islamic::day{static_cast(d)}; -} - -CONSTCD11 -inline -islamic::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return islamic::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA islamic::last_spec last{}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return islamic::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd))) - , index_(static_cast(index)) - {} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return os << wdi.weekday() << '[' << wdi.index() << ']'; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline islamic::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const islamic::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - return os << wdl.weekday() << "[last]"; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - return os << ym.year() << '/' << ym.month(); -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline islamic::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline islamic::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA islamic::day d[] = - {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 30_d}; - return m_.ok() && 1_d <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - return os << md.month() << '/' << md.day(); -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const islamic::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - return os << mdl.month() << "/last"; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const islamic::month& m, - const islamic::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - return os << mwd.month() << '/' << mwd.weekday_indexed(); -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const islamic::month& m, - const islamic::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - return os << mwdl.month() << '/' << mwdl.weekday_last(); -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const islamic::year& y, - const islamic::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA islamic::day d[] = - {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d}; - return month() != islamic::month(12) || !y_.is_leap() ? - d[static_cast(month())-1] : 30_d; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - return os << ymdl.year() << '/' << ymdl.month_day_last(); -} - -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const islamic::year& y, const islamic::month& m, - const islamic::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - 1; - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-29) / 30; - auto const yoe = static_cast(y - era * 30); // [0, 29] - auto const doy = 29*(m-1) + m/2 + d-1; // [0, 354] - auto const doe = yoe * 354 + (11*(yoe+1)+3)/30 + doy; // [0, 10630] - return days{era * 10631 + static_cast(doe) - 492148}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return 1_d <= d_ && d_ <= (y_/m_/last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os << ymd.year() << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os << ymd.day(); - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 492148; - auto const era = (z >= 0 ? z : z - 10630) / 10631; - auto const doe = static_cast(z - era * 10631); // [0, 10630] - auto const yoe = (30*doe + 10646)/10631 - 1; // [0, 29] - auto const y = static_cast(yoe) + era * 30 + 1; - auto const doy = doe - (yoe * 354 + (11*(yoe+1)+3)/30); // [0, 354] - auto const m = (11*doy + 330) / 325; // [1, 12] - auto const d = doy - (29*(m-1) + m/2) + 1; // [1, 30] - return year_month_day{islamic::year{y}, islamic::month(m), islamic::day(d)}; -} - -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const islamic::year& y, const islamic::month& m, - const islamic::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - islamic::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = islamic::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - islamic::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - return os << ymwdi.year() << '/' << ymwdi.month() - << '/' << ymwdi.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const islamic::year& y, - const islamic::month& m, - const islamic::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (islamic::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -} // namespace islamic - -#endif // ISLAMIC_H diff --git a/third_party/date/include/date/iso_week.h b/third_party/date/include/date/iso_week.h deleted file mode 100644 index 4a0a4a9b9..000000000 --- a/third_party/date/include/date/iso_week.h +++ /dev/null @@ -1,1751 +0,0 @@ -#ifndef ISO_WEEK_H -#define ISO_WEEK_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "date.h" - -#include - -namespace iso_week -{ - -// y/wn/wd -// wn/wd/y -// wd/wn/y - -using days = date::days; -using weeks = date::weeks; -using years = date::years; - -// time_point - -using sys_days = date::sys_days; -using local_days = date::local_days; - -// types - -struct last_week -{ - explicit last_week() = default; -}; - -class weekday; -class weeknum; -class year; - -class year_weeknum; -class year_lastweek; -class weeknum_weekday; -class lastweek_weekday; - -class year_weeknum_weekday; -class year_lastweek_weekday; - -// date composition operators - -CONSTCD11 year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT; -CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT; - -CONSTCD11 year_lastweek operator/(const year& y, last_week wn) NOEXCEPT; - -CONSTCD11 weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT; -CONSTCD11 weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT; -CONSTCD11 weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT; -CONSTCD11 weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT; - -CONSTCD11 lastweek_weekday operator/(const last_week& wn, const weekday& wd) NOEXCEPT; -CONSTCD11 lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT; -CONSTCD11 lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT; - -CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT; -CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT; -CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT; -CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT; - -CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT; -CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT; - -CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT; -CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT; - -// weekday - -class weekday -{ - unsigned char wd_; -public: - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - CONSTCD11 weekday(date::weekday wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - weekday& operator++() NOEXCEPT; - weekday operator++(int) NOEXCEPT; - weekday& operator--() NOEXCEPT; - weekday operator--(int) NOEXCEPT; - - weekday& operator+=(const days& d) NOEXCEPT; - weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 operator date::weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; - static CONSTCD11 unsigned char to_iso_encoding(unsigned char) NOEXCEPT; - static CONSTCD11 unsigned from_iso_encoding(unsigned) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// year - -class year -{ - short y_; - -public: - explicit CONSTCD11 year(int y) NOEXCEPT; - - year& operator++() NOEXCEPT; - year operator++(int) NOEXCEPT; - year& operator--() NOEXCEPT; - year operator--(int) NOEXCEPT; - - year& operator+=(const years& y) NOEXCEPT; - year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weeknum - -class weeknum -{ - unsigned char wn_; - -public: - explicit CONSTCD11 weeknum(unsigned wn) NOEXCEPT; - - weeknum& operator++() NOEXCEPT; - weeknum operator++(int) NOEXCEPT; - weeknum& operator--() NOEXCEPT; - weeknum operator--(int) NOEXCEPT; - - weeknum& operator+=(const weeks& y) NOEXCEPT; - weeknum& operator-=(const weeks& y) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT; -CONSTCD11 bool operator< (const weeknum& x, const weeknum& y) NOEXCEPT; -CONSTCD11 bool operator> (const weeknum& x, const weeknum& y) NOEXCEPT; -CONSTCD11 bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT; -CONSTCD11 bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT; - -CONSTCD11 weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT; -CONSTCD11 weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT; -CONSTCD11 weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT; -CONSTCD11 weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weeknum& wn); - -// year_weeknum - -class year_weeknum -{ - iso_week::year y_; - iso_week::weeknum wn_; - -public: - CONSTCD11 year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT; - - CONSTCD11 iso_week::year year() const NOEXCEPT; - CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; - - year_weeknum& operator+=(const years& dy) NOEXCEPT; - year_weeknum& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; - -CONSTCD11 year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT; -CONSTCD11 year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_weeknum& ym); - -// year_lastweek - -class year_lastweek -{ - iso_week::year y_; - -public: - CONSTCD11 explicit year_lastweek(const iso_week::year& y) NOEXCEPT; - - CONSTCD11 iso_week::year year() const NOEXCEPT; - CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; - - year_lastweek& operator+=(const years& dy) NOEXCEPT; - year_lastweek& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; - -CONSTCD11 year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT; -CONSTCD11 year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_lastweek& ym); - -// weeknum_weekday - -class weeknum_weekday -{ - iso_week::weeknum wn_; - iso_week::weekday wd_; - -public: - CONSTCD11 weeknum_weekday(const iso_week::weeknum& wn, - const iso_week::weekday& wd) NOEXCEPT; - - CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; - CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator< (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator> (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weeknum_weekday& md); - -// lastweek_weekday - -class lastweek_weekday -{ - iso_week::weekday wd_; - -public: - CONSTCD11 explicit lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT; - - CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator< (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator> (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const lastweek_weekday& md); - -// year_lastweek_weekday - -class year_lastweek_weekday -{ - iso_week::year y_; - iso_week::weekday wd_; - -public: - CONSTCD11 year_lastweek_weekday(const iso_week::year& y, - const iso_week::weekday& wd) NOEXCEPT; - - year_lastweek_weekday& operator+=(const years& y) NOEXCEPT; - year_lastweek_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 iso_week::year year() const NOEXCEPT; - CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; - CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; - -CONSTCD11 year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; -CONSTCD11 year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT; -CONSTCD11 year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd); - -// class year_weeknum_weekday - -class year_weeknum_weekday -{ - iso_week::year y_; - iso_week::weeknum wn_; - iso_week::weekday wd_; - -public: - CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, - const iso_week::weekday& wd) NOEXCEPT; - CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; - CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_weeknum_weekday(const local_days& dp) NOEXCEPT; - - year_weeknum_weekday& operator+=(const years& y) NOEXCEPT; - year_weeknum_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 iso_week::year year() const NOEXCEPT; - CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; - CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_weeknum_weekday from_days(days dp) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; - -CONSTCD11 year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; -CONSTCD11 year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT; -CONSTCD11 year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd); - -//----------------+ -// Implementation | -//----------------+ - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::to_iso_encoding(unsigned char z) NOEXCEPT -{ - return z != 0 ? z : (unsigned char)7; -} - -CONSTCD11 -inline -unsigned -weekday::from_iso_encoding(unsigned z) NOEXCEPT -{ - return z != 7 ? z : 0u; -} - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - return to_iso_encoding(static_cast(static_cast( - z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6))); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd)) - {} - -CONSTCD11 -inline -weekday::weekday(date::weekday wd) NOEXCEPT - : wd_(wd.iso_encoding()) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 8) wd_ = 1; return *this;} -inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 1) wd_ = 7; return *this;} -inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return wd_; -} - -CONSTCD11 -inline -weekday::operator date::weekday() const NOEXCEPT -{ - return date::weekday{from_iso_encoding(unsigned{wd_})}; -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return 1 <= wd_ && wd_ <= 7;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x) - 1u) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7) + 1u}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 7: - os << "Sun"; - break; - case 1: - os << "Mon"; - break; - case 2: - os << "Tue"; - break; - case 3: - os << "Wed"; - break; - case 4: - os << "Thu"; - break; - case 5: - os << "Fri"; - break; - case 6: - os << "Sat"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -inline year& year::operator++() NOEXCEPT {++y_; return *this;} -inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -inline year& year::operator--() NOEXCEPT {--y_; return *this;} -inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - using std::chrono::duration_cast; - static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); - static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); - return sizeof(minutes)*CHAR_BIT < 34 ? - year{1970} + duration_cast(minutes::min()) : - year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - using std::chrono::duration_cast; - static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); - static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); - return sizeof(minutes)*CHAR_BIT < 34 ? - year{1969} + duration_cast(minutes::max()) : - year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -iso_week::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return iso_week::year(static_cast(y)); -} - -CONSTCD11 -inline -iso_week::weeknum -operator "" _w(unsigned long long wn) NOEXCEPT -{ - return iso_week::weeknum(static_cast(wn)); -} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA iso_week::last_week last{}; - -CONSTDATA iso_week::weekday sun{7u}; -CONSTDATA iso_week::weekday mon{1u}; -CONSTDATA iso_week::weekday tue{2u}; -CONSTDATA iso_week::weekday wed{3u}; -CONSTDATA iso_week::weekday thu{4u}; -CONSTDATA iso_week::weekday fri{5u}; -CONSTDATA iso_week::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weeknum - -CONSTCD11 -inline -weeknum::weeknum(unsigned wn) NOEXCEPT - : wn_(static_cast(wn)) - {} - -inline weeknum& weeknum::operator++() NOEXCEPT {++wn_; return *this;} -inline weeknum weeknum::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -inline weeknum& weeknum::operator--() NOEXCEPT {--wn_; return *this;} -inline weeknum weeknum::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -inline -weeknum& -weeknum::operator+=(const weeks& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -inline -weeknum& -weeknum::operator-=(const weeks& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline weeknum::operator unsigned() const NOEXCEPT {return wn_;} -CONSTCD11 inline bool weeknum::ok() const NOEXCEPT {return 1 <= wn_ && wn_ <= 53;} - -CONSTCD11 -inline -bool -operator==(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -weeks -operator-(const weeknum& x, const weeknum& y) NOEXCEPT -{ - return weeks{static_cast(static_cast(x)) - - static_cast(static_cast(y))}; -} - -CONSTCD11 -inline -weeknum -operator+(const weeknum& x, const weeks& y) NOEXCEPT -{ - return weeknum{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -weeknum -operator+(const weeks& x, const weeknum& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -weeknum -operator-(const weeknum& x, const weeks& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weeknum& wn) -{ - date::detail::save_ostream _(os); - os << 'W'; - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(wn); - return os; -} - -// year_weeknum - -CONSTCD11 -inline -year_weeknum::year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT - : y_(y) - , wn_(wn) - {} - -CONSTCD11 inline year year_weeknum::year() const NOEXCEPT {return y_;} -CONSTCD11 inline weeknum year_weeknum::weeknum() const NOEXCEPT {return wn_;} -CONSTCD11 inline bool year_weeknum::ok() const NOEXCEPT -{ - return y_.ok() && 1u <= static_cast(wn_) && wn_ <= (y_/last).weeknum(); -} - -inline -year_weeknum& -year_weeknum::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -inline -year_weeknum& -year_weeknum::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return x.year() == y.year() && x.weeknum() == y.weeknum(); -} - -CONSTCD11 -inline -bool -operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.weeknum() < y.weeknum())); -} - -CONSTCD11 -inline -bool -operator>(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -year_weeknum -operator+(const year_weeknum& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.weeknum(); -} - -CONSTCD11 -inline -year_weeknum -operator+(const years& dy, const year_weeknum& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_weeknum -operator-(const year_weeknum& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_weeknum& ywn) -{ - return os << ywn.year() << '-' << ywn.weeknum(); -} - - -// year_lastweek - -CONSTCD11 -inline -year_lastweek::year_lastweek(const iso_week::year& y) NOEXCEPT - : y_(y) - {} - -CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;} - -CONSTCD14 -inline -weeknum -year_lastweek::weeknum() const NOEXCEPT -{ - const auto y = date::year{static_cast(y_)}; - const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]); - const auto s1 = sys_days(y/12/date::thu[date::last]); - return iso_week::weeknum(static_cast(date::trunc(s1-s0).count())); -} - -CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();} - -inline -year_lastweek& -year_lastweek::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -inline -year_lastweek& -year_lastweek::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return x.year() == y.year(); -} - -CONSTCD11 -inline -bool -operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return x.year() < y.year(); -} - -CONSTCD11 -inline -bool -operator>(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -year_lastweek -operator+(const year_lastweek& ym, const years& dy) NOEXCEPT -{ - return year_lastweek{ym.year() + dy}; -} - -CONSTCD11 -inline -year_lastweek -operator+(const years& dy, const year_lastweek& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_lastweek -operator-(const year_lastweek& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_lastweek& ywn) -{ - return os << ywn.year() << "-W last"; -} - -// weeknum_weekday - -CONSTCD11 -inline -weeknum_weekday::weeknum_weekday(const iso_week::weeknum& wn, - const iso_week::weekday& wd) NOEXCEPT - : wn_(wn) - , wd_(wd) - {} - -CONSTCD11 inline weeknum weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} -CONSTCD11 inline weekday weeknum_weekday::weekday() const NOEXCEPT {return wd_;} - -CONSTCD14 -inline -bool -weeknum_weekday::ok() const NOEXCEPT -{ - return wn_.ok() && wd_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return x.weeknum() < y.weeknum() ? true - : (x.weeknum() > y.weeknum() ? false - : (static_cast(x.weekday()) < static_cast(y.weekday()))); -} - -CONSTCD11 -inline -bool -operator>(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weeknum_weekday& md) -{ - return os << md.weeknum() << '-' << md.weekday(); -} - -// lastweek_weekday - -CONSTCD11 -inline -lastweek_weekday::lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT - : wd_(wd) - {} - -CONSTCD11 inline weekday lastweek_weekday::weekday() const NOEXCEPT {return wd_;} - -CONSTCD14 -inline -bool -lastweek_weekday::ok() const NOEXCEPT -{ - return wd_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return static_cast(x.weekday()) < static_cast(y.weekday()); -} - -CONSTCD11 -inline -bool -operator>(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const lastweek_weekday& md) -{ - return os << "W last-" << md.weekday(); -} - -// year_lastweek_weekday - -CONSTCD11 -inline -year_lastweek_weekday::year_lastweek_weekday(const iso_week::year& y, - const iso_week::weekday& wd) NOEXCEPT - : y_(y) - , wd_(wd) - {} - -inline -year_lastweek_weekday& -year_lastweek_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -inline -year_lastweek_weekday& -year_lastweek_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_lastweek_weekday::year() const NOEXCEPT {return y_;} - -CONSTCD14 -inline -weeknum -year_lastweek_weekday::weeknum() const NOEXCEPT -{ - return (y_ / last).weeknum(); -} - -CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return wd_;} - -CONSTCD14 -inline -year_lastweek_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days(date::year{static_cast(y_)}/date::dec/date::thu[date::last]) - + (sun - thu) - (sun - wd_); -} - -CONSTCD14 -inline -year_lastweek_weekday::operator local_days() const NOEXCEPT -{ - return local_days(date::year{static_cast(y_)}/date::dec/date::thu[date::last]) - + (sun - thu) - (sun - wd_); -} - -CONSTCD11 -inline -bool -year_lastweek_weekday::ok() const NOEXCEPT -{ - return y_.ok() && wd_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (static_cast(x.weekday()) < static_cast(y.weekday()))); -} - -CONSTCD11 -inline -bool -operator>(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -year_lastweek_weekday -operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT -{ - return (ywnwd.year() + y) / last / ywnwd.weekday(); -} - -CONSTCD11 -inline -year_lastweek_weekday -operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT -{ - return ywnwd + y; -} - -CONSTCD11 -inline -year_lastweek_weekday -operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT -{ - return ywnwd + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd) -{ - return os << ywnwd.year() << "-W last-" << ywnwd.weekday(); -} - -// year_weeknum_weekday - -CONSTCD11 -inline -year_weeknum_weekday::year_weeknum_weekday(const iso_week::year& y, - const iso_week::weeknum& wn, - const iso_week::weekday& wd) NOEXCEPT - : y_(y) - , wn_(wn) - , wd_(wd) - {} - -CONSTCD14 -inline -year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT - : y_(ylwwd.year()) - , wn_(ylwwd.weeknum()) - , wd_(ylwwd.weekday()) - {} - -CONSTCD14 -inline -year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT - : year_weeknum_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_weeknum_weekday::year_weeknum_weekday(const local_days& dp) NOEXCEPT - : year_weeknum_weekday(from_days(dp.time_since_epoch())) - {} - -inline -year_weeknum_weekday& -year_weeknum_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -inline -year_weeknum_weekday& -year_weeknum_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_weeknum_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline weeknum year_weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} -CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return wd_;} - -CONSTCD14 -inline -year_weeknum_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days(date::year{static_cast(y_)-1}/date::dec/date::thu[date::last]) - + (date::mon - date::thu) + weeks{static_cast(wn_)-1} + (wd_ - mon); -} - -CONSTCD14 -inline -year_weeknum_weekday::operator local_days() const NOEXCEPT -{ - return local_days(date::year{static_cast(y_)-1}/date::dec/date::thu[date::last]) - + (date::mon - date::thu) + weeks{static_cast(wn_)-1} + (wd_ - mon); -} - -CONSTCD14 -inline -bool -year_weeknum_weekday::ok() const NOEXCEPT -{ - return y_.ok() && wd_.ok() && iso_week::weeknum{1u} <= wn_ && wn_ <= year_lastweek{y_}.weeknum(); -} - -CONSTCD14 -inline -year_weeknum_weekday -year_weeknum_weekday::from_days(days d) NOEXCEPT -{ - const auto dp = sys_days{d}; - const auto wd = iso_week::weekday{dp}; - auto y = date::year_month_day{dp + days{3}}.year(); - auto start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); - if (dp < start) - { - --y; - start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); - } - const auto wn = iso_week::weeknum( - static_cast(date::trunc(dp - start).count() + 1)); - return {iso_week::year(static_cast(y)), wn, wd}; -} - -CONSTCD11 -inline -bool -operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.weeknum() < y.weeknum() ? true - : (x.weeknum() > y.weeknum() ? false - : (static_cast(x.weekday()) < static_cast(y.weekday()))))); -} - -CONSTCD11 -inline -bool -operator>(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -year_weeknum_weekday -operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT -{ - return (ywnwd.year() + y) / ywnwd.weeknum() / ywnwd.weekday(); -} - -CONSTCD11 -inline -year_weeknum_weekday -operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT -{ - return ywnwd + y; -} - -CONSTCD11 -inline -year_weeknum_weekday -operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT -{ - return ywnwd + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd) -{ - return os << ywnwd.year() << '-' << ywnwd.weeknum() << '-' << ywnwd.weekday(); -} - -// date composition operators - -CONSTCD11 -inline -year_weeknum -operator/(const year& y, const weeknum& wn) NOEXCEPT -{ - return {y, wn}; -} - -CONSTCD11 -inline -year_weeknum -operator/(const year& y, int wn) NOEXCEPT -{ - return y/weeknum(static_cast(wn)); -} - -CONSTCD11 -inline -year_lastweek -operator/(const year& y, last_week) NOEXCEPT -{ - return year_lastweek{y}; -} - -CONSTCD11 -inline -weeknum_weekday -operator/(const weeknum& wn, const weekday& wd) NOEXCEPT -{ - return {wn, wd}; -} - -CONSTCD11 -inline -weeknum_weekday -operator/(const weeknum& wn, int wd) NOEXCEPT -{ - return wn/weekday{static_cast(wd)}; -} - -CONSTCD11 -inline -weeknum_weekday -operator/(const weekday& wd, const weeknum& wn) NOEXCEPT -{ - return wn/wd; -} - -CONSTCD11 -inline -weeknum_weekday -operator/(const weekday& wd, int wn) NOEXCEPT -{ - return weeknum{static_cast(wn)}/wd; -} - -CONSTCD11 -inline -lastweek_weekday -operator/(const last_week&, const weekday& wd) NOEXCEPT -{ - return lastweek_weekday{wd}; -} - -CONSTCD11 -inline -lastweek_weekday -operator/(const last_week& wn, int wd) NOEXCEPT -{ - return wn / weekday{static_cast(wd)}; -} - -CONSTCD11 -inline -lastweek_weekday -operator/(const weekday& wd, const last_week& wn) NOEXCEPT -{ - return wn / wd; -} - -CONSTCD11 -inline -year_weeknum_weekday -operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT -{ - return {ywn.year(), ywn.weeknum(), wd}; -} - -CONSTCD11 -inline -year_weeknum_weekday -operator/(const year_weeknum& ywn, int wd) NOEXCEPT -{ - return ywn / weekday(static_cast(wd)); -} - -CONSTCD11 -inline -year_weeknum_weekday -operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT -{ - return {y, wnwd.weeknum(), wnwd.weekday()}; -} - -CONSTCD11 -inline -year_weeknum_weekday -operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT -{ - return wnwd / year{y}; -} - -CONSTCD11 -inline -year_lastweek_weekday -operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT -{ - return {ylw.year(), wd}; -} - -CONSTCD11 -inline -year_lastweek_weekday -operator/(const year_lastweek& ylw, int wd) NOEXCEPT -{ - return ylw / weekday(static_cast(wd)); -} - -CONSTCD11 -inline -year_lastweek_weekday -operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT -{ - return {y, lwwd.weekday()}; -} - -CONSTCD11 -inline -year_lastweek_weekday -operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT -{ - return lwwd / year{y}; -} - -} // namespace iso_week - -#endif // ISO_WEEK_H diff --git a/third_party/date/include/date/julian.h b/third_party/date/include/date/julian.h deleted file mode 100644 index d909d69b1..000000000 --- a/third_party/date/include/date/julian.h +++ /dev/null @@ -1,3052 +0,0 @@ -#ifndef JULIAN_H -#define JULIAN_H - -// The MIT License (MIT) -// -// Copyright (c) 2016 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#include "date.h" - -namespace julian -{ - -// durations - -using days = date::days; - -using weeks = date::weeks; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -using sys_days = date::sys_days; -using local_days = date::local_days; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - CONSTCD11 weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - julian::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const julian::weekday& wd) NOEXCEPT; - - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -// year_month - -class year_month -{ - julian::year y_; - julian::month m_; - -public: - CONSTCD11 year_month(const julian::year& y, const julian::month& m) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - julian::month m_; - julian::day d_; - -public: - CONSTCD11 month_day(const julian::month& m, const julian::day& d) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - julian::month m_; - -public: - CONSTCD11 explicit month_day_last(const julian::month& m) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - julian::month m_; - julian::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - julian::month m_; - julian::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const julian::month& m, - const julian::weekday_last& wd) NOEXCEPT; - - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - julian::year y_; - julian::month m_; - julian::day d_; - -public: - CONSTCD11 year_month_day(const julian::year& y, const julian::month& m, - const julian::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - julian::year y_; - julian::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const julian::year& y, - const julian::month_day_last& mdl) NOEXCEPT; - - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 julian::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - julian::year y_; - julian::month m_; - julian::weekday_indexed wdi_; - -public: - CONSTCD11 year_month_weekday(const julian::year& y, const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - julian::year y_; - julian::month m_; - julian::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const julian::year& y, const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT; - - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 julian::year year() const NOEXCEPT; - CONSTCD11 julian::month month() const NOEXCEPT; - CONSTCD11 julian::weekday weekday() const NOEXCEPT; - CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT; - -// CONSTDATA julian::month jan{1}; -// CONSTDATA julian::month feb{2}; -// CONSTDATA julian::month mar{3}; -// CONSTDATA julian::month apr{4}; -// CONSTDATA julian::month may{5}; -// CONSTDATA julian::month jun{6}; -// CONSTDATA julian::month jul{7}; -// CONSTDATA julian::month aug{8}; -// CONSTDATA julian::month sep{9}; -// CONSTDATA julian::month oct{10}; -// CONSTDATA julian::month nov{11}; -// CONSTDATA julian::month dec{12}; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -//----------------+ -// Implementation | -//----------------+ - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) - 1 + y.count(); - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - switch (static_cast(m)) - { - case 1: - os << "Jan"; - break; - case 2: - os << "Feb"; - break; - case 3: - os << "Mar"; - break; - case 4: - os << "Apr"; - break; - case 5: - os << "May"; - break; - case 6: - os << "Jun"; - break; - case 7: - os << "Jul"; - break; - case 8: - os << "Aug"; - break; - case 9: - os << "Sep"; - break; - case 10: - os << "Oct"; - break; - case 11: - os << "Nov"; - break; - case 12: - os << "Dec"; - break; - default: - os << static_cast(m) << " is not a valid month"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0; -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - return year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - return year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - return static_cast(static_cast( - z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd)) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return static_cast(wd_); -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 0: - os << "Sun"; - break; - case 1: - os << "Mon"; - break; - case 2: - os << "Tue"; - break; - case 3: - os << "Wed"; - break; - case 4: - os << "Thu"; - break; - case 5: - os << "Fri"; - break; - case 6: - os << "Sat"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -julian::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return julian::day{static_cast(d)}; -} - -CONSTCD11 -inline -julian::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return julian::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA julian::last_spec last{}; - -CONSTDATA julian::month jan{1}; -CONSTDATA julian::month feb{2}; -CONSTDATA julian::month mar{3}; -CONSTDATA julian::month apr{4}; -CONSTDATA julian::month may{5}; -CONSTDATA julian::month jun{6}; -CONSTDATA julian::month jul{7}; -CONSTDATA julian::month aug{8}; -CONSTDATA julian::month sep{9}; -CONSTDATA julian::month oct{10}; -CONSTDATA julian::month nov{11}; -CONSTDATA julian::month dec{12}; - -CONSTDATA julian::weekday sun{0u}; -CONSTDATA julian::weekday mon{1u}; -CONSTDATA julian::weekday tue{2u}; -CONSTDATA julian::weekday wed{3u}; -CONSTDATA julian::weekday thu{4u}; -CONSTDATA julian::weekday fri{5u}; -CONSTDATA julian::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return julian::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd))) - , index_(static_cast(index)) - {} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return os << wdi.weekday() << '[' << wdi.index() << ']'; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline julian::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const julian::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - return os << wdl.weekday() << "[last]"; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const julian::year& y, const julian::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - return os << ym.year() << '/' << ym.month(); -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const julian::month& m, const julian::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline julian::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline julian::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA julian::day d[] = { - julian::day(31), julian::day(29), julian::day(31), julian::day(30), - julian::day(31), julian::day(30), julian::day(31), julian::day(31), - julian::day(30), julian::day(31), julian::day(30), julian::day(31) - }; - return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - return os << md.month() << '/' << md.day(); -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const julian::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - return os << mdl.month() << "/last"; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const julian::month& m, - const julian::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - return os << mwd.month() << '/' << mwd.weekday_indexed(); -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - return os << mwdl.month() << '/' << mwdl.weekday_last(); -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const julian::year& y, - const julian::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA julian::day d[] = { - julian::day(31), julian::day(28), julian::day(31), julian::day(30), - julian::day(31), julian::day(30), julian::day(31), julian::day(31), - julian::day(30), julian::day(31), julian::day(30), julian::day(31) - }; - return month() != feb || !y_.is_leap() ? d[static_cast(month())-1] : julian::day(29); -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - return os << ymdl.year() << '/' << ymdl.month_day_last(); -} - -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const julian::year& y, const julian::month& m, - const julian::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= feb); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-3) / 4; - auto const yoe = static_cast(y - era * 4); // [0, 3] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + doy; // [0, 1460] - return days{era * 1461 + static_cast(doe) - 719470}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return julian::day(1) <= d_ && d_ <= (y_/m_/last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os << ymd.year() << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os << ymd.day(); - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719470; - auto const era = (z >= 0 ? z : z - 1460) / 1461; - auto const doe = static_cast(z - era * 1461); // [0, 1460] - auto const yoe = (doe - doe/1460) / 365; // [0, 3] - auto const y = static_cast(yoe) + era * 4; - auto const doy = doe - 365*yoe; // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{julian::year{y + (m <= 2)}, julian::month(m), julian::day(d)}; -} - -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const julian::year& y, const julian::month& m, - const julian::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - julian::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = julian::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - julian::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - return os << ymwdi.year() << '/' << ymwdi.month() - << '/' << ymwdi.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const julian::year& y, - const julian::month& m, - const julian::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (julian::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -} // namespace julian - -#endif // JULIAN_H diff --git a/third_party/date/include/date/ptz.h b/third_party/date/include/date/ptz.h deleted file mode 100644 index ebd6e04e9..000000000 --- a/third_party/date/include/date/ptz.h +++ /dev/null @@ -1,861 +0,0 @@ -#ifndef PTZ_H -#define PTZ_H - -// The MIT License (MIT) -// -// Copyright (c) 2017 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This header allows Posix-style time zones as specified for TZ here: -// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 -// -// Posix::time_zone can be constructed with a posix-style string and then used in -// a zoned_time like so: -// -// zoned_time zt{"EST5EDT,M3.2.0,M11.1.0", -// system_clock::now()}; -// or: -// -// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"}; -// zoned_time zt{tz, system_clock::now()}; -// -// If the rule set is missing (everything starting with ','), then the rule is that the -// alternate offset is never enabled. -// -// Note, Posix-style time zones are not recommended for all of the reasons described here: -// https://stackoverflow.com/tags/timezone/info -// -// They are provided here as a non-trivial custom time zone example, and if you really -// have to have Posix time zones, you're welcome to use this one. - -#include "date/tz.h" -#include -#include -#include - -namespace Posix -{ - -namespace detail -{ - -#if HAS_STRING_VIEW - -using string_t = std::string_view; - -#else // !HAS_STRING_VIEW - -using string_t = std::string; - -#endif // !HAS_STRING_VIEW - -class rule; - -void throw_invalid(const string_t& s, unsigned i, const string_t& message); -unsigned read_date(const string_t& s, unsigned i, rule& r); -unsigned read_name(const string_t& s, unsigned i, std::string& name); -unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t); -unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t); -unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, - const string_t& message = string_t{}); - -class rule -{ - enum {off, J, M, N}; - - date::month m_; - date::weekday wd_; - unsigned short n_ : 14; - unsigned short mode_ : 2; - std::chrono::duration time_ = std::chrono::hours{2}; - -public: - rule() : mode_(off) {} - - bool ok() const {return mode_ != off;} - date::local_seconds operator()(date::year y) const; - std::string to_string() const; - - friend std::ostream& operator<<(std::ostream& os, const rule& r); - friend unsigned read_date(const string_t& s, unsigned i, rule& r); - friend bool operator==(const rule& x, const rule& y); -}; - -inline -bool -operator==(const rule& x, const rule& y) -{ - if (x.mode_ != y.mode_) - return false; - switch (x.mode_) - { - case rule::J: - case rule::N: - return x.n_ == y.n_; - case rule::M: - return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_; - default: - return true; - } -} - -inline -bool -operator!=(const rule& x, const rule& y) -{ - return !(x == y); -} - -inline -date::local_seconds -rule::operator()(date::year y) const -{ - using date::local_days; - using date::January; - using date::days; - using date::last; - using sec = std::chrono::seconds; - date::local_seconds t; - switch (mode_) - { - case J: - t = local_days{y/January/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_}; - break; - case M: - t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_}; - break; - case N: - t = local_days{y/January/1} + days{n_} + sec{time_}; - break; - default: - assert(!"rule called with bad mode"); - } - return t; -} - -inline -std::string -rule::to_string() const -{ - using namespace std::chrono; - auto print_offset = [](seconds off) - { - std::string nm; - if (off != hours{2}) - { - date::hh_mm_ss offset{off}; - nm = '/'; - nm += std::to_string(offset.hours().count()); - if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) - { - nm += ':'; - if (offset.minutes() < minutes{10}) - nm += '0'; - nm += std::to_string(offset.minutes().count()); - if (offset.seconds() != seconds{0}) - { - nm += ':'; - if (offset.seconds() < seconds{10}) - nm += '0'; - nm += std::to_string(offset.seconds().count()); - } - } - } - return nm; - }; - - std::string nm; - switch (mode_) - { - case rule::J: - nm = 'J'; - nm += std::to_string(n_); - break; - case rule::M: - nm = 'M'; - nm += std::to_string(static_cast(m_)); - nm += '.'; - nm += std::to_string(n_); - nm += '.'; - nm += std::to_string(wd_.c_encoding()); - break; - case rule::N: - nm = std::to_string(n_); - break; - default: - break; - } - nm += print_offset(time_); - return nm; -} - -inline -std::ostream& -operator<<(std::ostream& os, const rule& r) -{ - switch (r.mode_) - { - case rule::J: - os << 'J' << r.n_ << date::format(" %T", r.time_); - break; - case rule::M: - if (r.n_ == 5) - os << r.m_/r.wd_[date::last]; - else - os << r.m_/r.wd_[r.n_]; - os << date::format(" %T", r.time_); - break; - case rule::N: - os << r.n_ << date::format(" %T", r.time_); - break; - default: - break; - } - return os; -} - -} // namespace detail - -class time_zone -{ - std::string std_abbrev_; - std::string dst_abbrev_ = {}; - std::chrono::seconds offset_; - std::chrono::seconds save_ = std::chrono::hours{1}; - detail::rule start_rule_; - detail::rule end_rule_; - -public: - explicit time_zone(const detail::string_t& name); - - template - date::sys_info get_info(date::sys_time st) const; - template - date::local_info get_info(date::local_time tp) const; - - template - date::sys_time::type> - to_sys(date::local_time tp) const; - - template - date::sys_time::type> - to_sys(date::local_time tp, date::choose z) const; - - template - date::local_time::type> - to_local(date::sys_time tp) const; - - friend std::ostream& operator<<(std::ostream& os, const time_zone& z); - - const time_zone* operator->() const {return this;} - - std::string name() const; - - friend bool operator==(const time_zone& x, const time_zone& y); - -private: - date::sys_seconds get_start(date::year y) const; - date::sys_seconds get_prev_start(date::year y) const; - date::sys_seconds get_next_start(date::year y) const; - date::sys_seconds get_end(date::year y) const; - date::sys_seconds get_prev_end(date::year y) const; - date::sys_seconds get_next_end(date::year y) const; - date::sys_info contant_offset() const; -}; - -inline -date::sys_seconds -time_zone::get_start(date::year y) const -{ - return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()}; -} - -inline -date::sys_seconds -time_zone::get_prev_start(date::year y) const -{ - return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()}; -} - -inline -date::sys_seconds -time_zone::get_next_start(date::year y) const -{ - return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()}; -} - -inline -date::sys_seconds -time_zone::get_end(date::year y) const -{ - return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()}; -} - -inline -date::sys_seconds -time_zone::get_prev_end(date::year y) const -{ - return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()}; -} - -inline -date::sys_seconds -time_zone::get_next_end(date::year y) const -{ - return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()}; -} - -date::sys_info -time_zone::contant_offset() const -{ - using date::year; - using date::sys_info; - using date::sys_days; - using date::January; - using date::December; - using date::last; - sys_info r; - r.begin = sys_days{year::min()/January/1}; - r.end = sys_days{year::max()/December/last}; - r.abbrev = std_abbrev_; - r.offset = offset_; - return r; -} - -inline -time_zone::time_zone(const detail::string_t& s) -{ - using detail::read_name; - using detail::read_signed_time; - using detail::throw_invalid; - auto i = read_name(s, 0, std_abbrev_); - i = read_signed_time(s, i, offset_); - offset_ = -offset_; - if (i != s.size()) - { - i = read_name(s, i, dst_abbrev_); - if (i != s.size()) - { - if (s[i] != ',') - { - i = read_signed_time(s, i, save_); - save_ = -save_ - offset_; - } - if (i != s.size()) - { - if (s[i] != ',') - throw_invalid(s, i, "Expecting end of string or ',' to start rule"); - ++i; - i = read_date(s, i, start_rule_); - if (i == s.size() || s[i] != ',') - throw_invalid(s, i, "Expecting ',' and then the ending rule"); - ++i; - i = read_date(s, i, end_rule_); - if (i != s.size()) - throw_invalid(s, i, "Found unexpected trailing characters"); - } - } - } -} - -template -date::sys_info -time_zone::get_info(date::sys_time st) const -{ - using date::sys_info; - using date::year_month_day; - using date::sys_days; - using date::floor; - using date::ceil; - using date::days; - using date::year; - using date::January; - using date::December; - using date::last; - using std::chrono::minutes; - sys_info r{}; - r.offset = offset_; - if (start_rule_.ok()) - { - auto y = year_month_day{floor(st)}.year(); - auto start = get_start(y); - auto end = get_end(y); - if (start <= end) // (northern hemisphere) - { - if (start <= st && st < end) - { - r.begin = start; - r.end = end; - r.offset += save_; - r.save = ceil(save_); - r.abbrev = dst_abbrev_; - } - else if (st < start) - { - r.begin = get_prev_end(y); - r.end = start; - r.abbrev = std_abbrev_; - } - else // st >= end - { - r.begin = end; - r.end = get_next_start(y); - r.abbrev = std_abbrev_; - } - } - else // end < start (southern hemisphere) - { - if (end <= st && st < start) - { - r.begin = end; - r.end = start; - r.abbrev = std_abbrev_; - } - else if (st < end) - { - r.begin = get_prev_start(y); - r.end = end; - r.offset += save_; - r.save = ceil(save_); - r.abbrev = dst_abbrev_; - } - else // st >= start - { - r.begin = start; - r.end = get_next_end(y); - r.offset += save_; - r.save = ceil(save_); - r.abbrev = dst_abbrev_; - } - } - } - else - r = contant_offset(); - return r; -} - -template -date::local_info -time_zone::get_info(date::local_time tp) const -{ - using date::local_info; - using date::year_month_day; - using date::days; - using date::sys_days; - using date::sys_seconds; - using date::year; - using date::ceil; - using date::January; - using date::December; - using date::last; - using std::chrono::seconds; - using std::chrono::minutes; - local_info r{}; - using date::floor; - if (start_rule_.ok()) - { - auto y = year_month_day{floor(tp)}.year(); - auto start = get_start(y); - auto end = get_end(y); - auto utcs = sys_seconds{floor(tp - offset_).time_since_epoch()}; - auto utcd = sys_seconds{floor(tp - (offset_ + save_)).time_since_epoch()}; - auto northern = start <= end; - if ((utcs < start) != (utcd < start)) - { - if (northern) - r.first.begin = get_prev_end(y); - else - r.first.begin = end; - r.first.end = start; - r.first.offset = offset_; - r.first.abbrev = std_abbrev_; - r.second.begin = start; - if (northern) - r.second.end = end; - else - r.second.end = get_next_end(y); - r.second.abbrev = dst_abbrev_; - r.second.offset = offset_ + save_; - r.second.save = ceil(save_); - r.result = save_ > seconds{0} ? local_info::nonexistent - : local_info::ambiguous; - } - else if ((utcs < end) != (utcd < end)) - { - if (northern) - r.first.begin = start; - else - r.first.begin = get_prev_start(y); - r.first.end = end; - r.first.offset = offset_ + save_; - r.first.save = ceil(save_); - r.first.abbrev = dst_abbrev_; - r.second.begin = end; - if (northern) - r.second.end = get_next_start(y); - else - r.second.end = start; - r.second.abbrev = std_abbrev_; - r.second.offset = offset_; - r.result = save_ > seconds{0} ? local_info::ambiguous - : local_info::nonexistent; - } - else - r.first = get_info(utcs); - } - else - r.first = contant_offset(); - return r; -} - -template -date::sys_time::type> -time_zone::to_sys(date::local_time tp) const -{ - using date::local_info; - using date::sys_time; - using date::ambiguous_local_time; - using date::nonexistent_local_time; - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - throw nonexistent_local_time(tp, i); - else if (i.result == local_info::ambiguous) - throw ambiguous_local_time(tp, i); - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -template -date::sys_time::type> -time_zone::to_sys(date::local_time tp, date::choose z) const -{ - using date::local_info; - using date::sys_time; - using date::choose; - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - { - return i.first.end; - } - else if (i.result == local_info::ambiguous) - { - if (z == choose::latest) - return sys_time{tp.time_since_epoch()} - i.second.offset; - } - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -template -date::local_time::type> -time_zone::to_local(date::sys_time tp) const -{ - using date::local_time; - using std::chrono::seconds; - using LT = local_time::type>; - auto i = get_info(tp); - return LT{(tp + i.offset).time_since_epoch()}; -} - -inline -std::ostream& -operator<<(std::ostream& os, const time_zone& z) -{ - using date::operator<<; - os << '{'; - os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_) - << date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}"; - return os; -} - -inline -std::string -time_zone::name() const -{ - using namespace date; - using namespace std::chrono; - auto nm = std_abbrev_; - auto print_offset = [](seconds off) - { - std::string nm; - hh_mm_ss offset{-off}; - if (offset.is_negative()) - nm += '-'; - nm += std::to_string(offset.hours().count()); - if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) - { - nm += ':'; - if (offset.minutes() < minutes{10}) - nm += '0'; - nm += std::to_string(offset.minutes().count()); - if (offset.seconds() != seconds{0}) - { - nm += ':'; - if (offset.seconds() < seconds{10}) - nm += '0'; - nm += std::to_string(offset.seconds().count()); - } - } - return nm; - }; - nm += print_offset(offset_); - if (!dst_abbrev_.empty()) - { - nm += dst_abbrev_; - if (save_ != hours{1}) - nm += print_offset(offset_+save_); - if (start_rule_.ok()) - { - nm += ','; - nm += start_rule_.to_string(); - nm += ','; - nm += end_rule_.to_string(); - } - } - return nm; -} - -inline -bool -operator==(const time_zone& x, const time_zone& y) -{ - return x.std_abbrev_ == y.std_abbrev_ && - x.dst_abbrev_ == y. dst_abbrev_ && - x.offset_ == y.offset_ && - x.save_ == y.save_ && - x.start_rule_ == y.start_rule_ && - x.end_rule_ == y.end_rule_; -} - -inline -bool -operator!=(const time_zone& x, const time_zone& y) -{ - return !(x == y); -} - -namespace detail -{ - -inline -void -throw_invalid(const string_t& s, unsigned i, const string_t& message) -{ - throw std::runtime_error(std::string("Invalid time_zone initializer.\n") + - std::string(message) + ":\n" + - std::string(s) + '\n' + - "\x1b[1;32m" + - std::string(i, '~') + '^' + - std::string(i < s.size() ? s.size()-i-1 : 0, '~') + - "\x1b[0m"); -} - -inline -unsigned -read_date(const string_t& s, unsigned i, rule& r) -{ - using date::month; - using date::weekday; - if (i == s.size()) - throw_invalid(s, i, "Expected rule but found end of string"); - if (s[i] == 'J') - { - ++i; - unsigned n; - i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]"); - r.mode_ = rule::J; - r.n_ = n; - } - else if (s[i] == 'M') - { - ++i; - unsigned m; - i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]"); - if (i == s.size() || s[i] != '.') - throw_invalid(s, i, "Expected '.' after month"); - ++i; - unsigned n; - i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]"); - if (i == s.size() || s[i] != '.') - throw_invalid(s, i, "Expected '.' after weekday index"); - ++i; - unsigned wd; - i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]"); - r.mode_ = rule::M; - r.m_ = month{m}; - r.wd_ = weekday{wd}; - r.n_ = n; - } - else if (std::isdigit(s[i])) - { - unsigned n; - i = read_unsigned(s, i, 3, n); - r.mode_ = rule::N; - r.n_ = n; - } - else - throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule"); - if (i != s.size() && s[i] == '/') - { - ++i; - std::chrono::seconds t; - i = read_unsigned_time(s, i, t); - r.time_ = t; - } - return i; -} - -inline -unsigned -read_name(const string_t& s, unsigned i, std::string& name) -{ - if (i == s.size()) - throw_invalid(s, i, "Expected a name but found end of string"); - if (s[i] == '<') - { - ++i; - while (true) - { - if (i == s.size()) - throw_invalid(s, i, - "Expected to find closing '>', but found end of string"); - if (s[i] == '>') - break; - name.push_back(s[i]); - ++i; - } - ++i; - } - else - { - while (i != s.size() && std::isalpha(s[i])) - { - name.push_back(s[i]); - ++i; - } - } - if (name.size() < 3) - throw_invalid(s, i, "Found name to be shorter than 3 characters"); - return i; -} - -inline -unsigned -read_signed_time(const string_t& s, unsigned i, - std::chrono::seconds& t) -{ - if (i == s.size()) - throw_invalid(s, i, "Expected to read signed time, but found end of string"); - bool negative = false; - if (s[i] == '-') - { - negative = true; - ++i; - } - else if (s[i] == '+') - ++i; - i = read_unsigned_time(s, i, t); - if (negative) - t = -t; - return i; -} - -inline -unsigned -read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t) -{ - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - if (i == s.size()) - throw_invalid(s, i, "Expected to read unsigned time, but found end of string"); - unsigned x; - i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]"); - t = hours{x}; - if (i != s.size() && s[i] == ':') - { - ++i; - i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]"); - t += minutes{x}; - if (i != s.size() && s[i] == ':') - { - ++i; - i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]"); - t += seconds{x}; - } - } - return i; -} - -inline -unsigned -read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, - const string_t& message) -{ - if (i == s.size() || !std::isdigit(s[i])) - throw_invalid(s, i, message); - u = static_cast(s[i] - '0'); - unsigned count = 1; - for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count) - u = u * 10 + static_cast(s[i] - '0'); - return i; -} - -} // namespace detail - -} // namespace Posix - -namespace date -{ - -template <> -struct zoned_traits -{ - -#if HAS_STRING_VIEW - - static - Posix::time_zone - locate_zone(std::string_view name) - { - return Posix::time_zone{name}; - } - -#else // !HAS_STRING_VIEW - - static - Posix::time_zone - locate_zone(const std::string& name) - { - return Posix::time_zone{name}; - } - - static - Posix::time_zone - locate_zone(const char* name) - { - return Posix::time_zone{name}; - } - -#endif // !HAS_STRING_VIEW - -}; - -} // namespace date - -#endif // PTZ_H diff --git a/third_party/date/include/date/solar_hijri.h b/third_party/date/include/date/solar_hijri.h deleted file mode 100644 index 6f6c331be..000000000 --- a/third_party/date/include/date/solar_hijri.h +++ /dev/null @@ -1,3151 +0,0 @@ -#ifndef SOLAR_HIJRI_H -#define SOLAR_HIJRI_H - -// The MIT License (MIT) -// -// Copyright (c) 2016 Howard Hinnant -// Copyright (c) 2019 Asad. Gharighi -// -// Calculations are based on: -// https://www.timeanddate.com/calendar/persian-calendar.html -// and follow style -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#include "date.h" - -namespace solar_hijri -{ - -namespace internal -{ -static const auto epoch = static_cast(2121446); -static const auto days_in_era = static_cast(1029983); -static const auto years_in_era = static_cast(2820); -static const auto unix_time_shift = static_cast(2440588); -auto const years_in_first_cycle = static_cast(29); -auto const years_in_other_cycles = static_cast(33); -auto const years_in_period = static_cast(128); // 29 + 3*33 -auto const days_in_first_cycle = static_cast(10592); // 28/4 + 29*365 -auto const days_in_other_cycles = static_cast(12053); // 32/4 + 33*365 -auto const days_in_period = static_cast(46751); // days_in_first_cycle + 3*days_in_other_cycles; -} - -// durations - -using days = date::days; - -using weeks = date::weeks; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -using sys_days = date::sys_days; -using local_days = date::local_days; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - day() = default; - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - month() = default; - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - year() = default; - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD14 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT; - static CONSTCD11 year max() NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - weekday() = default; - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - explicit weekday(int) = delete; - CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - weekday_indexed() = default; - CONSTCD11 weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - solar_hijri::weekday wd_; - -public: - weekday_last() = default; - explicit CONSTCD11 weekday_last(const solar_hijri::weekday& wd) NOEXCEPT; - - CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -// year_month - -class year_month -{ - solar_hijri::year y_; - solar_hijri::month m_; - -public: - year_month() = default; - CONSTCD11 year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT; - - CONSTCD11 solar_hijri::year year() const NOEXCEPT; - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - solar_hijri::month m_; - solar_hijri::day d_; - -public: - month_day() = default; - CONSTCD11 month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT; - - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - solar_hijri::month m_; - -public: - month_day_last() = default; - CONSTCD11 explicit month_day_last(const solar_hijri::month& m) NOEXCEPT; - - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - solar_hijri::month m_; - solar_hijri::weekday_indexed wdi_; -public: - month_weekday() = default; - CONSTCD11 month_weekday(const solar_hijri::month& m, - const solar_hijri::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - solar_hijri::month m_; - solar_hijri::weekday_last wdl_; - -public: - month_weekday_last() = default; - CONSTCD11 month_weekday_last(const solar_hijri::month& m, - const solar_hijri::weekday_last& wd) NOEXCEPT; - - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - solar_hijri::year y_; - solar_hijri::month m_; - solar_hijri::day d_; - -public: - year_month_day() = default; - CONSTCD11 year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, - const solar_hijri::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 solar_hijri::year year() const NOEXCEPT; - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - solar_hijri::year y_; - solar_hijri::month_day_last mdl_; - -public: - year_month_day_last() = default; - CONSTCD11 year_month_day_last(const solar_hijri::year& y, - const solar_hijri::month_day_last& mdl) NOEXCEPT; - - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 solar_hijri::year year() const NOEXCEPT; - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 solar_hijri::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - solar_hijri::year y_; - solar_hijri::month m_; - solar_hijri::weekday_indexed wdi_; - -public: - year_month_weekday() = default; - CONSTCD11 year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, - const solar_hijri::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 solar_hijri::year year() const NOEXCEPT; - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - solar_hijri::year y_; - solar_hijri::month m_; - solar_hijri::weekday_last wdl_; - -public: - year_month_weekday_last() = default; - CONSTCD11 year_month_weekday_last(const solar_hijri::year& y, const solar_hijri::month& m, - const solar_hijri::weekday_last& wdl) NOEXCEPT; - - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 solar_hijri::year year() const NOEXCEPT; - CONSTCD11 solar_hijri::month month() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; - CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 solar_hijri::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 solar_hijri::year operator "" _y(unsigned long long y) NOEXCEPT; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -//----------------+ -// Implementation | -//----------------+ - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) - 1 + y.count(); - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - switch (static_cast(m)) - { - case 1: - os << "Farvardin"; - break; - case 2: - os << "Ordibehesht"; - break; - case 3: - os << "Khordad"; - break; - case 4: - os << "Tir"; - break; - case 5: - os << "Mordad"; - break; - case 6: - os << "Shahrivar"; - break; - case 7: - os << "Mehr"; - break; - case 8: - os << "Aban"; - break; - case 9: - os << "Azar"; - break; - case 10: - os << "Dey"; - break; - case 11: - os << "Bahman"; - break; - case 12: - os << "Esfand"; - break; - default: - os << static_cast(m) << " is not a valid month"; - break; - } - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} - -CONSTCD14 -inline -bool -year::is_leap() const NOEXCEPT -{ - using namespace internal; - auto const y = static_cast(y_)-475; - auto const era_d = static_cast(y >= 0 ? y : y-years_in_era+1) / static_cast(years_in_era); - auto const era = static_cast(era_d); - auto const yoe = static_cast(y - era * years_in_era); - - // Reference: https://www.timeanddate.com/date/iran-leap-year.html - // 29 + 33 + 33 + 33 = 128 - // 22 * 128 + 4 - auto const yoc = (yoe < (22 * 128)) ? ((yoe%128) < 29 ? yoe%128 : (yoe%128 - 29)%33) : yoe - (22 * 128) + 33; - return (yoc != 0 && (yoc%4)==0); -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} -CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} - -CONSTCD11 -inline -year -year::min() NOEXCEPT -{ - return year{std::numeric_limits::min()}; -} - -CONSTCD11 -inline -year -year::max() NOEXCEPT -{ - return year{std::numeric_limits::max()}; -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os << static_cast(y); - return os; -} - -// weekday - -CONSTCD11 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - auto u = static_cast(z); - return static_cast(z >= -4 ? (u+4) % 7 : u % 7); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd != 7 ? wd : 0)) - {} - -CONSTCD11 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD11 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 -inline -weekday::operator unsigned() const NOEXCEPT -{ - return static_cast(wd_); -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const diff = static_cast(x) - static_cast(y); - return days{diff <= 6 ? diff : diff + 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - switch (static_cast(wd)) - { - case 0: - os << "Yekshanbe"; - break; - case 1: - os << "Doshanbe"; - break; - case 2: - os << "Seshanbe"; - break; - case 3: - os << "Chaharshanbe"; - break; - case 4: - os << "Panjshanbe"; - break; - case 5: - os << "Adine"; - break; - case 6: - os << "Shanbe"; - break; - default: - os << static_cast(wd) << " is not a valid weekday"; - break; - } - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -solar_hijri::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return solar_hijri::day{static_cast(d)}; -} - -CONSTCD11 -inline -solar_hijri::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return solar_hijri::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA solar_hijri::last_spec last{}; - -CONSTDATA solar_hijri::month far {1}; -CONSTDATA solar_hijri::month ord {2}; -CONSTDATA solar_hijri::month kho {3}; -CONSTDATA solar_hijri::month tir {4}; -CONSTDATA solar_hijri::month mor {5}; -CONSTDATA solar_hijri::month sha {6}; -CONSTDATA solar_hijri::month meh {7}; -CONSTDATA solar_hijri::month aba {8}; -CONSTDATA solar_hijri::month aza {9}; -CONSTDATA solar_hijri::month dey {10}; -CONSTDATA solar_hijri::month bah {11}; -CONSTDATA solar_hijri::month esf {12}; - -CONSTDATA solar_hijri::month Farvardin {1}; -CONSTDATA solar_hijri::month Ordibehesht {2}; -CONSTDATA solar_hijri::month Khordad {3}; -CONSTDATA solar_hijri::month Tir {4}; -CONSTDATA solar_hijri::month Mordad {5}; -CONSTDATA solar_hijri::month Shahrivar {6}; -CONSTDATA solar_hijri::month Mehr {7}; -CONSTDATA solar_hijri::month Aban {8}; -CONSTDATA solar_hijri::month Azar {9}; -CONSTDATA solar_hijri::month Dey {10}; -CONSTDATA solar_hijri::month Bahman {11}; -CONSTDATA solar_hijri::month Esfand {12}; - -CONSTDATA solar_hijri::weekday yek {0u}; -CONSTDATA solar_hijri::weekday dos {1u}; -CONSTDATA solar_hijri::weekday ses {2u}; -CONSTDATA solar_hijri::weekday cha {3u}; -CONSTDATA solar_hijri::weekday pan {4u}; -CONSTDATA solar_hijri::weekday adi {5u}; -CONSTDATA solar_hijri::weekday shn {6u}; - -CONSTDATA solar_hijri::weekday Yekshanbe {0u}; -CONSTDATA solar_hijri::weekday Doshanbe {1u}; -CONSTDATA solar_hijri::weekday Seshanbe {2u}; -CONSTDATA solar_hijri::weekday Chaharshanbe {3u}; -CONSTDATA solar_hijri::weekday Panjshanbe {4u}; -CONSTDATA solar_hijri::weekday Adine {5u}; -CONSTDATA solar_hijri::weekday Shanbe {6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return solar_hijri::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd))) - , index_(static_cast(index)) - {} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return os << wdi.weekday() << '[' << wdi.index() << ']'; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline solar_hijri::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const solar_hijri::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - return os << wdl.weekday() << "[last]"; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - return os << ym.year() << '/' << ym.month(); -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline solar_hijri::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline solar_hijri::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA solar_hijri::day d[] = { - solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), - solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), - solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), - solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30) - }; - return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - return os << md.month() << '/' << md.day(); -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const solar_hijri::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - return os << mdl.month() << "/last"; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const solar_hijri::month& m, - const solar_hijri::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - return os << mwd.month() << '/' << mwd.weekday_indexed(); -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const solar_hijri::month& m, - const solar_hijri::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - return os << mwdl.month() << '/' << mwdl.weekday_last(); -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const solar_hijri::year& y, - const solar_hijri::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA solar_hijri::day d[] = { - solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), - solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), - solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), - solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29) - }; - return month() != esf || !y_.is_leap() ? - d[static_cast(month()) - 1] : solar_hijri::day(30); -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - return os << ymdl.year() << '/' << ymdl.month_day_last(); -} - -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, - const solar_hijri::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - - using namespace internal; - auto const y = static_cast(y_) - 475; - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era_d = static_cast(y >= 0 ? y : y-years_in_era+1) / static_cast(years_in_era); - auto const era = static_cast(era_d); - auto const fdoe = static_cast(epoch + era * days_in_era); - auto const yoe = static_cast(y - era * years_in_era); - - auto const period_d = static_cast(yoe/years_in_period); - auto const period = static_cast(period_d); - auto const yop = yoe%years_in_period; - auto const fdop = period*days_in_period; - auto const cycle = yop < 29 ? 0 : static_cast((yop-29)/years_in_other_cycles + 1); - auto const yoc = yop < 29 ? yop : (yop-29)%years_in_other_cycles; - auto const fdoc = cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0; - auto const group = yoc < 1 ? 0 : static_cast((yoc-1) / 4); - auto const yog = static_cast(yoc < 1 ? -1 : (yoc-1) % 4); - auto const fdoyog = group*1461 + (yog+1)*365; - auto const fdoyoe = fdop + fdoc + fdoyog; - - auto const doy = 30*(m-1) + ((m > 6) ? 6 : m-1) + d-1; // [0, 365] - auto const doe = fdoe + fdoyoe + doy; - return days{doe - unix_time_shift}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return solar_hijri::day(1) <= d_ && d_ <= (y_/m_/last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os << ymd.year() << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os << ymd.day(); - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - - using namespace internal; - auto const z = dp.count() + unix_time_shift; - auto const delta = static_cast(z - epoch); - auto const era = static_cast(delta >= 0 ? delta : delta-days_in_era+1) / static_cast(days_in_era); - auto const era_i = static_cast(era); - auto const fdoe = static_cast(epoch + static_cast(era_i * days_in_era)); - - auto const doe_fdoe = z - fdoe; - auto const period = static_cast(doe_fdoe < 22*days_in_period ? doe_fdoe / days_in_period : 22); - auto const dop = doe_fdoe % days_in_period; - auto const cycle = dop < days_in_first_cycle ? 0 : (dop-days_in_first_cycle) / days_in_other_cycles + 1; - auto const doc = dop < days_in_first_cycle ? dop : (dop-days_in_first_cycle) % days_in_other_cycles; - auto const group = doc < 365 && period != 22 ? -1 : static_cast(((doc < 365 ? 365 : doc)-365)/1461); - auto const yog = doc < 365 && period != 22 ? -1 : static_cast( (period != 22 ? ((doc-365 )%1461) : doc)/365); - auto const yoc = group == -1 ? 0 : (period != 22 ? 1 : 0) + group*4 + (yog == 4 ? 3 : yog); - auto const doy = group == -1 ? doc : (period != 22 ? ((yoc-1)%4 == 0 ? (group >= 0 ? (doe_fdoe - - (period*days_in_period) - - (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - - (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) - : 365) - : doe_fdoe - - (period*days_in_period) - - (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - - (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) - : (yog == 4 ? 365 : doe_fdoe - (period*days_in_period) - yog*365)); - auto const yoe = period != 22 ? period*years_in_period + - (cycle > 0 ? years_in_first_cycle + - (cycle-1)*years_in_other_cycles - : 0) + - yoc - : 22*years_in_period + ((yog == 4) ? 3 : yog); - auto const y = static_cast(static_cast(yoe) + 475 + era_i * years_in_era); - auto const m = doy < 186 ? doy/31 + 1 : (doy-186)/30 + 7; // [1, 12] - auto const d = doy - (30*(m-1) + ((m > 6) ? 6 : m-1) - 1); // [1, 31] - - return year_month_day{solar_hijri::year(y), solar_hijri::month(m), solar_hijri::day(d)}; -} - -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, - const solar_hijri::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - solar_hijri::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = solar_hijri::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - solar_hijri::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - return os << ymwdi.year() << '/' << ymwdi.month() - << '/' << ymwdi.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const solar_hijri::year& y, - const solar_hijri::month& m, - const solar_hijri::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (solar_hijri::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -} // namespace solar_hijri - -#endif // SOLAR_HIJRI_H diff --git a/third_party/date/include/date/tz.h b/third_party/date/include/date/tz.h deleted file mode 100644 index 492106839..000000000 --- a/third_party/date/include/date/tz.h +++ /dev/null @@ -1,2792 +0,0 @@ -#ifndef TZ_H -#define TZ_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2017 Jiangang Zhuang -// Copyright (c) 2017 Aaron Bishop -// Copyright (c) 2017 Tomasz Kamiński -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -// Get more recent database at http://www.iana.org/time-zones - -// The notion of "current timezone" is something the operating system is expected to "just -// know". How it knows this is system specific. It's often a value set by the user at OS -// installation time and recorded by the OS somewhere. On Linux and Mac systems the current -// timezone name is obtained by looking at the name or contents of a particular file on -// disk. On Windows the current timezone name comes from the registry. In either method, -// there is no guarantee that the "native" current timezone name obtained will match any -// of the "Standard" names in this library's "database". On Linux, the names usually do -// seem to match so mapping functions to map from native to "Standard" are typically not -// required. On Windows, the names are never "Standard" so mapping is always required. -// Technically any OS may use the mapping process but currently only Windows does use it. - -#ifndef USE_OS_TZDB -# define USE_OS_TZDB 0 -#endif - -#ifndef HAS_REMOTE_API -# if USE_OS_TZDB == 0 -# ifdef _WIN32 -# define HAS_REMOTE_API 0 -# else -# define HAS_REMOTE_API 1 -# endif -# else // HAS_REMOTE_API makes no since when using the OS timezone database -# define HAS_REMOTE_API 0 -# endif -#endif - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wconstant-logical-operand" -#endif - -static_assert(!(USE_OS_TZDB && HAS_REMOTE_API), - "USE_OS_TZDB and HAS_REMOTE_API can not be used together"); - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifndef AUTO_DOWNLOAD -# define AUTO_DOWNLOAD HAS_REMOTE_API -#endif - -static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, - "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); - -#ifndef USE_SHELL_API -# define USE_SHELL_API 1 -#endif - -#if USE_OS_TZDB -# ifdef _WIN32 -# error "USE_OS_TZDB can not be used on Windows" -# endif -#endif - -#ifndef HAS_DEDUCTION_GUIDES -# if __cplusplus >= 201703 -# define HAS_DEDUCTION_GUIDES 1 -# else -# define HAS_DEDUCTION_GUIDES 0 -# endif -#endif // HAS_DEDUCTION_GUIDES - -#include "date.h" - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz_private.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# ifdef DATE_BUILD_DLL -# define DATE_API __declspec(dllexport) -# elif defined(DATE_USE_DLL) -# define DATE_API __declspec(dllimport) -# else -# define DATE_API -# endif -#else -# ifdef DATE_BUILD_DLL -# define DATE_API __attribute__ ((visibility ("default"))) -# else -# define DATE_API -# endif -#endif - -namespace date -{ - -enum class choose {earliest, latest}; - -namespace detail -{ - struct undocumented; - - template - struct nodeduct - { - using type = T; - }; - - template - using nodeduct_t = typename nodeduct::type; -} - -struct sys_info -{ - sys_seconds begin; - sys_seconds end; - std::chrono::seconds offset; - std::chrono::minutes save; - std::string abbrev; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_info& r) -{ - os << r.begin << '\n'; - os << r.end << '\n'; - os << make_time(r.offset) << "\n"; - os << make_time(r.save) << "\n"; - os << r.abbrev << '\n'; - return os; -} - -struct local_info -{ - enum {unique, nonexistent, ambiguous} result; - sys_info first; - sys_info second; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_info& r) -{ - if (r.result == local_info::nonexistent) - os << "nonexistent between\n"; - else if (r.result == local_info::ambiguous) - os << "ambiguous between\n"; - os << r.first; - if (r.result != local_info::unique) - { - os << "and\n"; - os << r.second; - } - return os; -} - -class nonexistent_local_time - : public std::runtime_error -{ -public: - template - nonexistent_local_time(local_time tp, const local_info& i); - -private: - template - static - std::string - make_msg(local_time tp, const local_info& i); -}; - -template -inline -nonexistent_local_time::nonexistent_local_time(local_time tp, - const local_info& i) - : std::runtime_error(make_msg(tp, i)) -{ -} - -template -std::string -nonexistent_local_time::make_msg(local_time tp, const local_info& i) -{ - assert(i.result == local_info::nonexistent); - std::ostringstream os; - os << tp << " is in a gap between\n" - << local_seconds{i.first.end.time_since_epoch()} + i.first.offset << ' ' - << i.first.abbrev << " and\n" - << local_seconds{i.second.begin.time_since_epoch()} + i.second.offset << ' ' - << i.second.abbrev - << " which are both equivalent to\n" - << i.first.end << " UTC"; - return os.str(); -} - -class ambiguous_local_time - : public std::runtime_error -{ -public: - template - ambiguous_local_time(local_time tp, const local_info& i); - -private: - template - static - std::string - make_msg(local_time tp, const local_info& i); -}; - -template -inline -ambiguous_local_time::ambiguous_local_time(local_time tp, const local_info& i) - : std::runtime_error(make_msg(tp, i)) -{ -} - -template -std::string -ambiguous_local_time::make_msg(local_time tp, const local_info& i) -{ - assert(i.result == local_info::ambiguous); - std::ostringstream os; - os << tp << " is ambiguous. It could be\n" - << tp << ' ' << i.first.abbrev << " == " - << tp - i.first.offset << " UTC or\n" - << tp << ' ' << i.second.abbrev << " == " - << tp - i.second.offset << " UTC"; - return os.str(); -} - -class time_zone; - -#if HAS_STRING_VIEW -DATE_API const time_zone* locate_zone(std::string_view tz_name); -#else -DATE_API const time_zone* locate_zone(const std::string& tz_name); -#endif - -DATE_API const time_zone* current_zone(); - -template -struct zoned_traits -{ -}; - -template <> -struct zoned_traits -{ - static - const time_zone* - default_zone() - { - return date::locate_zone("Etc/UTC"); - } - -#if HAS_STRING_VIEW - - static - const time_zone* - locate_zone(std::string_view name) - { - return date::locate_zone(name); - } - -#else // !HAS_STRING_VIEW - - static - const time_zone* - locate_zone(const std::string& name) - { - return date::locate_zone(name); - } - - static - const time_zone* - locate_zone(const char* name) - { - return date::locate_zone(name); - } - -#endif // !HAS_STRING_VIEW -}; - -template -class zoned_time; - -template -bool -operator==(const zoned_time& x, - const zoned_time& y); - -template -class zoned_time -{ -public: - using duration = typename std::common_type::type; - -private: - TimeZonePtr zone_; - sys_time tp_; - -public: -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::default_zone())> -#endif - zoned_time(); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::default_zone())> -#endif - zoned_time(const sys_time& st); - explicit zoned_time(TimeZonePtr z); - -#if HAS_STRING_VIEW - template ::locate_zone(std::string_view())) - >::value - >::type> - explicit zoned_time(std::string_view name); -#else -# if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())) - >::value - >::type> -# endif - explicit zoned_time(const std::string& name); -#endif - - template , - sys_time>::value - >::type> - zoned_time(const zoned_time& zt) NOEXCEPT; - - zoned_time(TimeZonePtr z, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ()->to_sys(local_time{})), - sys_time - >::value - >::type> -#endif - zoned_time(TimeZonePtr z, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ()->to_sys(local_time{}, - choose::earliest)), - sys_time - >::value - >::type> -#endif - zoned_time(TimeZonePtr z, const local_time& tp, choose c); - - template , - sys_time>::value - >::type> - zoned_time(TimeZonePtr z, const zoned_time& zt); - - template , - sys_time>::value - >::type> - zoned_time(TimeZonePtr z, const zoned_time& zt, choose); - -#if HAS_STRING_VIEW - - template ::locate_zone(std::string_view())), - sys_time - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> st); - - template ::locate_zone(std::string_view())), - local_time - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> tp); - - template ::locate_zone(std::string_view())), - local_time, - choose - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> tp, choose c); - - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string_view())), - zoned_time - >::value - >::type> - zoned_time(std::string_view name, const zoned_time& zt); - - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string_view())), - zoned_time, - choose - >::value - >::type> - zoned_time(std::string_view name, const zoned_time& zt, choose); - -#else // !HAS_STRING_VIEW - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - sys_time - >::value - >::type> -#endif - zoned_time(const std::string& name, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - sys_time - >::value - >::type> -#endif - zoned_time(const char* name, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time - >::value - >::type> -#endif - zoned_time(const std::string& name, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time - >::value - >::type> -#endif - zoned_time(const char* name, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time, - choose - >::value - >::type> -#endif - zoned_time(const std::string& name, const local_time& tp, choose c); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time, - choose - >::value - >::type> -#endif - zoned_time(const char* name, const local_time& tp, choose c); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time - >::value - >::type> -#else - template -#endif - zoned_time(const std::string& name, const zoned_time& zt); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time - >::value - >::type> -#else - template -#endif - zoned_time(const char* name, const zoned_time& zt); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time, - choose - >::value - >::type> -#else - template -#endif - zoned_time(const std::string& name, const zoned_time& zt, - choose); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time, - choose - >::value - >::type> -#else - template -#endif - zoned_time(const char* name, const zoned_time& zt, - choose); - -#endif // !HAS_STRING_VIEW - - zoned_time& operator=(const sys_time& st); - zoned_time& operator=(const local_time& ut); - - explicit operator sys_time() const; - explicit operator local_time() const; - - TimeZonePtr get_time_zone() const; - local_time get_local_time() const; - sys_time get_sys_time() const; - sys_info get_info() const; - - template - friend - bool - operator==(const zoned_time& x, - const zoned_time& y); - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, - const zoned_time& t); - -private: - template friend class zoned_time; - - template - static - TimeZonePtr2&& - check(TimeZonePtr2&& p); -}; - -using zoned_seconds = zoned_time; - -#if HAS_DEDUCTION_GUIDES - -namespace detail -{ - template - using time_zone_representation = - std::conditional_t - < - std::is_convertible::value, - time_zone const*, - std::remove_cv_t> - >; -} - -zoned_time() - -> zoned_time; - -template -zoned_time(sys_time) - -> zoned_time>; - -template -zoned_time(TimeZonePtrOrName&&) - -> zoned_time>; - -template -zoned_time(TimeZonePtrOrName&&, sys_time) - -> zoned_time, detail::time_zone_representation>; - -template -zoned_time(TimeZonePtrOrName&&, local_time, choose = choose::earliest) - -> zoned_time, detail::time_zone_representation>; - -template -zoned_time(TimeZonePtrOrName&&, zoned_time, choose = choose::earliest) - -> zoned_time, detail::time_zone_representation>; - -#endif // HAS_DEDUCTION_GUIDES - -template -inline -bool -operator==(const zoned_time& x, - const zoned_time& y) -{ - return x.zone_ == y.zone_ && x.tp_ == y.tp_; -} - -template -inline -bool -operator!=(const zoned_time& x, - const zoned_time& y) -{ - return !(x == y); -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - -namespace detail -{ -# if USE_OS_TZDB - struct transition; - struct expanded_ttinfo; -# else // !USE_OS_TZDB - struct zonelet; - class Rule; -# endif // !USE_OS_TZDB -} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -class time_zone -{ -private: - std::string name_; -#if USE_OS_TZDB - std::vector transitions_; - std::vector ttinfos_; -#else // !USE_OS_TZDB - std::vector zonelets_; -#endif // !USE_OS_TZDB - std::unique_ptr adjusted_; - -public: -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - time_zone(time_zone&&) = default; - time_zone& operator=(time_zone&&) = default; -#else // defined(_MSC_VER) && (_MSC_VER < 1900) - time_zone(time_zone&& src); - time_zone& operator=(time_zone&& src); -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - - DATE_API explicit time_zone(const std::string& s, detail::undocumented); - - const std::string& name() const NOEXCEPT; - - template sys_info get_info(sys_time st) const; - template local_info get_info(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp, choose z) const; - - template - local_time::type> - to_local(sys_time tp) const; - - friend bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT; - friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; - friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z); - -#if !USE_OS_TZDB - DATE_API void add(const std::string& s); -#endif // !USE_OS_TZDB - -private: - DATE_API sys_info get_info_impl(sys_seconds tp) const; - DATE_API local_info get_info_impl(local_seconds tp) const; - - template - sys_time::type> - to_sys_impl(local_time tp, choose z, std::false_type) const; - template - sys_time::type> - to_sys_impl(local_time tp, choose, std::true_type) const; - -#if USE_OS_TZDB - DATE_API void init() const; - DATE_API void init_impl(); - DATE_API sys_info - load_sys_info(std::vector::const_iterator i) const; - - template - DATE_API void - load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, - std::int32_t tzh_typecnt, std::int32_t tzh_charcnt); -#else // !USE_OS_TZDB - DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const; - DATE_API void adjust_infos(const std::vector& rules); - DATE_API void parse_info(std::istream& in); -#endif // !USE_OS_TZDB -}; - -#if defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -time_zone::time_zone(time_zone&& src) - : name_(std::move(src.name_)) - , zonelets_(std::move(src.zonelets_)) - , adjusted_(std::move(src.adjusted_)) - {} - -inline -time_zone& -time_zone::operator=(time_zone&& src) -{ - name_ = std::move(src.name_); - zonelets_ = std::move(src.zonelets_); - adjusted_ = std::move(src.adjusted_); - return *this; -} - -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -const std::string& -time_zone::name() const NOEXCEPT -{ - return name_; -} - -template -inline -sys_info -time_zone::get_info(sys_time st) const -{ - return get_info_impl(date::floor(st)); -} - -template -inline -local_info -time_zone::get_info(local_time tp) const -{ - return get_info_impl(date::floor(tp)); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp) const -{ - return to_sys_impl(tp, choose{}, std::true_type{}); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp, choose z) const -{ - return to_sys_impl(tp, z, std::false_type{}); -} - -template -inline -local_time::type> -time_zone::to_local(sys_time tp) const -{ - using LT = local_time::type>; - auto i = get_info(tp); - return LT{(tp + i.offset).time_since_epoch()}; -} - -inline bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ == y.name_;} -inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} - -inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} -inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} -inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} -inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x < y);} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose z, std::false_type) const -{ - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - { - return i.first.end; - } - else if (i.result == local_info::ambiguous) - { - if (z == choose::latest) - return sys_time{tp.time_since_epoch()} - i.second.offset; - } - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose, std::true_type) const -{ - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - throw nonexistent_local_time(tp, i); - else if (i.result == local_info::ambiguous) - throw ambiguous_local_time(tp, i); - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -#if !USE_OS_TZDB - -class time_zone_link -{ -private: - std::string name_; - std::string target_; -public: - DATE_API explicit time_zone_link(const std::string& s); - - const std::string& name() const {return name_;} - const std::string& target() const {return target_;} - - friend bool operator==(const time_zone_link& x, const time_zone_link& y) {return x.name_ == y.name_;} - friend bool operator< (const time_zone_link& x, const time_zone_link& y) {return x.name_ < y.name_;} - - friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone_link& x); -}; - -using link = time_zone_link; - -inline bool operator!=(const time_zone_link& x, const time_zone_link& y) {return !(x == y);} -inline bool operator> (const time_zone_link& x, const time_zone_link& y) {return y < x;} -inline bool operator<=(const time_zone_link& x, const time_zone_link& y) {return !(y < x);} -inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return !(x < y);} - -#endif // !USE_OS_TZDB - -class leap_second -{ -private: - sys_seconds date_; - -public: -#if USE_OS_TZDB - DATE_API explicit leap_second(const sys_seconds& s, detail::undocumented); -#else - DATE_API explicit leap_second(const std::string& s, detail::undocumented); -#endif - - sys_seconds date() const {return date_;} - - friend bool operator==(const leap_second& x, const leap_second& y) {return x.date_ == y.date_;} - friend bool operator< (const leap_second& x, const leap_second& y) {return x.date_ < y.date_;} - - template - friend - bool - operator==(const leap_second& x, const sys_time& y) - { - return x.date_ == y; - } - - template - friend - bool - operator< (const leap_second& x, const sys_time& y) - { - return x.date_ < y; - } - - template - friend - bool - operator< (const sys_time& x, const leap_second& y) - { - return x < y.date_; - } - - friend DATE_API std::ostream& operator<<(std::ostream& os, const leap_second& x); -}; - -inline bool operator!=(const leap_second& x, const leap_second& y) {return !(x == y);} -inline bool operator> (const leap_second& x, const leap_second& y) {return y < x;} -inline bool operator<=(const leap_second& x, const leap_second& y) {return !(y < x);} -inline bool operator>=(const leap_second& x, const leap_second& y) {return !(x < y);} - -template -inline -bool -operator==(const sys_time& x, const leap_second& y) -{ - return y == x; -} - -template -inline -bool -operator!=(const leap_second& x, const sys_time& y) -{ - return !(x == y); -} - -template -inline -bool -operator!=(const sys_time& x, const leap_second& y) -{ - return !(x == y); -} - -template -inline -bool -operator> (const leap_second& x, const sys_time& y) -{ - return y < x; -} - -template -inline -bool -operator> (const sys_time& x, const leap_second& y) -{ - return y < x; -} - -template -inline -bool -operator<=(const leap_second& x, const sys_time& y) -{ - return !(y < x); -} - -template -inline -bool -operator<=(const sys_time& x, const leap_second& y) -{ - return !(y < x); -} - -template -inline -bool -operator>=(const leap_second& x, const sys_time& y) -{ - return !(x < y); -} - -template -inline -bool -operator>=(const sys_time& x, const leap_second& y) -{ - return !(x < y); -} - -using leap = leap_second; - -#ifdef _WIN32 - -namespace detail -{ - -// The time zone mapping is modelled after this data file: -// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml -// and the field names match the element names from the mapZone element -// of windowsZones.xml. -// The website displays this file here: -// http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html -// The html view is sorted before being displayed but is otherwise the same -// There is a mapping between the os centric view (in this case windows) -// the html displays uses and the generic view the xml file. -// That mapping is this: -// display column "windows" -> xml field "other". -// display column "region" -> xml field "territory". -// display column "tzid" -> xml field "type". -// This structure uses the generic terminology because it could be -// used to to support other os/native name conversions, not just windows, -// and using the same generic names helps retain the connection to the -// origin of the data that we are using. -struct timezone_mapping -{ - timezone_mapping(const char* other, const char* territory, const char* type) - : other(other), territory(territory), type(type) - { - } - timezone_mapping() = default; - std::string other; - std::string territory; - std::string type; -}; - -} // detail - -#endif // _WIN32 - -struct tzdb -{ - std::string version = "unknown"; - std::vector zones; -#if !USE_OS_TZDB - std::vector links; -#endif - std::vector leap_seconds; -#if !USE_OS_TZDB - std::vector rules; -#endif -#ifdef _WIN32 - std::vector mappings; -#endif - tzdb* next = nullptr; - - tzdb() = default; -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - tzdb(tzdb&&) = default; - tzdb& operator=(tzdb&&) = default; -#else // defined(_MSC_VER) && (_MSC_VER < 1900) - tzdb(tzdb&& src) - : version(std::move(src.version)) - , zones(std::move(src.zones)) - , links(std::move(src.links)) - , leap_seconds(std::move(src.leap_seconds)) - , rules(std::move(src.rules)) - , mappings(std::move(src.mappings)) - {} - - tzdb& operator=(tzdb&& src) - { - version = std::move(src.version); - zones = std::move(src.zones); - links = std::move(src.links); - leap_seconds = std::move(src.leap_seconds); - rules = std::move(src.rules); - mappings = std::move(src.mappings); - return *this; - } -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - -#if HAS_STRING_VIEW - const time_zone* locate_zone(std::string_view tz_name) const; -#else - const time_zone* locate_zone(const std::string& tz_name) const; -#endif - const time_zone* current_zone() const; -}; - -using TZ_DB = tzdb; - -DATE_API std::ostream& -operator<<(std::ostream& os, const tzdb& db); - -DATE_API const tzdb& get_tzdb(); - -class tzdb_list -{ - std::atomic head_{nullptr}; - -public: - ~tzdb_list(); - tzdb_list() = default; - tzdb_list(tzdb_list&& x) NOEXCEPT; - - const tzdb& front() const NOEXCEPT {return *head_;} - tzdb& front() NOEXCEPT {return *head_;} - - class const_iterator; - - const_iterator begin() const NOEXCEPT; - const_iterator end() const NOEXCEPT; - - const_iterator cbegin() const NOEXCEPT; - const_iterator cend() const NOEXCEPT; - - const_iterator erase_after(const_iterator p) NOEXCEPT; - - struct undocumented_helper; -private: - void push_front(tzdb* tzdb) NOEXCEPT; -}; - -class tzdb_list::const_iterator -{ - tzdb* p_ = nullptr; - - explicit const_iterator(tzdb* p) NOEXCEPT : p_{p} {} -public: - const_iterator() = default; - - using iterator_category = std::forward_iterator_tag; - using value_type = tzdb; - using reference = const value_type&; - using pointer = const value_type*; - using difference_type = std::ptrdiff_t; - - reference operator*() const NOEXCEPT {return *p_;} - pointer operator->() const NOEXCEPT {return p_;} - - const_iterator& operator++() NOEXCEPT {p_ = p_->next; return *this;} - const_iterator operator++(int) NOEXCEPT {auto t = *this; ++(*this); return t;} - - friend - bool - operator==(const const_iterator& x, const const_iterator& y) NOEXCEPT - {return x.p_ == y.p_;} - - friend - bool - operator!=(const const_iterator& x, const const_iterator& y) NOEXCEPT - {return !(x == y);} - - friend class tzdb_list; -}; - -inline -tzdb_list::const_iterator -tzdb_list::begin() const NOEXCEPT -{ - return const_iterator{head_}; -} - -inline -tzdb_list::const_iterator -tzdb_list::end() const NOEXCEPT -{ - return const_iterator{nullptr}; -} - -inline -tzdb_list::const_iterator -tzdb_list::cbegin() const NOEXCEPT -{ - return begin(); -} - -inline -tzdb_list::const_iterator -tzdb_list::cend() const NOEXCEPT -{ - return end(); -} - -DATE_API tzdb_list& get_tzdb_list(); - -#if !USE_OS_TZDB - -DATE_API const tzdb& reload_tzdb(); -DATE_API void set_install(const std::string& install); - -#endif // !USE_OS_TZDB - -#if HAS_REMOTE_API - -DATE_API std::string remote_version(); -// if provided error_buffer size should be at least CURL_ERROR_SIZE -DATE_API bool remote_download(const std::string& version, char* error_buffer = nullptr); -DATE_API bool remote_install(const std::string& version); - -#endif - -// zoned_time - -namespace detail -{ - -template -inline -T* -to_raw_pointer(T* p) NOEXCEPT -{ - return p; -} - -template -inline -auto -to_raw_pointer(Pointer p) NOEXCEPT - -> decltype(detail::to_raw_pointer(p.operator->())) -{ - return detail::to_raw_pointer(p.operator->()); -} - -} // namespace detail - -template -template -inline -TimeZonePtr2&& -zoned_time::check(TimeZonePtr2&& p) -{ - if (detail::to_raw_pointer(p) == nullptr) - throw std::runtime_error( - "zoned_time constructed with a time zone pointer == nullptr"); - return std::forward(p); -} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time() - : zone_(check(zoned_traits::default_zone())) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const sys_time& st) - : zone_(check(zoned_traits::default_zone())) - , tp_(st) - {} - -template -inline -zoned_time::zoned_time(TimeZonePtr z) - : zone_(check(std::move(z))) - {} - -#if HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(std::string_view name) - : zoned_time(zoned_traits::locate_zone(name)) - {} - -#else // !HAS_STRING_VIEW - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name) - : zoned_time(zoned_traits::locate_zone(name)) - {} - -#endif // !HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(const zoned_time& zt) NOEXCEPT - : zone_(zt.zone_) - , tp_(zt.tp_) - {} - -template -inline -zoned_time::zoned_time(TimeZonePtr z, const sys_time& st) - : zone_(check(std::move(z))) - , tp_(st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(TimeZonePtr z, const local_time& t) - : zone_(check(std::move(z))) - , tp_(zone_->to_sys(t)) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(TimeZonePtr z, const local_time& t, - choose c) - : zone_(check(std::move(z))) - , tp_(zone_->to_sys(t, c)) - {} - -template -template -inline -zoned_time::zoned_time(TimeZonePtr z, - const zoned_time& zt) - : zone_(check(std::move(z))) - , tp_(zt.tp_) - {} - -template -template -inline -zoned_time::zoned_time(TimeZonePtr z, - const zoned_time& zt, choose) - : zoned_time(std::move(z), zt) - {} - -#if HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -#else // !HAS_STRING_VIEW - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const sys_time& st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const sys_time& st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const local_time& t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const local_time& t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const local_time& t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const local_time& t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const char* name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const char* name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -#endif // HAS_STRING_VIEW - -template -inline -zoned_time& -zoned_time::operator=(const sys_time& st) -{ - tp_ = st; - return *this; -} - -template -inline -zoned_time& -zoned_time::operator=(const local_time& ut) -{ - tp_ = zone_->to_sys(ut); - return *this; -} - -template -inline -zoned_time::operator local_time::duration>() const -{ - return get_local_time(); -} - -template -inline -zoned_time::operator sys_time::duration>() const -{ - return get_sys_time(); -} - -template -inline -TimeZonePtr -zoned_time::get_time_zone() const -{ - return zone_; -} - -template -inline -local_time::duration> -zoned_time::get_local_time() const -{ - return zone_->to_local(tp_); -} - -template -inline -sys_time::duration> -zoned_time::get_sys_time() const -{ - return tp_; -} - -template -inline -sys_info -zoned_time::get_info() const -{ - return zone_->get_info(tp_); -} - -// make_zoned_time - -inline -zoned_time -make_zoned() -{ - return zoned_time(); -} - -template -inline -zoned_time::type> -make_zoned(const sys_time& tp) -{ - return zoned_time::type>(tp); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class - < - typename std::decay - < - decltype(*detail::to_raw_pointer(std::declval())) - >::type - >{} - >::type -#endif -#endif - > -inline -zoned_time -make_zoned(TimeZonePtr z) -{ - return zoned_time(std::move(z)); -} - -inline -zoned_seconds -make_zoned(const std::string& name) -{ - return zoned_seconds(name); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const local_time& tp) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), tp); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const local_time& tp, choose c) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), tp, c); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp) -{ - return zoned_time::type>(name, tp); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp, choose c) -{ - return zoned_time::type>(name, tp, c); -} - -template -inline -zoned_time -make_zoned(TimeZonePtr zone, const zoned_time& zt) -{ - return zoned_time(std::move(zone), zt); -} - -template -inline -zoned_time -make_zoned(const std::string& name, const zoned_time& zt) -{ - return zoned_time(name, zt); -} - -template -inline -zoned_time -make_zoned(TimeZonePtr zone, const zoned_time& zt, choose c) -{ - return zoned_time(std::move(zone), zt, c); -} - -template -inline -zoned_time -make_zoned(const std::string& name, const zoned_time& zt, choose c) -{ - return zoned_time(name, zt, c); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const sys_time& st) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), st); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const sys_time& st) -{ - return zoned_time::type>(name, st); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const zoned_time& tp) -{ - using duration = typename zoned_time::duration; - using LT = local_time; - auto const st = tp.get_sys_time(); - auto const info = tp.get_time_zone()->get_info(st); - return to_stream(os, fmt, LT{(st+info.offset).time_since_epoch()}, - &info.abbrev, &info.offset); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const zoned_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', ' ', '%', 'Z', CharT{}}; - return to_stream(os, fmt, t); -} - -class utc_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static CONSTDATA bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_sys(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - from_sys(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&); -}; - -template - using utc_time = std::chrono::time_point; - -using utc_seconds = utc_time; - -template -utc_time::type> -utc_clock::from_sys(const sys_time& st) -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - auto const& leaps = get_tzdb().leap_seconds; - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), st); - return utc_time{st.time_since_epoch() + seconds{lt-leaps.begin()}}; -} - -// Return pair -// first is true if ut is during a leap second insertion, otherwise false. -// If ut is during a leap second insertion, that leap second is included in the count -template -std::pair -is_leap_second(date::utc_time const& ut) -{ - using std::chrono::seconds; - using duration = typename std::common_type::type; - auto const& leaps = get_tzdb().leap_seconds; - auto tp = sys_time{ut.time_since_epoch()}; - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); - auto ds = seconds{lt-leaps.begin()}; - tp -= ds; - auto ls = false; - if (lt > leaps.begin()) - { - if (tp < lt[-1]) - { - if (tp >= lt[-1].date() - seconds{1}) - ls = true; - else - --ds; - } - } - return {ls, ds}; -} - -struct leap_second_info -{ - bool is_leap_second; - std::chrono::seconds elapsed; -}; - -template -leap_second_info -get_leap_second_info(date::utc_time const& ut) -{ - auto p = is_leap_second(ut); - return {p.first, p.second}; -} - -template -sys_time::type> -utc_clock::to_sys(const utc_time& ut) -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - auto ls = is_leap_second(ut); - auto tp = sys_time{ut.time_since_epoch() - ls.second}; - if (ls.first) - tp = floor(tp) + seconds{1} - CD{1}; - return tp; -} - -inline -utc_clock::time_point -utc_clock::now() -{ - return from_sys(std::chrono::system_clock::now()); -} - -template -utc_time::type> -utc_clock::from_local(const local_time& st) -{ - return from_sys(sys_time{st.time_since_epoch()}); -} - -template -local_time::type> -utc_clock::to_local(const utc_time& ut) -{ - using CD = typename std::common_type::type; - return local_time{to_sys(ut).time_since_epoch()}; -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const utc_time& t) -{ - using std::chrono::seconds; - using CT = typename std::common_type::type; - const std::string abbrev("UTC"); - CONSTDATA seconds offset{0}; - auto ls = is_leap_second(t); - auto tp = sys_time{t.time_since_epoch() - ls.second}; - auto const sd = floor(tp); - year_month_day ymd = sd; - auto time = make_time(tp - sys_seconds{sd}); - time.seconds(detail::undocumented{}) += seconds{ls.first}; - fields fds{ymd, time}; - return to_stream(os, fmt, fds, &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const utc_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - utc_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using std::chrono::seconds; - using std::chrono::minutes; - using CT = typename std::common_type::type; - minutes offset_local{}; - auto offptr = offset ? offset : &offset_local; - fields fds{}; - fds.has_tod = true; - from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - { - bool is_60_sec = fds.tod.seconds() == seconds{60}; - if (is_60_sec) - fds.tod.seconds(detail::undocumented{}) -= seconds{1}; - auto tmp = utc_clock::from_sys(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); - if (is_60_sec) - tmp += seconds{1}; - if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) - { - is.setstate(std::ios::failbit); - return is; - } - tp = std::chrono::time_point_cast(tmp); - } - return is; -} - -// tai_clock - -class tai_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&) NOEXCEPT; -}; - -template - using tai_time = std::chrono::time_point; - -using tai_seconds = tai_time; - -template -inline -utc_time::type> -tai_clock::to_utc(const tai_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return utc_time{t.time_since_epoch()} - - (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); -} - -template -inline -tai_time::type> -tai_clock::from_utc(const utc_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); -} - -inline -tai_clock::time_point -tai_clock::now() -{ - return from_utc(utc_clock::now()); -} - -template -inline -local_time::type> -tai_clock::to_local(const tai_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return local_time{t.time_since_epoch()} - - (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); -} - -template -inline -tai_time::type> -tai_clock::from_local(const local_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const tai_time& t) -{ - const std::string abbrev("TAI"); - CONSTDATA std::chrono::seconds offset{0}; - return to_stream(os, fmt, tai_clock::to_local(t), &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const tai_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - tai_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - local_time lp; - from_stream(is, fmt, lp, abbrev, offset); - if (!is.fail()) - tp = tai_clock::from_local(lp); - return is; -} - -// gps_clock - -class gps_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&) NOEXCEPT; -}; - -template - using gps_time = std::chrono::time_point; - -using gps_seconds = gps_time; - -template -inline -utc_time::type> -gps_clock::to_utc(const gps_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return utc_time{t.time_since_epoch()} + - (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + - seconds{9}); -} - -template -inline -gps_time::type> -gps_clock::from_utc(const utc_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + - seconds{9}); -} - -inline -gps_clock::time_point -gps_clock::now() -{ - return from_utc(utc_clock::now()); -} - -template -inline -local_time::type> -gps_clock::to_local(const gps_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return local_time{t.time_since_epoch()} + - (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); -} - -template -inline -gps_time::type> -gps_clock::from_local(const local_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); -} - - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const gps_time& t) -{ - const std::string abbrev("GPS"); - CONSTDATA std::chrono::seconds offset{0}; - return to_stream(os, fmt, gps_clock::to_local(t), &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const gps_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - gps_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - local_time lp; - from_stream(is, fmt, lp, abbrev, offset); - if (!is.fail()) - tp = gps_clock::from_local(lp); - return is; -} - -// clock_time_conversion - -template -struct clock_time_conversion -{}; - -template <> -struct clock_time_conversion -{ - template - CONSTCD14 - sys_time - operator()(const sys_time& st) const - { - return st; - } -}; - -template <> -struct clock_time_conversion -{ - template - CONSTCD14 - utc_time - operator()(const utc_time& ut) const - { - return ut; - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - local_time - operator()(const local_time& lt) const - { - return lt; - } -}; - -template <> -struct clock_time_conversion -{ - template - utc_time::type> - operator()(const sys_time& st) const - { - return utc_clock::from_sys(st); - } -}; - -template <> -struct clock_time_conversion -{ - template - sys_time::type> - operator()(const utc_time& ut) const - { - return utc_clock::to_sys(ut); - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - local_time - operator()(const sys_time& st) const - { - return local_time{st.time_since_epoch()}; - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - sys_time - operator()(const local_time& lt) const - { - return sys_time{lt.time_since_epoch()}; - } -}; - -template<> -struct clock_time_conversion -{ - template - utc_time::type> - operator()(const local_time& lt) const - { - return utc_clock::from_local(lt); - } -}; - -template<> -struct clock_time_conversion -{ - template - local_time::type> - operator()(const utc_time& ut) const - { - return utc_clock::to_local(ut); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - std::chrono::time_point - operator()(const std::chrono::time_point& tp) const - { - return tp; - } -}; - -namespace ctc_detail -{ - -template - using time_point = std::chrono::time_point; - -using std::declval; -using std::chrono::system_clock; - -//Check if TimePoint is time for given clock, -//if not emits hard error -template -struct return_clock_time -{ - using clock_time_point = time_point; - using type = TimePoint; - - static_assert(std::is_same::value, - "time point with appropariate clock shall be returned"); -}; - -// Check if Clock has to_sys method accepting TimePoint with given duration const& and -// returning sys_time. If so has nested type member equal to return type to_sys. -template -struct return_to_sys -{}; - -template -struct return_to_sys - < - Clock, Duration, - decltype(Clock::to_sys(declval const&>()), void()) - > - : return_clock_time - < - system_clock, - decltype(Clock::to_sys(declval const&>())) - > -{}; - -// Similiar to above -template -struct return_from_sys -{}; - -template -struct return_from_sys - < - Clock, Duration, - decltype(Clock::from_sys(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_sys(declval const&>())) - > -{}; - -// Similiar to above -template -struct return_to_utc -{}; - -template -struct return_to_utc - < - Clock, Duration, - decltype(Clock::to_utc(declval const&>()), void()) - > - : return_clock_time - < - utc_clock, - decltype(Clock::to_utc(declval const&>()))> -{}; - -// Similiar to above -template -struct return_from_utc -{}; - -template -struct return_from_utc - < - Clock, Duration, - decltype(Clock::from_utc(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_utc(declval const&>())) - > -{}; - -// Similiar to above -template -struct return_to_local -{}; - -template -struct return_to_local - < - Clock, Duration, - decltype(Clock::to_local(declval const&>()), - void()) - > - : return_clock_time - < - local_t, - decltype(Clock::to_local(declval const&>())) - > -{}; - -// Similiar to above -template -struct return_from_local -{}; - -template -struct return_from_local - < - Clock, Duration, - decltype(Clock::from_local(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_local(declval const&>())) - > -{}; - -} // namespace ctc_detail - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_sys::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_sys(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_sys::type - operator()(const sys_time& st) const - { - return DstClock::from_sys(st); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_utc::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_utc(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_utc::type - operator()(const utc_time& ut) const - { - return DstClock::from_utc(ut); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_local::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_local(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_local::type - operator()(const local_time& lt) const - { - return DstClock::from_local(lt); - } -}; - -namespace clock_cast_detail -{ - -template - using time_point = std::chrono::time_point; -using std::chrono::system_clock; - -template -CONSTCD14 -auto -conv_clock(const time_point& t) - -> decltype(std::declval>()(t)) -{ - return clock_time_conversion{}(t); -} - -//direct trait conversion, 1st candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const time_point*) - -> decltype(conv_clock(t)) -{ - return conv_clock(t); -} - -//conversion through sys, 2nd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const void*) - -> decltype(conv_clock(conv_clock(t))) -{ - return conv_clock(conv_clock(t)); -} - -//conversion through utc, 2nd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const void*) - -> decltype(0, // MSVC_WORKAROUND - conv_clock(conv_clock(t))) -{ - return conv_clock(conv_clock(t)); -} - -//conversion through sys and utc, 3rd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, ...) - -> decltype(conv_clock(conv_clock(conv_clock(t)))) -{ - return conv_clock(conv_clock(conv_clock(t))); -} - -//conversion through utc and sys, 3rd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, ...) - -> decltype(0, // MSVC_WORKAROUND - conv_clock(conv_clock(conv_clock(t)))) -{ - return conv_clock(conv_clock(conv_clock(t))); -} - -} // namespace clock_cast_detail - -template -CONSTCD14 -auto -clock_cast(const std::chrono::time_point& tp) - -> decltype(clock_cast_detail::cc_impl(tp, &tp)) -{ - return clock_cast_detail::cc_impl(tp, &tp); -} - -// Deprecated API - -template -inline -sys_time::type> -to_sys_time(const utc_time& t) -{ - return utc_clock::to_sys(t); -} - -template -inline -sys_time::type> -to_sys_time(const tai_time& t) -{ - return utc_clock::to_sys(tai_clock::to_utc(t)); -} - -template -inline -sys_time::type> -to_sys_time(const gps_time& t) -{ - return utc_clock::to_sys(gps_clock::to_utc(t)); -} - - -template -inline -utc_time::type> -to_utc_time(const sys_time& t) -{ - return utc_clock::from_sys(t); -} - -template -inline -utc_time::type> -to_utc_time(const tai_time& t) -{ - return tai_clock::to_utc(t); -} - -template -inline -utc_time::type> -to_utc_time(const gps_time& t) -{ - return gps_clock::to_utc(t); -} - - -template -inline -tai_time::type> -to_tai_time(const sys_time& t) -{ - return tai_clock::from_utc(utc_clock::from_sys(t)); -} - -template -inline -tai_time::type> -to_tai_time(const utc_time& t) -{ - return tai_clock::from_utc(t); -} - -template -inline -tai_time::type> -to_tai_time(const gps_time& t) -{ - return tai_clock::from_utc(gps_clock::to_utc(t)); -} - - -template -inline -gps_time::type> -to_gps_time(const sys_time& t) -{ - return gps_clock::from_utc(utc_clock::from_sys(t)); -} - -template -inline -gps_time::type> -to_gps_time(const utc_time& t) -{ - return gps_clock::from_utc(t); -} - -template -inline -gps_time::type> -to_gps_time(const tai_time& t) -{ - return gps_clock::from_utc(tai_clock::to_utc(t)); -} - -} // namespace date - -#endif // TZ_H diff --git a/third_party/date/include/date/tz_private.h b/third_party/date/include/date/tz_private.h deleted file mode 100644 index aec01d045..000000000 --- a/third_party/date/include/date/tz_private.h +++ /dev/null @@ -1,316 +0,0 @@ -#ifndef TZ_PRIVATE_H -#define TZ_PRIVATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -#include "tz.h" -#else -#include "date.h" -#include -#endif - -namespace date -{ - -namespace detail -{ - -#if !USE_OS_TZDB - -enum class tz {utc, local, standard}; - -//forward declare to avoid warnings in gcc 6.2 -class MonthDayTime; -std::istream& operator>>(std::istream& is, MonthDayTime& x); -std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); - - -class MonthDayTime -{ -private: - struct pair - { -#if defined(_MSC_VER) && (_MSC_VER < 1900) - pair() : month_day_(date::jan / 1), weekday_(0U) {} - - pair(const date::month_day& month_day, const date::weekday& weekday) - : month_day_(month_day), weekday_(weekday) {} -#endif - - date::month_day month_day_; - date::weekday weekday_; - }; - - enum Type {month_day, month_last_dow, lteq, gteq}; - - Type type_{month_day}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - date::month_day month_day_; - date::month_weekday_last month_weekday_last_; - pair month_day_weekday_; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - U() : month_day_{date::jan/1} {} -#else - U() : - month_day_(date::jan/1), - month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) - {} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - - U& operator=(const date::month_day& x); - U& operator=(const date::month_weekday_last& x); - U& operator=(const pair& x); - } u; - - std::chrono::hours h_{0}; - std::chrono::minutes m_{0}; - std::chrono::seconds s_{0}; - tz zone_{tz::local}; - -public: - MonthDayTime() = default; - MonthDayTime(local_seconds tp, tz timezone); - MonthDayTime(const date::month_day& md, tz timezone); - - date::day day() const; - date::month month() const; - tz zone() const {return zone_;} - - void canonicalize(date::year y); - - sys_seconds - to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; - sys_days to_sys_days(date::year y) const; - - sys_seconds to_time_point(date::year y) const; - int compare(date::year y, const MonthDayTime& x, date::year yx, - std::chrono::seconds offset, std::chrono::minutes prev_save) const; - - friend std::istream& operator>>(std::istream& is, MonthDayTime& x); - friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); -}; - -// A Rule specifies one or more set of datetimes without using an offset. -// Multiple dates are specified with multiple years. The years in effect -// go from starting_year_ to ending_year_, inclusive. starting_year_ <= -// ending_year_. save_ is in effect for times from the specified time -// onward, including the specified time. When the specified time is -// local, it uses the save_ from the chronologically previous Rule, or if -// there is none, 0. - -//forward declare to avoid warnings in gcc 6.2 -class Rule; -bool operator==(const Rule& x, const Rule& y); -bool operator<(const Rule& x, const Rule& y); -bool operator==(const Rule& x, const date::year& y); -bool operator<(const Rule& x, const date::year& y); -bool operator==(const date::year& x, const Rule& y); -bool operator<(const date::year& x, const Rule& y); -bool operator==(const Rule& x, const std::string& y); -bool operator<(const Rule& x, const std::string& y); -bool operator==(const std::string& x, const Rule& y); -bool operator<(const std::string& x, const Rule& y); -std::ostream& operator<<(std::ostream& os, const Rule& r); - -class Rule -{ -private: - std::string name_; - date::year starting_year_{0}; - date::year ending_year_{0}; - MonthDayTime starting_at_; - std::chrono::minutes save_{0}; - std::string abbrev_; - -public: - Rule() = default; - explicit Rule(const std::string& s); - Rule(const Rule& r, date::year starting_year, date::year ending_year); - - const std::string& name() const {return name_;} - const std::string& abbrev() const {return abbrev_;} - - const MonthDayTime& mdt() const {return starting_at_;} - const date::year& starting_year() const {return starting_year_;} - const date::year& ending_year() const {return ending_year_;} - const std::chrono::minutes& save() const {return save_;} - - static void split_overlaps(std::vector& rules); - - friend bool operator==(const Rule& x, const Rule& y); - friend bool operator<(const Rule& x, const Rule& y); - friend bool operator==(const Rule& x, const date::year& y); - friend bool operator<(const Rule& x, const date::year& y); - friend bool operator==(const date::year& x, const Rule& y); - friend bool operator<(const date::year& x, const Rule& y); - friend bool operator==(const Rule& x, const std::string& y); - friend bool operator<(const Rule& x, const std::string& y); - friend bool operator==(const std::string& x, const Rule& y); - friend bool operator<(const std::string& x, const Rule& y); - - friend std::ostream& operator<<(std::ostream& os, const Rule& r); - -private: - date::day day() const; - date::month month() const; - static void split_overlaps(std::vector& rules, std::size_t i, std::size_t& e); - static bool overlaps(const Rule& x, const Rule& y); - static void split(std::vector& rules, std::size_t i, std::size_t k, - std::size_t& e); -}; - -inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);} -inline bool operator> (const Rule& x, const Rule& y) {return y < x;} -inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);} -inline bool operator> (const Rule& x, const date::year& y) {return y < x;} -inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);} - -inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);} -inline bool operator> (const date::year& x, const Rule& y) {return y < x;} -inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);} -inline bool operator> (const Rule& x, const std::string& y) {return y < x;} -inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);} - -inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);} -inline bool operator> (const std::string& x, const Rule& y) {return y < x;} -inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} - -struct zonelet -{ - enum tag {has_rule, has_save, is_empty}; - - std::chrono::seconds gmtoff_; - tag tag_ = has_rule; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - std::string rule_; - std::chrono::minutes save_; - - ~U() {} - U() {} - U(const U&) {} - U& operator=(const U&) = delete; - } u; - - std::string format_; - date::year until_year_{0}; - MonthDayTime until_date_; - sys_seconds until_utc_; - local_seconds until_std_; - local_seconds until_loc_; - std::chrono::minutes initial_save_{0}; - std::string initial_abbrev_; - std::pair first_rule_{nullptr, date::year::min()}; - std::pair last_rule_{nullptr, date::year::max()}; - - ~zonelet(); - zonelet(); - zonelet(const zonelet& i); - zonelet& operator=(const zonelet&) = delete; -}; - -#else // USE_OS_TZDB - -struct ttinfo -{ - std::int32_t tt_gmtoff; - unsigned char tt_isdst; - unsigned char tt_abbrind; - unsigned char pad[2]; -}; - -static_assert(sizeof(ttinfo) == 8, ""); - -struct expanded_ttinfo -{ - std::chrono::seconds offset; - std::string abbrev; - bool is_dst; -}; - -struct transition -{ - sys_seconds timepoint; - const expanded_ttinfo* info; - - transition(sys_seconds tp, const expanded_ttinfo* i = nullptr) - : timepoint(tp) - , info(i) - {} - - friend - std::ostream& - operator<<(std::ostream& os, const transition& t) - { - using date::operator<<; - os << t.timepoint << "Z "; - if (t.info->offset >= std::chrono::seconds{0}) - os << '+'; - os << make_time(t.info->offset); - if (t.info->is_dst > 0) - os << " daylight "; - else - os << " standard "; - os << t.info->abbrev; - return os; - } -}; - -#endif // USE_OS_TZDB - -} // namespace detail - -} // namespace date - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz.h" -#endif - -#endif // TZ_PRIVATE_H diff --git a/third_party/thread_pool/CMakeLists.txt b/third_party/thread_pool/CMakeLists.txt deleted file mode 100644 index d60e2e21d..000000000 --- a/third_party/thread_pool/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_library(thread_pool INTERFACE) -target_include_directories(thread_pool INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) \ No newline at end of file diff --git a/third_party/thread_pool/include/ThreadPool.h b/third_party/thread_pool/include/ThreadPool.h deleted file mode 100644 index 0c23c04af..000000000 --- a/third_party/thread_pool/include/ThreadPool.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef THREAD_POOL_H -#define THREAD_POOL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class ThreadPool { -public: - ThreadPool(size_t); - template - auto enqueue(F&& f, Args&&... args) - -> std::future::type>; - ~ThreadPool(); -private: - // need to keep track of threads so we can join them - std::vector< std::thread > workers; - // the task queue - std::queue< std::function > tasks; - - // synchronization - std::mutex queue_mutex; - std::condition_variable condition; - bool stop; -}; - -// the constructor just launches some amount of workers -inline ThreadPool::ThreadPool(size_t threads) : stop(false) { - for (size_t i = 0; i < threads; ++i) - workers.emplace_back([this] { - for (;;) { - std::function task; - - { - std::unique_lock lock(this->queue_mutex); - this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); - if (this->stop && this->tasks.empty()) { - return; - } - task = std::move(this->tasks.front()); - this->tasks.pop(); - } - - task(); - } - }); -} - -// add new work item to the pool -template -auto ThreadPool::enqueue(F&& f, Args&&... args) - -> std::future::type> -{ - using return_type = typename std::result_of::type; - - auto task = std::make_shared< std::packaged_task >( - std::bind(std::forward(f), std::forward(args)...) - ); - - std::future res = task->get_future(); - { - std::unique_lock lock(queue_mutex); - - // don't allow enqueueing after stopping the pool - if(stop) - throw std::runtime_error("enqueue on stopped ThreadPool"); - - tasks.emplace([task](){ (*task)(); }); - } - condition.notify_one(); - return res; -} - -// the destructor joins all threads -inline ThreadPool::~ThreadPool() -{ - { - std::unique_lock lock(queue_mutex); - stop = true; - } - condition.notify_all(); - for(std::thread &worker: workers) - worker.join(); -} - -#endif From e74408a4bda337acaf880b7419f469d49b50f7a9 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 23 Aug 2021 21:08:45 +0800 Subject: [PATCH 046/133] Support tracing (#14) Support tracing through OpenCensus and transform to OpenTelemetry on exporting --- .gitignore | 1 + api/rocketmq/AsyncCallback.h | 2 +- api/rocketmq/ErrorCode.h | 2 + api/rocketmq/MQMessage.h | 15 +- api/rocketmq/MQMessageExt.h | 8 +- api/rocketmq/SendResult.h | 7 + bazel/rocketmq_deps.bzl | 26 +- example/rocketmq/ExampleAsyncProducer.cpp | 2 +- src/main/cpp/base/MQMessage.cpp | 25 +- src/main/cpp/base/MQMessageExt.cpp | 6 - src/main/cpp/base/MessageAccessor.cpp | 5 + src/main/cpp/base/MixAll.cpp | 32 +- src/main/cpp/base/include/MessageAccessor.h | 4 +- src/main/cpp/base/include/MixAll.h | 31 +- src/main/cpp/client/BUILD.bazel | 1 - src/main/cpp/client/ClientManagerImpl.cpp | 93 +---- src/main/cpp/client/include/ClientManager.h | 2 + .../cpp/client/include/ClientManagerImpl.h | 4 +- .../client/mocks/include/ClientManagerMock.h | 2 + src/main/cpp/rocketmq/BUILD.bazel | 1 + src/main/cpp/rocketmq/ClientImpl.cpp | 4 + .../ConsumeStandardMessageService.cpp | 133 +++++-- src/main/cpp/rocketmq/ProducerImpl.cpp | 156 +++++++-- src/main/cpp/rocketmq/SendCallbacks.cpp | 40 ++- src/main/cpp/rocketmq/TransactionImpl.cpp | 4 +- src/main/cpp/rocketmq/include/ClientImpl.h | 2 + src/main/cpp/rocketmq/include/ProducerImpl.h | 18 +- src/main/cpp/rocketmq/include/SendCallbacks.h | 61 +++- .../cpp/rocketmq/include/TransactionImpl.h | 5 +- src/main/cpp/tracing/exporters/BUILD.bazel | 18 + .../cpp/tracing/exporters/OtlpExporter.cpp | 328 ++++++++++++++++++ .../tracing/exporters/include/OtlpExporter.h | 111 ++++++ .../ConsumeStandardMessageServiceTest.cpp | 10 + .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 14 +- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 9 +- .../cpp/ut/rocketmq/SendCallbacksTest.cpp | 7 +- src/test/cpp/ut/tracing/exporters/BUILD.bazel | 24 ++ .../tracing/exporters/OtlpExportersTest.cpp | 51 +++ .../ut/tracing/exporters/SpanContextTest.cpp | 10 + 39 files changed, 1045 insertions(+), 229 deletions(-) create mode 100644 src/main/cpp/tracing/exporters/BUILD.bazel create mode 100644 src/main/cpp/tracing/exporters/OtlpExporter.cpp create mode 100644 src/main/cpp/tracing/exporters/include/OtlpExporter.h create mode 100644 src/test/cpp/ut/tracing/exporters/BUILD.bazel create mode 100644 src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp create mode 100644 src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp diff --git a/.gitignore b/.gitignore index 2d9a80cb5..f3ec0b2d6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ cmake-build-debug-coverage .DS_Store external bazel-apache_rocketmq_client_cpp +bazel-rocketmq-client-cpp diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index 6bf4a56a0..44d89cdee 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -15,7 +15,7 @@ using sendCallbackType = SendCallbackType; class SendCallback : public AsyncCallback { public: virtual ~SendCallback() = default; - virtual void onSuccess(const SendResult& send_result) = 0; + virtual void onSuccess(SendResult& send_result) = 0; virtual void onException(const MQException& e) = 0; virtual SendCallbackType getSendCallbackType() { return SendCallbackType::noAutoDeleteSendCallback; } }; diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index 89d00e8c5..fa6cdf0d7 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -22,4 +22,6 @@ constexpr int BAD_CONFIGURATION = -8; constexpr int MESSAGE_QUEUE_ILLEGAL = -9; +constexpr int ERR_INVALID_MAX_ATTEMPT_TIME = -10; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 76a6387de..7efa448e8 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -1,10 +1,11 @@ #pragma once +#include #include #include #include -#include #include +#include #include "RocketMQ.h" @@ -26,6 +27,8 @@ class MQMessage { MQMessage(const MQMessage& other); MQMessage& operator=(const MQMessage& other); + const std::string& getMsgId() const; + void setProperty(const std::string& name, const std::string& value); std::string getProperty(const std::string& name) const; @@ -40,7 +43,8 @@ class MQMessage { /** * @brief Add a unique key for the message - * TODO: a message may be associated with multiple keys. setKey, actually mean attach the given key to the message. Better rename it. + * TODO: a message may be associated with multiple keys. setKey, actually mean attach the given key to the message. + * Better rename it. * @param key Unique key in perspective of bussiness logic. */ void setKey(const std::string& key); @@ -49,6 +53,13 @@ class MQMessage { int getDelayTimeLevel() const; void setDelayTimeLevel(int level); + const std::string& traceContext() const; + void traceContext(const std::string& trace_context); + + std::string getBornHost() const; + + std::chrono::system_clock::time_point deliveryTimestamp() const; + const std::string& getBody() const; void setBody(const char* data, int len); void setBody(const std::string& body); diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h index e1328a699..c814c8ed3 100644 --- a/api/rocketmq/MQMessageExt.h +++ b/api/rocketmq/MQMessageExt.h @@ -1,7 +1,7 @@ #pragma once -#include #include "MQMessage.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -24,23 +24,17 @@ class MQMessageExt : public MQMessage { std::chrono::system_clock::time_point bornTimestamp() const; - std::string getBornHost() const; - std::chrono::system_clock::time_point storeTimestamp() const; int64_t getStoreTimestamp() const; std::string getStoreHost() const; - const std::string& getMsgId() const; - int64_t getQueueOffset() const; int32_t getDeliveryAttempt() const; const std::string& receiptHandle() const; - const std::string& traceContext() const; - bool operator==(const MQMessageExt& other); friend class MessageAccessor; diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 681943cac..9eba74699 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -76,6 +76,12 @@ class SendResult { transaction_id_ = transaction_id; } + const std::string &traceContext() const { return trace_context_; } + + void traceContext(std::string trace_context) { + trace_context_ = std::move(trace_context); + } + private: SendStatus send_status_; std::string message_id_; @@ -83,6 +89,7 @@ class SendResult { long long queue_offset_; std::string transaction_id_; std::string region_id_; + std::string trace_context_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index 1b7e8beba..6b59155d7 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -1,7 +1,6 @@ """Load dependencies needed to compile and test the RocketMQ library as a 3rd-party consumer.""" - +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") def rocketmq_deps(): """Loads dependencies need to compile and test the RocketMQ library.""" @@ -89,12 +88,23 @@ def rocketmq_deps(): urls = ["https://github.com/grpc/grpc/archive/v1.38.0.tar.gz"], ) - if "com_github_opentelemetry" not in native.existing_rules(): + if "io_opentelemetry_cpp" not in native.existing_rules(): http_archive( - name = "com_github_opentelemetry", - sha256 = "2b5b423096c6e486ec23e77d0d75b28e32f86e7cdf6e7560da686c71c69908d3", - strip_prefix = "opentelemetry-cpp-1.0.0-rc2", + name = "io_opentelemetry_cpp", + sha256 = "24ba9b83f6cb8ba717ae30ebc570f5e8d0569008aee3c8b9a7ce6e4e1a5115b7", + strip_prefix = "opentelemetry-cpp-1.0.0-rc4", urls = [ - "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.0.0-rc2.tar.gz", + "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.0.0-rc4.tar.gz", ], - ) \ No newline at end of file + ) + + maybe( + http_archive, + name = "com_github_opentelemetry_proto", + build_file = "@io_opentelemetry_cpp//bazel:opentelemetry_proto.BUILD", + sha256 = "08f090570e0a112bfae276ba37e9c45bf724b64d902a7a001db33123b840ebd6", + strip_prefix = "opentelemetry-proto-0.6.0", + urls = [ + "https://github.com/open-telemetry/opentelemetry-proto/archive/v0.6.0.tar.gz", + ], + ) \ No newline at end of file diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 40c84ff30..b59534eee 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -137,7 +137,7 @@ class SampleSendCallback : public rocketmq::SendCallback { public: SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) {} - void onSuccess(const SendResult& send_result) override { counter_.fetch_add(1, std::memory_order_relaxed); } + void onSuccess(SendResult& send_result) override { counter_.fetch_add(1, std::memory_order_relaxed); } void onException(const MQException& e) override { error_.fetch_add(1, std::memory_order_relaxed); } diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 7a42d1e2d..68b26bc0a 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -1,7 +1,11 @@ #include "rocketmq/MQMessage.h" -#include "MessageImpl.h" #include "MessageAccessor.h" +#include "MessageImpl.h" +#include "MixAll.h" +#include "UniqueIdGenerator.h" +#include "UtilAll.h" #include "rocketmq/MQMessageExt.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -20,6 +24,11 @@ MQMessage::MQMessage(const std::string& topic, const std::string& tags, const st if (!keys.empty()) { impl_->system_attribute_.keys.emplace_back(keys); } + + impl_->system_attribute_.born_host = UtilAll::hostname(); + impl_->system_attribute_.born_timestamp = absl::Now(); + impl_->system_attribute_.message_id = UniqueIdGenerator::instance().next(); + impl_->body_.clear(); impl_->body_.reserve(body.length()); impl_->body_.append(body.data(), body.length()); @@ -37,6 +46,14 @@ MQMessage& MQMessage::operator=(const MQMessage& other) { return *this; } +const std::string& MQMessage::getMsgId() const { return impl_->system_attribute_.message_id; } + +std::string MQMessage::getBornHost() const { return impl_->system_attribute_.born_host; } + +std::chrono::system_clock::time_point MQMessage::deliveryTimestamp() const { + absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); +} + void MQMessage::setProperty(const std::string& name, const std::string& value) { impl_->user_attribute_map_[name] = value; } @@ -69,6 +86,12 @@ int MQMessage::getDelayTimeLevel() const { return impl_->system_attribute_.delay void MQMessage::setDelayTimeLevel(int level) { impl_->system_attribute_.delay_level = level; } +const std::string& MQMessage::traceContext() const { return impl_->system_attribute_.trace_context; } + +void MQMessage::traceContext(const std::string& trace_context) { + impl_->system_attribute_.trace_context = trace_context; +} + const std::string& MQMessage::getBody() const { return impl_->body_; } void MQMessage::setBody(const char* body, int len) { diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp index 84a1aca3f..a108138ff 100644 --- a/src/main/cpp/base/MQMessageExt.cpp +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -24,8 +24,6 @@ std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { int64_t MQMessageExt::getBornTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); } -std::string MQMessageExt::getBornHost() const { return impl_->system_attribute_.born_host; } - std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); } @@ -34,16 +32,12 @@ int64_t MQMessageExt::getStoreTimestamp() const { return absl::ToUnixMillis(impl std::string MQMessageExt::getStoreHost() const { return impl_->system_attribute_.store_host; } -const std::string& MQMessageExt::getMsgId() const { return impl_->system_attribute_.message_id; } - int64_t MQMessageExt::getQueueOffset() const { return impl_->system_attribute_.partition_offset; } int32_t MQMessageExt::getDeliveryAttempt() const { return impl_->system_attribute_.attempt_times; } const std::string& MQMessageExt::receiptHandle() const { return impl_->system_attribute_.receipt_handle; } -const std::string& MQMessageExt::traceContext() const { return impl_->system_attribute_.trace_context; } - bool MQMessageExt::operator==(const MQMessageExt& other) { return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; } diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp index ba4307731..5ed587868 100644 --- a/src/main/cpp/base/MessageAccessor.cpp +++ b/src/main/cpp/base/MessageAccessor.cpp @@ -1,5 +1,6 @@ #include "MessageAccessor.h" #include "MessageImpl.h" +#include "rocketmq/MQMessage.h" ROCKETMQ_NAMESPACE_BEGIN @@ -43,6 +44,10 @@ void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time deco message.impl_->system_attribute_.decode_timestamp = decode_timestamp; } +absl::Time MessageAccessor::decodedTimestamp(const MQMessageExt& message) { + return message.impl_->system_attribute_.decode_timestamp; +} + void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { message.impl_->system_attribute_.invisible_period = invisible_period; } diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 7d0316165..e9564d80a 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -10,10 +10,10 @@ #include #include -#include -#include #include "zlib.h" #include +#include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -44,6 +44,34 @@ const std::string MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_ = "AVG"; const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; +const char* MixAll::MESSAGE_KEY_SEPARATOR = " "; + +// Span name list +const char* MixAll::SPAN_NAME_SEND_MESSAGE = "SendMessage"; +const char* MixAll::SPAN_NAME_END_TRANSACTION = "EndTransaction"; +const char* MixAll::SPAN_NAME_AWAIT_CONSUMPTION = "AwaitingConsumption"; +const char* MixAll::SPAN_NAME_CONSUME_MESSAGE = "ConsumeMessage"; +const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; + +// Span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_ACCESS_KEY = "ak"; +const char* MixAll::SPAN_ATTRIBUTE_ARN = "arn"; +const char* MixAll::SPAN_ATTRIBUTE_KEYS = "keys"; +const char* MixAll::SPAN_ATTRIBUTE_MESSAGE_TYPE = "msgType"; +const char* MixAll::SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP = "deliveryTimestamp"; +const char* MixAll::SPAN_ATTRIBUTE_TOPIC = "topic"; +const char* MixAll::SPAN_ATTRIBUTE_GROUP = "group"; +const char* MixAll::SPAN_ATTRIBUTE_MESSAGE_ID = "msgId"; +const char* MixAll::SPAN_ATTRIBUTE_TAG = "tags"; +const char* MixAll::SPAN_ATTRIBUTE_HOST = "host"; +const char* MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME = "attempt"; +const char* MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION = "commitAction"; +const char* MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP = "availableTimestamp"; +const char* MixAll::SPAN_ATTRIBUTE_BATCH_SIZE = "batchSize"; + +// Span annotation +const char* MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION = "__await_consumption"; +const char* MixAll::SPAN_ANNOTATION_ATTR_START_TIME = "__start_time"; bool MixAll::validate(const MQMessage& message) { if (message.getTopic().empty()) { diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h index c97dd13f7..9f6ca757e 100644 --- a/src/main/cpp/base/include/MessageAccessor.h +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -28,7 +28,9 @@ class MessageAccessor { static void setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times); - static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); + static void setDecodedTimestamp(MQMessageExt &message, absl::Time decode_timestamp); + static absl::Time decodedTimestamp(const MQMessageExt &message); + static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); static void setReceiptHandle(MQMessageExt& message, std::string receipt_handle); diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 1b0029f7a..5b3df864c 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -1,11 +1,11 @@ #pragma once #include "re2/re2.h" +#include "rocketmq/MQMessage.h" #include #include #include #include -#include "rocketmq/MQMessage.h" ROCKETMQ_NAMESPACE_BEGIN @@ -40,6 +40,35 @@ class MixAll { static const char* HOME_PROFILE_ENV_; + static const char* MESSAGE_KEY_SEPARATOR; + + // Tracing span name list + static const char* SPAN_NAME_SEND_MESSAGE; + static const char* SPAN_NAME_END_TRANSACTION; + static const char* SPAN_NAME_AWAIT_CONSUMPTION; + static const char* SPAN_NAME_CONSUME_MESSAGE; + static const char* SPAN_NAME_PULL_MESSAGE; + + // Tracing attribute name list + static const char* SPAN_ATTRIBUTE_ACCESS_KEY; + static const char* SPAN_ATTRIBUTE_ARN; + static const char* SPAN_ATTRIBUTE_TOPIC; + static const char* SPAN_ATTRIBUTE_GROUP; + static const char* SPAN_ATTRIBUTE_MESSAGE_ID; + static const char* SPAN_ATTRIBUTE_TAG; + static const char* SPAN_ATTRIBUTE_KEYS; + static const char* SPAN_ATTRIBUTE_HOST; + static const char* SPAN_ATTRIBUTE_MESSAGE_TYPE; + static const char* SPAN_ATTRIBUTE_ATTEMPT_TIME; + static const char* SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION; + static const char* SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_BATCH_SIZE; + + // Tracing annotation + static const char* SPAN_ANNOTATION_AWAIT_CONSUMPTION; + static const char* SPAN_ANNOTATION_ATTR_START_TIME; + template static int64_t millisecondsOf(std::chrono::duration duration) { return std::chrono::duration_cast(duration).count(); } diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index ab5eae81e..a19626e8b 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -17,7 +17,6 @@ cc_library( "@com_google_absl//absl/container:flat_hash_set", "@com_github_grpc_grpc//:grpc_secure", "@com_github_grpc_grpc//:grpc++", - "@com_github_opentelemetry//api", "@boringssl//:ssl", "//external:gtest", ], diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index c7d4b103e..bd1a1e7c3 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -20,10 +20,6 @@ #include #include -#ifdef ENABLE_TRACING -#include "TracingUtility.h" -#endif - ROCKETMQ_NAMESPACE_BEGIN ClientManagerImpl::ClientManagerImpl(std::string arn) @@ -201,79 +197,6 @@ void ClientManagerImpl::doHealthCheck() { SPDLOG_DEBUG("Health check completed"); } -/** - * TODO:(lingchu) optimize implementation. Client may just select one broker by random. - */ -#ifdef ENABLE_TRACING -void ClientInstance::updateTraceProvider() { - SPDLOG_DEBUG("Start to update global trace provider"); - if (State::STARTED != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); - return; - } - absl::flat_hash_set exporter_endpoint_set; - { - absl::MutexLock lock(&topic_route_table_mtx_); - for (const auto& route_entry : topic_route_table_) { - for (const auto& partition : route_entry.second->partitions()) { - const std::string& broker_name = partition.broker().name(); - if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { - continue; - } - exporter_endpoint_set.insert(partition.broker().serviceAddress()); - } - } - } - { - absl::MutexLock lock(&exporter_endpoint_set_mtx_); - // Available endpoint was not changed, no need to change global trace provider. - if (exporter_endpoint_set == exporter_endpoint_set_) { - return; - } - - // TODO: support ipv6. - std::string exporter_endpoint; - if (!exporter_endpoint_set.empty()) { - exporter_endpoint = absl::StrJoin(exporter_endpoint_set.begin(), exporter_endpoint_set.end(), ","); - } - - opentelemetry::exporter::otlp::OtlpExporterOptions exporter_options; - // If no available export, use default export here. - if (!exporter_endpoint_set.empty()) { - exporter_options.endpoint = exporter_endpoint; - } - - auto exporter = std::unique_ptr(new otlp::OtlpExporter(exporter_options)); - - sdktrace::BatchSpanProcessorOptions options{}; - options.max_queue_size = 32768; - options.max_export_batch_size = 16384; - options.schedule_delay_millis = std::chrono::milliseconds(1000); - - auto processor = - std::shared_ptr(new sdktrace::BatchSpanProcessor(std::move(exporter), options)); - auto provider = nostd::shared_ptr(new sdktrace::TracerProvider(processor)); - // Set the global trace provider - trace::Provider::SetTracerProvider(provider); - - exporter_endpoint_set_.clear(); - exporter_endpoint_set_.merge(exporter_endpoint_set); - } -} - -nostd::shared_ptr ClientInstance::getTracer() { - { - absl::MutexLock lock(&exporter_endpoint_set_mtx_); - if (exporter_endpoint_set_.empty()) { - return nostd::shared_ptr(nullptr); - } - auto provider = trace::Provider::GetTracerProvider(); - return provider->GetTracer("RocketmqClient"); - } -} - -#endif - void ClientManagerImpl::cleanOfflineRpcClients() { absl::flat_hash_set hosts; { @@ -474,6 +397,22 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met return true; } + +/** + * @brief Create a gRPC channel to target host. + * + * @param target_host + * @return std::shared_ptr + */ +std::shared_ptr ClientManagerImpl::createChannel(const std::string& target_host) { + std::vector> interceptor_factories; + interceptor_factories.emplace_back(absl::make_unique()); + auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( + target_host, channel_credential_, channel_arguments_, std::move(interceptor_factories)); + return channel; +} + + RpcClientSharedPtr ClientManagerImpl::getRpcClient(const std::string& target_host, bool need_heartbeat) { std::shared_ptr client; { diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 58e4717a4..0df570b52 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -24,6 +24,8 @@ class ClientManager { virtual TopAddressing& topAddressing() = 0; + virtual std::shared_ptr createChannel(const std::string& target_host) = 0; + virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, const std::function& cb) = 0; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index aae3d670e..4a80d18fa 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -28,7 +28,7 @@ #include "src/cpp/server/thread_pool_interface.h" #ifdef ENABLE_TRACING -#include "opentelemetry/exporters/otlp/otlp_exporter.h" +#include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" #include "opentelemetry/sdk/trace/batch_span_processor.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/trace/provider.h" @@ -58,6 +58,8 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share static void assignLabels(Histogram& histogram); + std::shared_ptr createChannel(const std::string& target_host) override; + /** * Resolve route data from name server for the given topic. * diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index ca8af8ee5..56e874828 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -15,6 +15,8 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(TopAddressing&, topAddressing, (), (override)); + MOCK_METHOD((std::shared_ptr), createChannel, (const std::string&), (override)); + MOCK_METHOD(void, resolveRoute, (const std::string&, const Metadata&, const QueryRouteRequest&, std::chrono::milliseconds, (const std::function&)), diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index cd4a32423..498fa3987 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -9,6 +9,7 @@ cc_library( strip_include_prefix = "//src/main/cpp/rocketmq/include", deps = [ "//src/main/cpp/client:client_library", + "//src/main/cpp/tracing/exporters:otlp_exporter", "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", "@com_google_absl//absl/types:optional", diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 428088b6a..f382e0362 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -25,6 +25,10 @@ void ClientImpl::start() { client_manager_ = ClientManagerFactory::getInstance().getClientManager(*this); client_manager_->start(); + + exporter_ = std::make_shared(client_manager_, this); + exporter_->start(); + bool update_name_server_list = false; { absl::MutexLock lock(&name_server_list_mtx_); diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 44739cabe..874d64d81 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -1,17 +1,29 @@ #include "ConsumeMessageService.h" #include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "MixAll.h" +#include "OtlpExporter.h" #include "Protocol.h" #include "PushConsumer.h" #include "TracingUtility.h" +#include "UtilAll.h" #include "absl/memory/memory.h" #include "absl/strings/str_join.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" #include "rocketmq/ConsumeType.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MessageListener.h" #include +#include +#include ROCKETMQ_NAMESPACE_BEGIN -ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, - int thread_count, MessageListener* message_listener_ptr) +ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener_ptr) : ConsumeMessageService(std::move(consumer), thread_count, message_listener_ptr) {} void ConsumeStandardMessageService::start() { @@ -90,14 +102,6 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce if (!consumer) { return; } - // consumer does not start yet. -#ifdef ENABLE_TRACING - nostd::shared_ptr tracer = consumer_shared_ptr->getTracer(); - if (!tracer) { - return; - } - auto system_start = std::chrono::system_clock::now(); -#endif std::shared_ptr> rate_limiter = rateLimiter(topic); if (rate_limiter) { @@ -108,6 +112,72 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce SPDLOG_DEBUG("{} rate-limit permits acquired", msgs.size()); } + // Record await-consumption-span + { + for (const auto& msg : msgs) { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_AWAIT_CONSUMPTION, span_context, + &Samplers::always()); + } else { + span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_AWAIT_CONSUMPTION, nullptr, {&Samplers::always()}); + } + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, + consumer->credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, msg.getTags()); + const auto& keys = msg.getKeys(); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, msg.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP, + absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); + span.AddAnnotation( + MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, + {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, + opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); + span.End(); + } + } + + // Trace start of consume message + std::vector spans; + { + for (const auto& msg : msgs) { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + auto span = opencensus::trace::Span::BlankSpan(); + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_CONSUME_MESSAGE, span_context); + } else { + span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_CONSUME_MESSAGE); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, + consumer->credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, msg.getTags()); + const auto& keys = msg.getKeys(); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, msg.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP, + absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_BATCH_SIZE, msgs.size()); + spans.emplace_back(std::move(span)); + } + } + auto steady_start = std::chrono::steady_clock::now(); try { @@ -122,14 +192,24 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce auto duration = std::chrono::steady_clock::now() - steady_start; + // Trace end of consumption + { + for (auto& span : spans) { + switch (status) { + case ConsumeMessageResult::SUCCESS: + span.SetStatus(opencensus::trace::StatusCode::OK); + break; + case ConsumeMessageResult::FAILURE: + span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); + break; + } + span.End(); + } + } + // Log client consume-time costs SPDLOG_DEBUG("Business callback spent {}ms processing {} messages.", MixAll::millisecondsOf(duration), msgs.size()); -#ifdef ENABLE_TRACING - std::chrono::microseconds average_duration = - std::chrono::microseconds(MixAll::microsecondsOf(duration) / msgs.size()); -#endif - if (MessageModel::CLUSTERING == consumer->messageModel()) { for (const auto& msg : msgs) { const std::string& message_id = msg.getMsgId(); @@ -137,29 +217,6 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce // Release message number and memory quota process_queue_ptr->release(msg.getBody().size(), msg.getQueueOffset()); -#ifdef ENABLE_TRACING - nostd::shared_ptr span = nostd::shared_ptr(nullptr); - trace::EndSpanOptions end_options; - if (consumer_shared_ptr->isTracingEnabled()) { - const std::string& serialized_span_context = msg.traceContext(); - trace::SpanContext span_context = TracingUtility::extractContextFromTraceParent(serialized_span_context); - trace::StartSpanOptions start_options; - start_options.start_system_time = - opentelemetry::core::SystemTimestamp(system_start + i * std::chrono::microseconds(average_duration)); - start_options.start_steady_time = - opentelemetry::core::SteadyTimestamp(steady_start + i * std::chrono::microseconds(average_duration)); - start_options.parent = span_context; - end_options.end_steady_time = - opentelemetry::core::SteadyTimestamp(steady_start + (i + 1) * std::chrono::microseconds(average_duration)); - span = tracer->StartSpan("ConsumeMessage", start_options); - } - - if (span) { - span->SetAttribute(TracingUtility::get().expired_, false); - span->SetAttribute(TracingUtility::get().success_, CONSUME_SUCCESS == status); - span->End(end_options); - } -#endif if (status == ConsumeMessageResult::SUCCESS) { auto callback = [process_queue_ptr, message_id](bool ok) { if (ok) { diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index dd825bcd7..f6b520a8e 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -1,17 +1,26 @@ #include "ProducerImpl.h" + #include "MessageAccessor.h" #include "MessageGroupQueueSelector.h" #include "MetadataConstants.h" #include "MixAll.h" +#include "OtlpExporter.h" #include "Protocol.h" +#include "RpcClient.h" #include "SendCallbacks.h" #include "SendMessageContext.h" #include "Signature.h" #include "TransactionImpl.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" +#include "absl/strings/str_join.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/Transaction.h" #include ROCKETMQ_NAMESPACE_BEGIN @@ -261,49 +270,82 @@ void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerP transaction_state_checker_ = std::move(checker); } +void ProducerImpl::sendImpl(RetrySendCallback* callback) { + const std::string& target = callback->messageQueue().serviceAddress(); + if (target.empty()) { + SPDLOG_WARN("Failed to resolve broker address from MessageQueue"); + MQClientException e("Failed to resolve broker address", FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE, __FILE__, + __LINE__); + callback->onException(e); + return; + } + + SendMessageRequest request; + wrapSendMessageRequest(callback->message(), request, callback->messageQueue()); + Metadata metadata; + Signature::sign(this, metadata); + + { + // Trace Send RPC + auto& message = callback->message(); + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_SEND_MESSAGE, span_context, + {&Samplers::always()}); + } else { + span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_SEND_MESSAGE, nullptr, {&Samplers::always()}); + } + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, message.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, message.getTags()); + const auto& keys = callback->message().getKeys(); + if (!keys.empty()) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); + } + // Note: attempt-time is 0-based + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, 1 + callback->attemptTime()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + + if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP, + absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); + } + callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); + callback->span() = span; + } + client_manager_->send(target, metadata, request, callback); +} + void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times) { assert(callback); if (list.empty()) { - MQClientException e("Topic route not found", -1, __FILE__, __LINE__); + MQClientException e("Topic route not found", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); callback->onException(e); return; } if (max_attempt_times <= 0) { - MQClientException e("Retry times illegal", BAD_CONFIGURATION, __FILE__, __LINE__); + MQClientException e("Retry times illegal", ERR_INVALID_MAX_ATTEMPT_TIME, __FILE__, __LINE__); callback->onException(e); return; } - - const std::string& target = list[0].serviceAddress(); - if (target.empty()) { - SPDLOG_DEBUG("Unable to resolve broker address"); - THROW_MQ_EXCEPTION(MQClientException, "Failed to resolve broker address from topic route", - FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE); - } - - SendMessageRequest request; - // Unique message-id is generated using IP, timestamp, hash-code and sequence during the transformation - wrapSendMessageRequest(message, request, list[0]); - - absl::flat_hash_map metadata; - - // Sign the request. Metadata entries will be part of HTT2 headers frame - Signature::sign(this, metadata); - - if (max_attempt_times == 1) { - client_manager_->send(target, metadata, request, callback); - return; - } - + MQMessageQueue message_queue = list[0]; auto retry_callback = - new RetrySendCallback(client_manager_, metadata, request, max_attempt_times, callback, std::move(list)); - client_manager_->send(target, metadata, request, retry_callback); + new RetrySendCallback(shared_from_this(), message, max_attempt_times, callback, std::move(list)); + sendImpl(retry_callback); } bool ProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, - const std::string& transaction_id, TransactionState resolution) { + const std::string& transaction_id, TransactionState resolution, + std::string trace_context) { EndTransactionRequest request; request.set_message_id(message_id); @@ -323,25 +365,66 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& Signature::sign(this, metadata); bool completed = false; bool success = false; + // Trace transactional message + opencensus::trace::SpanContext span_context = opencensus::trace::propagation::FromTraceParentHeader(trace_context); + auto span = opencensus::trace::Span::BlankSpan(); + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_END_TRANSACTION, span_context, + {&Samplers::always()}); + } else { + span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_END_TRANSACTION, nullptr, {&Samplers::always()}); + } + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message_id); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + switch (resolution) { + case TransactionState::COMMIT: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "commit"); + break; + case TransactionState::ROLLBACK: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "rollback"); + break; + } + absl::Mutex mtx; absl::CondVar cv; - auto cb = [&](bool rpc_ok, const EndTransactionResponse& response) { + auto cb = [&, span](bool rpc_ok, const EndTransactionResponse& response) { completed = true; if (!rpc_ok) { + { + span.SetStatus(opencensus::trace::StatusCode::ABORTED); + span.AddAnnotation("gRPC tier failure"); + span.End(); + } + SPDLOG_WARN("Failed to send {} transaction request to {}", action, target); success = false; } else if (response.common().status().code() != google::rpc::Code::OK) { + { + span.SetStatus(opencensus::trace::ABORTED); + span.AddAnnotation(response.common().status().DebugString()); + span.End(); + } success = false; SPDLOG_WARN("Server[host={}] failed to {} transaction. Reason: {}", target, action, response.common().DebugString()); } else { + { + span.SetStatus(opencensus::trace::StatusCode::OK); + span.End(); + } success = true; } + { absl::MutexLock lk(&mtx); cv.SignalAll(); } }; + client_manager_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); { absl::MutexLock lk(&mtx); @@ -393,22 +476,23 @@ void ProducerImpl::isolateEndpoint(const std::string& target) { std::unique_ptr ProducerImpl::prepare(const MQMessage& message) { try { SendResult send_result = send(message); - return std::unique_ptr(new TransactionImpl(send_result.getMsgId(), send_result.getTransactionId(), - send_result.getMessageQueue().serviceAddress(), - ProducerImpl::shared_from_this())); + return std::unique_ptr(new TransactionImpl( + send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), + send_result.traceContext(), ProducerImpl::shared_from_this())); } catch (const MQClientException& e) { SPDLOG_ERROR("Failed to send transaction message. Cause: {}", e.what()); return nullptr; } } -bool ProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionState::COMMIT); +bool ProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, + const std::string& trace_context, const std::string& target) { + return endTransaction0(target, message_id, transaction_id, TransactionState::COMMIT, trace_context); } bool ProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, - const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionState::ROLLBACK); + const std::string& trace_context, const std::string& target) { + return endTransaction0(target, message_id, transaction_id, TransactionState::ROLLBACK, trace_context); } void ProducerImpl::asyncPublishInfo(const std::string& topic, @@ -533,7 +617,7 @@ void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transa if (transaction_state_checker_) { TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); const std::string& target_host = MessageAccessor::targetEndpoint(message); - endTransaction0(target_host, message.getMsgId(), transaction_id, state); + endTransaction0(target_host, message.getMsgId(), transaction_id, state, message.traceContext()); } else { SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); } diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index 66027a14d..cddcb6898 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -1,6 +1,11 @@ #include "SendCallbacks.h" +#include "ProducerImpl.h" +#include "TransactionImpl.h" +#include "opencensus/trace/span.h" +#include "opencensus/trace/propagation/trace_context.h" #include "rocketmq/Logger.h" +#include "rocketmq/MQMessageQueue.h" #include "spdlog/spdlog.h" ROCKETMQ_NAMESPACE_BEGIN @@ -9,7 +14,7 @@ void OnewaySendCallback::onException(const MQException& e) { SPDLOG_WARN("Failed to one-way send message. Message: {}", e.what()); } -void OnewaySendCallback::onSuccess(const SendResult& send_result) { +void OnewaySendCallback::onSuccess(SendResult& send_result) { SPDLOG_DEBUG("Send message in one-way OK. MessageId: {}", send_result.getMsgId()); } @@ -25,7 +30,7 @@ void AwaitSendCallback::await() { } } -void AwaitSendCallback::onSuccess(const SendResult& send_result) { +void AwaitSendCallback::onSuccess(SendResult& send_result) { send_result_ = send_result; success_ = true; completed_ = true; @@ -41,13 +46,24 @@ void AwaitSendCallback::onException(const MQException& e) { cv_.SignalAll(); } -void RetrySendCallback::onSuccess(const SendResult& send_result) { +void RetrySendCallback::onSuccess(SendResult& send_result) { + { + // Mark end of send-message span. + span_.SetStatus(opencensus::trace::StatusCode::OK); + span_.End(); + } + send_result.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); callback_->onSuccess(send_result); - SPDLOG_DEBUG(""); delete this; } void RetrySendCallback::onException(const MQException& e) { + { + // Mark end of the send-message span. + span_.SetStatus(opencensus::trace::StatusCode::INTERNAL); + span_.End(); + } + if (++attempt_times_ >= max_attempt_times_) { SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, max_attempt_times_); callback_->onException(e); @@ -55,9 +71,9 @@ void RetrySendCallback::onException(const MQException& e) { return; } - std::shared_ptr client = client_manager_.lock(); - if (!client) { - SPDLOG_WARN("Client instance has destructed"); + std::shared_ptr producer = producer_.lock(); + if (!producer) { + SPDLOG_WARN("Producer has been destructed"); callback_->onException(e); delete this; return; @@ -70,14 +86,8 @@ void RetrySendCallback::onException(const MQException& e) { return; } - const std::string& host = candidates_[attempt_times_ % candidates_.size()].serviceAddress(); - SPDLOG_DEBUG("Retry-send message to {} for {} times", host, attempt_times_); - - // TODO: Sync target broker-name and partition-id with current partition. - request_.mutable_message()->mutable_system_attribute()->set_partition_id( - candidates_[attempt_times_ % candidates_.size()].getQueueId()); - - client->send(host, metadata_, request_, this); + MQMessageQueue message_queue = candidates_[attempt_times_ % candidates_.size()]; + producer->sendImpl(this); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 0ba44e89b..7fc2e5b7e 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -9,7 +9,7 @@ bool TransactionImpl::commit() { return false; } - return producer->commit(message_id_, transaction_id_, endpoint_); + return producer->commit(message_id_, transaction_id_, endpoint_, trace_context_); } bool TransactionImpl::rollback() { @@ -17,7 +17,7 @@ bool TransactionImpl::rollback() { if (!producer) { return false; } - return producer->rollback(message_id_, transaction_id_, endpoint_); + return producer->rollback(message_id_, transaction_id_, endpoint_, trace_context_); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 331126536..83fea0b2c 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -3,6 +3,7 @@ #include "ClientManager.h" #include "ClientResourceBundle.h" #include "InvocationContext.h" +#include "OtlpExporter.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" #include @@ -49,6 +50,7 @@ class ClientImpl : virtual public Client, protected: ClientManagerPtr client_manager_; + std::shared_ptr exporter_; std::atomic state_; absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index d88d2c2d1..c1cd31fc5 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -8,11 +8,13 @@ #include "ClientImpl.h" #include "ClientManagerImpl.h" #include "MixAll.h" +#include "SendCallbacks.h" #include "TopicPublishInfo.h" #include "TransactionImpl.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/LocalTransactionStateChecker.h" #include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" #include "rocketmq/MQSelector.h" #include "rocketmq/SendResult.h" #include "rocketmq/State.h" @@ -50,9 +52,9 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t std::unique_ptr prepare(const MQMessage& message); - bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& target); + bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); - bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& target); + bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); /** * Check if the RPC client for the target host is isolated or not @@ -81,6 +83,16 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void compressBodyThreshold(uint32_t threshold) { compress_body_threshold_ = threshold; } + /** + * @brief Send message with tracing. + * + * @param message + * @param callback + * @param message_queue + * @param attempt_time current attempt times, which starts from 0. + */ + void sendImpl(RetrySendCallback* callback); + protected: std::shared_ptr self() override { return shared_from_this(); } @@ -119,7 +131,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); bool endTransaction0(const std::string& target, const std::string& message_id, const std::string& transaction_id, - TransactionState resolution); + TransactionState resolution, std::string trace_context); void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index 8742f09e8..658e0bb3a 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -1,10 +1,13 @@ #pragma once -#include "ClientManager.h" +#include "TransactionImpl.h" #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "apache/rocketmq/v1/service.grpc.pb.h" +#include "opencensus/trace/span.h" #include "rocketmq/AsyncCallback.h" +#include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageQueue.h" #include ROCKETMQ_NAMESPACE_BEGIN @@ -13,26 +16,26 @@ using SendMessageRequest = apache::rocketmq::v1::SendMessageRequest; class OnewaySendCallback : public SendCallback { public: - void onSuccess(const SendResult& send_result) override; + void onSuccess(SendResult &send_result) override; - void onException(const MQException& e) override; + void onException(const MQException &e) override; }; -OnewaySendCallback* onewaySendCallback(); +OnewaySendCallback *onewaySendCallback(); class AwaitSendCallback : public SendCallback { public: - void onSuccess(const SendResult& send_result) override; + void onSuccess(SendResult &send_result) override; - void onException(const MQException& e) override; + void onException(const MQException &e) override; void await(); explicit operator bool() const { return success_; } - const SendResult& sendResult() const { return send_result_; } + const SendResult &sendResult() const { return send_result_; } - const std::string& errorMessage() const { return error_message_; } + const std::string &errorMessage() const { return error_message_; } private: absl::Mutex mtx_; @@ -43,26 +46,50 @@ class AwaitSendCallback : public SendCallback { std::string error_message_; }; +class ProducerImpl; + class RetrySendCallback : public SendCallback { public: - RetrySendCallback(std::weak_ptr client_manager, absl::flat_hash_map metadata, - SendMessageRequest request, int max_attempt_times, SendCallback* callback, + RetrySendCallback(std::weak_ptr producer, MQMessage message, + int max_attempt_times, SendCallback *callback, std::vector candidates) - : client_manager_(std::move(client_manager)), metadata_(std::move(metadata)), request_(std::move(request)), - max_attempt_times_(max_attempt_times), callback_(callback), candidates_(std::move(candidates)) {} + : producer_(std::move(producer)), message_(std::move(message)), + max_attempt_times_(max_attempt_times), callback_(callback), + candidates_(std::move(candidates)), + span_(opencensus::trace::Span::BlankSpan()) {} + + void onSuccess(SendResult &send_result) override; + + void onException(const MQException &e) override; + + MQMessage &message() { return message_; } - void onSuccess(const SendResult& send_result) override; + int attemptTime() const { return attempt_times_; } - void onException(const MQException& e) override; + const MQMessageQueue &messageQueue() const { + int index = attempt_times_ % candidates_.size(); + return candidates_[index]; + } + + opencensus::trace::Span &span() { return span_; } private: - std::weak_ptr client_manager_; - absl::flat_hash_map metadata_; - SendMessageRequest request_; + std::weak_ptr producer_; + MQMessage message_; int attempt_times_{0}; int max_attempt_times_; SendCallback* callback_; + + /** + * @brief Once the first publish attempt failed, the following routable message queues are employed. + * + */ std::vector candidates_; + + /** + * @brief The on-going span. Should be terminated in the callback functions. + */ + opencensus::trace::Span span_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 18f5d102e..0074a54e8 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -11,10 +11,10 @@ class ProducerImpl; class TransactionImpl : public Transaction { public: - TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, + TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, std::string trace_context, const std::shared_ptr& producer) : message_id_(std::move(message_id)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), - producer_(producer) {} + trace_context_(std::move(trace_context)), producer_(producer) {} ~TransactionImpl() override = default; @@ -26,6 +26,7 @@ class TransactionImpl : public Transaction { std::string message_id_; std::string transaction_id_; std::string endpoint_; + std::string trace_context_; std::weak_ptr producer_; }; diff --git a/src/main/cpp/tracing/exporters/BUILD.bazel b/src/main/cpp/tracing/exporters/BUILD.bazel new file mode 100644 index 000000000..a6687f406 --- /dev/null +++ b/src/main/cpp/tracing/exporters/BUILD.bazel @@ -0,0 +1,18 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "otlp_exporter", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/tracing/exporters/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/client:client_library", + "@com_google_absl//absl/base", + "@com_google_absl//absl/memory", + "@io_opencensus_cpp//opencensus/trace", + "@com_github_opentelemetry_proto//:trace_service_grpc_cc", + "@com_github_grpc_grpc//:grpc++", + "@com_github_fmtlib_fmt//:fmtlib" + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp new file mode 100644 index 000000000..e85557056 --- /dev/null +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -0,0 +1,328 @@ +#include "OtlpExporter.h" +#include "InvocationContext.h" +#include "MixAll.h" +#include "Signature.h" +#include "fmt/format.h" +#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" +#include "opentelemetry/proto/common/v1/common.pb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace trace = opentelemetry::proto::trace::v1; +namespace common = opentelemetry::proto::common::v1; + +opencensus::trace::Sampler& Samplers::always() { + static opencensus::trace::AlwaysSampler sampler; + return sampler; +} + +void OtlpExporter::start() { + std::shared_ptr self = shared_from_this(); + auto handler = absl::make_unique(self); + handler->start(); + opencensus::trace::exporter::SpanExporter::RegisterHandler(std::move(handler)); +} + +void ExportClient::asyncExport(const collector_trace::ExportTraceServiceRequest& request, + InvocationContext* invocation_context) { + auto completion_queue = completion_queue_.lock(); + if (!completion_queue) { + // Server should have shutdown + return; + } + + invocation_context->response_reader = + stub_->PrepareAsyncExport(&invocation_context->context, request, completion_queue.get()); + invocation_context->response_reader->StartCall(); + invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, + invocation_context); +} + +OtlpExporterHandler::OtlpExporterHandler(std::weak_ptr exporter) + : exporter_(std::move(exporter)), completion_queue_(std::make_shared()) { + auto exp = exporter_.lock(); +} + +void OtlpExporterHandler::start() { + poll_thread_ = std::thread(std::bind(&OtlpExporterHandler::poll, this)); + { + absl::MutexLock lk(&start_mtx_); + start_cv_.Wait(&start_mtx_); + } +} + +void OtlpExporterHandler::shutdown() { + stopped_.store(true, std::memory_order_relaxed); + if (poll_thread_.joinable()) { + poll_thread_.join(); + } +} + +void OtlpExporterHandler::syncExportClients() { + auto exp = exporter_.lock(); + if (!exp) { + return; + } + + std::vector&& hosts = exp->hosts(); + { + absl::MutexLock lk(&clients_map_mtx_); + for (auto i = clients_map_.begin(); i != clients_map_.end(); i++) { + if (std::none_of(hosts.cbegin(), hosts.cend(), [&](const std::string& host) { return i->first == host; })) { + clients_map_.erase(i++); + } else { + i++; + } + } + + auto client_manager = exp->clientManager().lock(); + for (const auto& host : hosts) { + if (!clients_map_.contains(host)) { + if (client_manager) { + auto channel = client_manager->createChannel(host); + auto export_client = absl::make_unique(completion_queue_, channel); + clients_map_.emplace(host, std::move(export_client)); + } + } + } + } +} + +void OtlpExporterHandler::poll() { + { + // Notify main thread that the poller thread has started + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + + while (!stopped_.load(std::memory_order_relaxed)) { + bool ok = false; + void* opaque_invocation_context; + while (completion_queue_->Next(&opaque_invocation_context, &ok)) { + auto invocation_context = static_cast(opaque_invocation_context); + if (!ok) { + // the call is dead + SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); + } + invocation_context->onCompletion(ok); + } + SPDLOG_INFO("CompletionQueue is fully drained and shut down"); + } + SPDLOG_INFO("poll completed and quit"); +} + +void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) { + auto exp = exporter_.lock(); + if (!exp) { + return; + } + + switch (exp->traceMode()) { + case TraceMode::OFF: + return; + case TraceMode::DEBUG: { + { + for (const auto& span : spans) { + SPDLOG_INFO("{} --> {}: {}", absl::FormatTime(span.start_time()), absl::FormatTime(span.end_time()), + span.name().data()); + for (const auto& event : span.annotations().events()) { + for (const auto& attr : event.event().attributes()) { + switch (attr.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.bool_value()); + break; + } + } + } + } + } + return; + } + case TraceMode::GRPC: + break; + } + + syncExportClients(); + absl::MutexLock lk(&clients_map_mtx_); + if (clients_map_.empty()) { + SPDLOG_WARN("No exporter client is available"); + return; + } + + uint32_t client_index = round_robin_++ % clients_map_.size(); + + auto iterator = clients_map_.begin(); + for (uint32_t i = 0; i < client_index; i++) { + iterator++; + } + + auto& exporter_client = iterator->second; + + collector_trace::ExportTraceServiceRequest request; + + auto resource = new trace::ResourceSpans(); + auto instrument_library_span = new trace::InstrumentationLibrarySpans(); + for (const auto& span : spans) { + auto item = new trace::Span(); + item->set_trace_id(span.context().trace_id().ToHex()); + item->set_span_id(span.context().span_id().ToHex()); + item->set_parent_span_id(span.parent_span_id().ToHex()); + item->set_name(span.name().data()); + + item->set_start_time_unix_nano(absl::ToUnixNanos(span.start_time())); + item->set_end_time_unix_nano(absl::ToUnixNanos(span.end_time())); + + item->set_kind(trace::Span_SpanKind::Span_SpanKind_SPAN_KIND_CLIENT); + + // OpenCensus has annotations or message_events, which maps to events in OpenTelemetry. + if (!span.message_events().events().empty()) { + for (const auto& event : span.message_events().events()) { + auto ev = new trace::Span::Event(); + ev->set_time_unix_nano(absl::ToUnixNanos(event.timestamp())); + } + item->set_dropped_events_count(span.message_events().dropped_events_count()); + } + + if (!span.annotations().events().empty()) { + for (const auto& annotation : span.annotations().events()) { + // Specialized annotation to adjust span start-time. + // OpenCensus does not expose function to modify span start time. As as result, we need to apply this system + // annotation duration transforming opencensus span to OpenTelemetry span. + if (annotation.event().description() == MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION) { + for (const auto& attr : annotation.event().attributes()) { + if (attr.first == MixAll::SPAN_ANNOTATION_ATTR_START_TIME) { + assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kInt); + item->set_start_time_unix_nano(attr.second.int_value() * 1e6); + } + } + continue; + } + auto ev = new trace::Span::Event(); + ev->set_time_unix_nano(absl::ToUnixNanos(annotation.timestamp())); + auto attrs = annotation.event().attributes(); + for (const auto& attr : attrs) { + auto kv = new common::KeyValue(); + kv->set_key(attr.first); + auto value = new common::AnyValue(); + switch (attr.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attr.second.bool_value()); + break; + } + ev->mutable_attributes()->AddAllocated(kv); + } + item->mutable_events()->AddAllocated(ev); + } + item->set_dropped_events_count(span.annotations().dropped_events_count()); + } + + for (const auto& link : span.links()) { + auto span_link = new trace::Span::Link(); + span_link->set_trace_id(link.trace_id().ToHex()); + span_link->set_span_id(link.span_id().ToHex()); + for (const auto& attribute : link.attributes()) { + auto kv = new common::KeyValue(); + kv->set_key(attribute.first); + auto value = new common::AnyValue(); + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; + } + kv->set_allocated_value(value); + span_link->mutable_attributes()->AddAllocated(kv); + } + + item->mutable_links()->AddAllocated(span_link); + } + item->set_dropped_links_count(span.num_attributes_dropped()); + + for (const auto& attribute : span.attributes()) { + auto kv = new common::KeyValue(); + kv->set_key(attribute.first); + auto value = new common::AnyValue(); + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; + } + item->mutable_attributes()->AddAllocated(kv); + } + + if (span.status().ok()) { + item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_OK); + } else { + item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_ERROR); + item->mutable_status()->set_message(span.status().error_message()); + } + + // item->mutable_status()->set_code() + + instrument_library_span->mutable_spans()->AddAllocated(item); + } + resource->mutable_instrumentation_library_spans()->AddAllocated(instrument_library_span); + request.mutable_resource_spans()->AddAllocated(resource); + + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = iterator->first; + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); + invocation_context->context.set_deadline(deadline); + + absl::flat_hash_map metadata; + Signature::sign(exp->clientConfig(), metadata); + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + auto callback = [](const InvocationContext* invocation_context) { + if (invocation_context->status.ok()) { + SPDLOG_DEBUG("Export tracing spans OK"); + } else { + SPDLOG_WARN("Failed to export tracing spans to {}", invocation_context->remote_address); + } + }; + invocation_context->callback = callback; + + exporter_client->asyncExport(request, invocation_context); +} + +thread_local std::uint32_t OtlpExporterHandler::round_robin_ = 0; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h new file mode 100644 index 000000000..e8a820449 --- /dev/null +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -0,0 +1,111 @@ +#pragma once + +#include "ClientConfig.h" +#include "ClientManager.h" +#include "InvocationContext.h" +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "opencensus/trace/exporter/span_data.h" +#include "opencensus/trace/exporter/span_exporter.h" +#include "opencensus/trace/sampler.h" +#include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h" +#include "rocketmq/RocketMQ.h" +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +namespace collector = opentelemetry::proto::collector; +namespace collector_trace = collector::trace::v1; + +enum class TraceMode : std::uint8_t { OFF = 0, DEBUG = 1, GRPC = 2 }; + +class Samplers { +public: + static opencensus::trace::Sampler& always(); +}; + +class OtlpExporter : public std::enable_shared_from_this { +public: + OtlpExporter(std::weak_ptr client_manager, ClientConfig* client_config) + : client_manager_(std::move(client_manager)), client_config_(client_config) {} + + void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { + absl::MutexLock lk(&hosts_mtx_); + hosts_ = std::move(hosts); + } + + void start(); + + void shutdown(); + + std::vector hosts() LOCKS_EXCLUDED(hosts_mtx_) { + absl::MutexLock lk(&hosts_mtx_); + return hosts_; + } + + std::weak_ptr& clientManager() { return client_manager_; } + + ClientConfig* clientConfig() { return client_config_; } + + void traceMode(TraceMode mode) { mode_ = mode; } + + TraceMode traceMode() const { return mode_; } + +private: + std::weak_ptr client_manager_; + ClientConfig* client_config_; + + std::vector hosts_; + absl::Mutex hosts_mtx_; + + TraceMode mode_{TraceMode::OFF}; +}; + +class ExportClient { +public: + ExportClient(std::shared_ptr completion_queue, std::shared_ptr channel) + : completion_queue_(std::move(completion_queue)), stub_(collector_trace::TraceService::NewStub(channel)) {} + + void asyncExport(const collector_trace::ExportTraceServiceRequest& request, + InvocationContext* invocation_context); + +private: + std::weak_ptr completion_queue_; + std::unique_ptr stub_; +}; + +class OtlpExporterHandler : public ::opencensus::trace::exporter::SpanExporter::Handler { +public: + OtlpExporterHandler(std::weak_ptr exporter); + + void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) override; + + void start(); + + void shutdown(); + +private: + std::weak_ptr exporter_; + std::shared_ptr completion_queue_; + std::thread poll_thread_; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; + + absl::flat_hash_map> clients_map_ GUARDED_BY(clients_map_mtx_); + absl::Mutex clients_map_mtx_; + + thread_local static std::uint32_t round_robin_; + + std::atomic_bool stopped_{false}; + + void poll(); + + void syncExportClients() LOCKS_EXCLUDED(clients_map_mtx_); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp index f17dfdafa..873296ed7 100644 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -3,6 +3,7 @@ #include "ProcessQueueMock.h" #include "PushConsumerMock.h" #include "grpc/grpc.h" +#include "rocketmq/CredentialsProvider.h" #include "rocketmq/MQMessageExt.h" #include "gtest/gtest.h" #include @@ -11,6 +12,9 @@ ROCKETMQ_NAMESPACE_BEGIN class ConsumeStandardMessageServiceTest : public testing::Test { public: + ConsumeStandardMessageServiceTest() + : credentials_provider_(std::make_shared("access_key", "access_secret")) {} + void SetUp() override { grpc_init(); consumer_ = std::make_shared>(); @@ -32,6 +36,9 @@ class ConsumeStandardMessageServiceTest : public testing::Test { ON_CALL(*process_queue_, take).WillByDefault(testing::Invoke(mock_take)); ON_CALL(*process_queue_, getConsumer).WillByDefault(testing::Return(consumer)); ON_CALL(*consumer_, customExecutor).WillByDefault(testing::ReturnRef(executor_)); + ON_CALL(*consumer_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); + ON_CALL(*consumer_, arn).WillByDefault(testing::ReturnRef(arn_)); + ON_CALL(*consumer_, getGroupName).WillByDefault(testing::ReturnRef(group_name_)); } void TearDown() override { grpc_shutdown(); } @@ -41,11 +48,14 @@ class ConsumeStandardMessageServiceTest : public testing::Test { std::string topic_{"TestTopic"}; std::string tag_{"TagA"}; std::string body_{"Body Content"}; + std::string arn_{"arn:mq://test"}; + std::string group_name_{"CID_Test"}; uint32_t consume_batch_size_; std::shared_ptr> consumer_; std::shared_ptr consume_standard_message_service_; testing::NiceMock message_listener_; std::shared_ptr> process_queue_; + std::shared_ptr credentials_provider_; Executor executor_; }; diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 63e332392..3adf054e5 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -2,7 +2,9 @@ #include "MQClientTest.h" #include "ProducerImpl.h" +#include "rocketmq/CredentialsProvider.h" #include "rocketmq/MQSelector.h" +#include #include #include @@ -10,7 +12,9 @@ ROCKETMQ_NAMESPACE_BEGIN class DefaultMQProducerUnitTest : public MQClientTest { public: - DefaultMQProducerUnitTest() : MQClientTest() {} + DefaultMQProducerUnitTest() : MQClientTest() { + credentials_provider_ = std::make_shared(access_key_, access_secret_); + } void SetUp() override { MQClientTest::SetUp(); @@ -59,6 +63,9 @@ class DefaultMQProducerUnitTest : public MQClientTest { absl::flat_hash_map metadata_; std::shared_ptr> rpc_client_broker_; std::string message_id_{"msg_id_0"}; + std::string access_key_{"access_key"}; + std::string access_secret_{"access_secret"}; + std::shared_ptr credentials_provider_; }; TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { @@ -103,7 +110,7 @@ class UnitTestSendCallback : public SendCallback { public: UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) {} - void onSuccess(const SendResult& send_result) override { + void onSuccess(SendResult& send_result) override { absl::MutexLock lk(&mtx_); msg_id_ = send_result.getMsgId(); completed_ = true; @@ -126,6 +133,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); + producer->setCredentialsProvider(credentials_provider_); producer->start(); MQMessage message; message.setTopic(topic_); @@ -148,6 +156,7 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); + producer->setCredentialsProvider(credentials_provider_); producer->start(); MQMessage message; SendResult send_result = producer->send(message); @@ -159,6 +168,7 @@ TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { auto producer = std::make_shared(group_name_); producer->arn(arn_); producer->setNameServerList(name_server_list_); + producer->setCredentialsProvider(credentials_provider_); producer->start(); const char* isolated_endpoint = "ipv4:10.0.0.0:10911"; diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index ef20e87cf..1f8d848d7 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -13,6 +13,9 @@ ROCKETMQ_NAMESPACE_BEGIN class ProducerImplTest : public testing::Test { public: + ProducerImplTest() + : credentials_provider_(std::make_shared(access_key_, access_secret_)) {} + void SetUp() override { grpc_init(); client_manager_ = std::make_shared>(); @@ -20,6 +23,7 @@ class ProducerImplTest : public testing::Test { producer_ = std::make_shared(group_); producer_->arn(arn_); producer_->setNameServerList(name_server_list_); + producer_->setCredentialsProvider(credentials_provider_); { std::vector partitions; @@ -53,6 +57,9 @@ class ProducerImplTest : public testing::Test { std::string broker_host_{"10.0.0.1"}; int broker_port_{10911}; TopicRouteDataPtr topic_route_data_; + std::string access_key_{"access_key"}; + std::string access_secret_{"access_secret"}; + std::shared_ptr credentials_provider_; }; TEST_F(ProducerImplTest, testStartShutdown) { @@ -167,7 +174,7 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { class TestSendCallback : public SendCallback { public: TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) {} - void onSuccess(const SendResult& send_result) override { + void onSuccess(SendResult& send_result) override { absl::MutexLock lk(&mtx_); completed_ = true; cv_.SignalAll(); diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp index cc40de843..a66005d03 100644 --- a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp +++ b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp @@ -1,10 +1,10 @@ -#include "gtest/gtest.h" #include "SendCallbacks.h" +#include "gtest/gtest.h" +#include ROCKETMQ_NAMESPACE_BEGIN -class SendCallbacksTest : public testing::Test { -}; +class SendCallbacksTest : public testing::Test {}; TEST_F(SendCallbacksTest, testAwait) { std::string msg_id("Msg-0"); @@ -24,5 +24,4 @@ TEST_F(SendCallbacksTest, testAwait) { } } - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/BUILD.bazel b/src/test/cpp/ut/tracing/exporters/BUILD.bazel new file mode 100644 index 000000000..8646cc806 --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/BUILD.bazel @@ -0,0 +1,24 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "otlp_exporter_test", + srcs = [ + "OtlpExportersTest.cpp", + ], + deps = [ + "//src/main/cpp/client/mocks:client_mocks", + "//src/main/cpp/tracing/exporters:otlp_exporter", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "span_context_test", + srcs = [ + "SpanContextTest.cpp", + ], + deps = [ + "@io_opencensus_cpp//opencensus/trace", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp new file mode 100644 index 000000000..c1909ac8e --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -0,0 +1,51 @@ +#include "ClientConfigMock.h" +#include "ClientManagerMock.h" +#include "OtlpExporter.h" +#include "opencensus/trace/sampler.h" +#include "opencensus/trace/span.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class OtlpExporterTest : public testing::Test { +public: + void SetUp() override { client_manager_ = std::make_shared>(); } + + void TearDown() override {} + +protected: + std::shared_ptr> client_manager_; + ClientConfigMock client_config_; +}; + +TEST_F(OtlpExporterTest, testExport) { + auto exporter = std::make_shared(client_manager_, &client_config_); + exporter->traceMode(TraceMode::DEBUG); + exporter->start(); + + auto& sampler = Samplers::always(); + + auto span_generator = [&] { + int total = 20; + while (total) { + auto span = opencensus::trace::Span::StartSpan("TestSpan", nullptr, {&sampler}); + span.AddAnnotation("annotation-1", {{"key-1", "value-1"}, {"key-2", 2}, {"key-3", true}}); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + span.End(); + if (0 == --total % 10) { + std::cout << "Total: " << total << std::endl; + } + } + }; + + std::thread t(span_generator); + if (t.joinable()) { + t.join(); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp new file mode 100644 index 000000000..ffc58a651 --- /dev/null +++ b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp @@ -0,0 +1,10 @@ +#include "opencensus/trace/propagation/trace_context.h" +#include "gtest/gtest.h" + +TEST(SpanContextTest, testSpanContext) { + std::string empty_context; + opencensus::trace::SpanContext span_context; + EXPECT_NO_THROW(span_context = opencensus::trace::propagation::FromTraceParentHeader(empty_context)); + EXPECT_FALSE(span_context.IsValid()); +} + From da3e1dbcb0ddabd0f0addd1b705d897943900264 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 23 Aug 2021 21:14:12 +0800 Subject: [PATCH 047/133] Remove unused third party libraries --- third_party/cpp_httplib.BUILD | 6 --- third_party/curl.BUILD | 37 -------------- third_party/libuv.BUILD | 92 ----------------------------------- 3 files changed, 135 deletions(-) delete mode 100644 third_party/cpp_httplib.BUILD delete mode 100644 third_party/curl.BUILD delete mode 100644 third_party/libuv.BUILD diff --git a/third_party/cpp_httplib.BUILD b/third_party/cpp_httplib.BUILD deleted file mode 100644 index 2fdd5db35..000000000 --- a/third_party/cpp_httplib.BUILD +++ /dev/null @@ -1,6 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") -cc_library( - name = "httplib", - hdrs = ["httplib.h"], - visibility = ["//visibility:public"], -) \ No newline at end of file diff --git a/third_party/curl.BUILD b/third_party/curl.BUILD deleted file mode 100644 index dbbe3e8e2..000000000 --- a/third_party/curl.BUILD +++ /dev/null @@ -1,37 +0,0 @@ -# Builds CURL from a distribution. - -load("@org_apache_rocketmq//bazel:curl.bzl", "CURL_COPTS") - -package(features = ["no_copts_tokenization"]) - -config_setting( - name = "windows", - values = {"cpu": "x64_windows"}, - visibility = ["//visibility:private"], -) - -config_setting( - name = "osx", - values = {"cpu": "darwin"}, - visibility = ["//visibility:private"], -) - -cc_library( - name = "curl", - srcs = glob([ - "lib/**/*.c", - ]), - hdrs = glob([ - "include/curl/*.h", - "lib/**/*.h", - ]), - copts = CURL_COPTS + [ - "-DOS=\"os\"", - "-DCURL_EXTERN_SYMBOL=__attribute__((__visibility__(\"default\")))", - ], - includes = [ - "include/", - "lib/", - ], - visibility = ["//visibility:public"], -) \ No newline at end of file diff --git a/third_party/libuv.BUILD b/third_party/libuv.BUILD deleted file mode 100644 index 61f3eb3b1..000000000 --- a/third_party/libuv.BUILD +++ /dev/null @@ -1,92 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") -UV_COMMON_SOURCES = [ - "include/uv/unix.h", - "include/uv/linux.h", - "include/uv/sunos.h", - "include/uv/darwin.h", - "include/uv/bsd.h", - "include/uv/aix.h", - "src/idna.h", - "src/idna.c", - "src/fs-poll.c", - "src/heap-inl.h", - "src/inet.c", - "src/queue.h", - "src/threadpool.c", - "src/timer.c", - "src/strscpy.h", - "src/strscpy.c", - "src/uv-common.c", - "src/uv-common.h", - "src/uv-data-getter-setters.c", - "src/version.c" -] - -UV_UNIX_SOURCES = [ - "src/unix/async.c", - "src/unix/atomic-ops.h", - "src/unix/core.c", - "src/unix/dl.c", - "src/unix/fs.c", - "src/unix/getaddrinfo.c", - "src/unix/getnameinfo.c", - "src/unix/internal.h", - "src/unix/loop.c", - "src/unix/loop-watcher.c", - "src/unix/pipe.c", - "src/unix/poll.c", - "src/unix/process.c", - "src/unix/signal.c", - "src/unix/spinlock.h", - "src/unix/stream.c", - "src/unix/tcp.c", - "src/unix/thread.c", - "src/unix/tty.c", - "src/unix/udp.c", -] - -UV_DARWIN_SOURCES = [ - "src/unix/bsd-ifaddrs.c", - "src/unix/darwin.c", - "src/unix/darwin-proctitle.c", - "src/unix/fsevents.c", - "src/unix/kqueue.c", - "src/unix/proctitle.c", -] - -UV_LINUX_SOURCES = [ - "src/unix/linux-core.c", - "src/unix/linux-inotify.c", - "src/unix/linux-syscalls.c", - "src/unix/linux-syscalls.h", - "src/unix/procfs-exepath.c", - "src/unix/proctitle.c", - "src/unix/sysinfo-loadavg.c", -] - -cc_library( - name = "libuv", - srcs = UV_COMMON_SOURCES + select({ - "@bazel_tools//src/conditions:darwin": UV_UNIX_SOURCES + UV_DARWIN_SOURCES, - # FIXME(ofrobots): Figure out the right way to detect linux. - "//conditions:default": UV_UNIX_SOURCES + UV_LINUX_SOURCES, - }), - hdrs = [ - "src/unix/internal.h", - "include/uv.h", - "include/uv/darwin.h", - "include/uv/errno.h", - "include/uv/threadpool.h", - "include/uv/tree.h", - "include/uv/unix.h", - "include/uv/version.h", - ], - includes = ["include", "src"], - defines = [ - "_GNU_SOURCE", - ], - linkopts = [ - "-ldl", - ], - visibility = ["//visibility:public"], -) From d10f5ba58ecdd3eb0ab243d42d5c9de57551f00e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 23 Aug 2021 21:18:40 +0800 Subject: [PATCH 048/133] Remove unused third party libraries --- bazel/curl.bzl | 201 ----------------------------------------- third_party/zlib.BUILD | 36 -------- 2 files changed, 237 deletions(-) delete mode 100644 bazel/curl.bzl delete mode 100644 third_party/zlib.BUILD diff --git a/bazel/curl.bzl b/bazel/curl.bzl deleted file mode 100644 index 21d99e458..000000000 --- a/bazel/curl.bzl +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright 2020, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Compiler options for building libcurl. - -BASE_CURL_COPTS = [ - # Disable everything else except HTTP protocol. - "-DHTTP_ONLY=1", - "-DENABLE_IPV6=1", - "-DGETHOSTNAME_TYPE_ARG2=size_t", - "-DGETSERVBYPORT_R_ARGS=6", - "-DGETSERVBYPORT_R_BUFSIZE=4096", - "-DHAVE_ALARM=1", - "-DHAVE_ALLOCA_H=1", - "-DHAVE_ARPA_INET_H=1", - "-DHAVE_ARPA_TFTP_H=1", - "-DHAVE_ASSERT_H=1", - "-DHAVE_BASENAME=1", - "-DHAVE_BOOL_T=1", - "-DHAVE_CLOCK_GETTIME_MONOTONIC=1", - "-DHAVE_CONNECT=1", - "-DHAVE_DLFCN_H=1", - "-DHAVE_ENGINE_LOAD_BUILTIN_ENGINES=1", - "-DHAVE_ERRNO_H=1", - "-DHAVE_FCNTL=1", - "-DHAVE_FCNTL_H=1", - "-DHAVE_FCNTL_O_NONBLOCK=1", - "-DHAVE_FDOPEN=1", - "-DHAVE_FREEADDRINFO=1", - "-DHAVE_FREEIFADDRS=1", - "-DHAVE_FSETXATTR=1", - "-DHAVE_FSETXATTR_5=1", - "-DHAVE_FTRUNCATE=1", - "-DHAVE_GAI_STRERROR=1", - "-DHAVE_GETADDRINFO=1", - "-DHAVE_GETADDRINFO_THREADSAFE=1", - "-DHAVE_GETEUID=1", - "-DHAVE_GETHOSTBYADDR=1", - "-DHAVE_GETHOSTBYADDR_R=1", - "-DHAVE_GETHOSTBYADDR_R_8=1", - "-DHAVE_GETHOSTBYNAME=1", - "-DHAVE_GETHOSTBYNAME_R=1", - "-DHAVE_GETHOSTBYNAME_R_6=1", - "-DHAVE_GETHOSTNAME=1", - "-DHAVE_GETIFADDRS=1", - "-DHAVE_GETNAMEINFO=1", - "-DHAVE_GETPPID=1", - "-DHAVE_GETPWUID=1", - "-DHAVE_GETPWUID_R=1", - "-DHAVE_GETRLIMIT=1", - "-DHAVE_GETSERVBYPORT_R=1", - "-DHAVE_GETTIMEOFDAY=1", - "-DHAVE_GMTIME_R=1", - "-DHAVE_IFADDRS_H=1", - "-DHAVE_IF_NAMETOINDEX=1", - "-DHAVE_INET_NTOP=1", - "-DHAVE_INET_PTON=1", - "-DHAVE_INTTYPES_H=1", - "-DHAVE_IOCTL=1", - "-DHAVE_IOCTL_FIONBIO=1", - "-DHAVE_IOCTL_SIOCGIFADDR=1", - "-DHAVE_LIBGEN_H=1", - "-DHAVE_LL=1", - "-DHAVE_LOCALE_H=1", - "-DHAVE_LOCALTIME_R=1", - "-DHAVE_LONGLONG=1", - "-DHAVE_MALLOC_H=1", - "-DHAVE_MEMORY_H=1", - "-DHAVE_NETDB_H=1", - "-DHAVE_NETINET_IN_H=1", - "-DHAVE_NETINET_TCP_H=1", - "-DHAVE_NET_IF_H=1", - "-DHAVE_PIPE=1", - "-DHAVE_POLL=1", - "-DHAVE_POLL_FINE=1", - "-DHAVE_POLL_H=1", - "-DHAVE_POSIX_STRERROR_R=1", - "-DHAVE_PTHREAD_H=1", - "-DHAVE_PWD_H=1", - "-DHAVE_RECV=1", - "-DHAVE_SELECT=1", - "-DHAVE_SEND=1", - "-DHAVE_SETJMP_H=1", - "-DHAVE_SETLOCALE=1", - "-DHAVE_SETRLIMIT=1", - "-DHAVE_SETSOCKOPT=1", - "-DHAVE_SGTTY_H=1", - "-DHAVE_SIGACTION=1", - "-DHAVE_SIGINTERRUPT=1", - "-DHAVE_SIGNAL=1", - "-DHAVE_SIGNAL_H=1", - "-DHAVE_SIGSETJMP=1", - "-DHAVE_SIG_ATOMIC_T=1", - "-DHAVE_SOCKADDR_IN6_SIN6_SCOPE_ID=1", - "-DHAVE_SOCKET=1", - "-DHAVE_SOCKETPAIR=1", - "-DHAVE_STDBOOL_H=1", - "-DHAVE_STDINT_H=1", - "-DHAVE_STDIO_H=1", - "-DHAVE_STDLIB_H=1", - "-DHAVE_STRCASECMP=1", - "-DHAVE_STRDUP=1", - "-DHAVE_STRERROR_R=1", - "-DHAVE_STRINGS_H=1", - "-DHAVE_STRING_H=1", - "-DHAVE_STRNCASECMP=1", - "-DHAVE_STRSTR=1", - "-DHAVE_STRTOK_R=1", - "-DHAVE_STRTOLL=1", - "-DHAVE_STRUCT_SOCKADDR_STORAGE=1", - "-DHAVE_STRUCT_TIMEVAL=1", - "-DHAVE_SYS_IOCTL_H=1", - "-DHAVE_SYS_PARAM_H=1", - "-DHAVE_SYS_POLL_H=1", - "-DHAVE_SYS_RESOURCE_H=1", - "-DHAVE_SYS_SELECT_H=1", - "-DHAVE_SYS_SOCKET_H=1", - "-DHAVE_SYS_STAT_H=1", - "-DHAVE_SYS_TIME_H=1", - "-DHAVE_SYS_TYPES_H=1", - "-DHAVE_SYS_UIO_H=1", - "-DHAVE_SYS_UN_H=1", - "-DHAVE_SYS_WAIT_H=1", - "-DHAVE_SYS_XATTR_H=1", - "-DHAVE_TERMIOS_H=1", - "-DHAVE_TERMIO_H=1", - "-DHAVE_TIME_H=1", - "-DHAVE_UNISTD_H=1", - "-DHAVE_UTIME=1", - "-DHAVE_UTIMES=1", - "-DHAVE_UTIME_H=1", - "-DHAVE_VARIADIC_MACROS_C99=1", - "-DHAVE_VARIADIC_MACROS_GCC=1", - "-DHAVE_WRITABLE_ARGV=1", - "-DHAVE_WRITEV=1", - "-DRECV_TYPE_ARG1=int", - "-DRECV_TYPE_ARG2=void*", - "-DRECV_TYPE_ARG3=size_t", - "-DRECV_TYPE_ARG4=int", - "-DRECV_TYPE_RETV=ssize_t", - "-DRETSIGTYPE=void", - "-DSELECT_QUAL_ARG5=", - "-DSELECT_TYPE_ARG1=int", - "-DSELECT_TYPE_ARG234=fd_set*", - "-DSELECT_TYPE_RETV=int", - "-DSEND_QUAL_ARG2=const", - "-DSEND_TYPE_ARG1=int", - "-DSEND_TYPE_ARG2=void*", - "-DSEND_TYPE_ARG3=size_t", - "-DSEND_TYPE_ARG4=int", - "-DSEND_TYPE_RETV=ssize_t", - "-DSIZEOF_CURL_OFF_T=8", - "-DSIZEOF_INT=4", - "-DSIZEOF_LONG=8", - "-DSIZEOF_OFF_T=8", - "-DSIZEOF_SHORT=2", - "-DSIZEOF_SIZE_T=8", - "-DSIZEOF_TIME_T=8", - "-DSTDC_HEADERS=1", - "-DSTRERROR_R_TYPE_ARG3=size_t", - "-DTIME_WITH_SYS_TIME=1", - "-DUSE_THREADS_POSIX=1", - "-DUSE_UNIX_SOCKETS=1", - - # Extra defines needed by curl - "-DBUILDING_LIBCURL", - "-DCURL_HIDDEN_SYMBOLS", -] - -LINUX_CURL_COPTS = [ - "-DHAVE_LINUX_TCP_H=1", - "-DHAVE_MSG_NOSIGNAL=1", -] - -CURL_COPTS = select({ - "//:windows": [ - # Disable everything else except HTTP protocol. - "/DHTTP_ONLY=1", - "/DCURL_STATICLIB", - "/DWIN32", - "/DBUILDING_LIBCURL", - "/DUSE_WIN32_IDN", - "/DWANT_IDN_PROTOTYPES", - "/DUSE_IPV6", - "/DUSE_WINDOWS_SSPI", - "/DUSE_SCHANNEL", - ], - "//:osx": BASE_CURL_COPTS, - "//conditions:default": BASE_CURL_COPTS + LINUX_CURL_COPTS, -}) \ No newline at end of file diff --git a/third_party/zlib.BUILD b/third_party/zlib.BUILD deleted file mode 100644 index 95ae7c5a8..000000000 --- a/third_party/zlib.BUILD +++ /dev/null @@ -1,36 +0,0 @@ -cc_library( - name = "zlib", - srcs = [ - "adler32.c", - "compress.c", - "crc32.c", - "deflate.c", - "infback.c", - "inffast.c", - "inflate.c", - "inftrees.c", - "trees.c", - "uncompr.c", - "zutil.c", - ], - hdrs = [ - "crc32.h", - "deflate.h", - "gzguts.h", - "inffast.h", - "inffixed.h", - "inflate.h", - "inftrees.h", - "trees.h", - "zconf.h", - "zlib.h", - "zutil.h", - ], - includes = [ - ".", - ], - linkstatic = 1, - visibility = [ - "//visibility:public", - ], -) From a06a84ddc67641eee9a56116bf78740ab51295e5 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 24 Aug 2021 14:52:55 +0800 Subject: [PATCH 049/133] Minor fix while debug --- src/main/cpp/base/UniqueIdGenerator.cpp | 2 +- src/main/cpp/rocketmq/CredentialsProvider.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp index 9ce6c8a1f..0b23dd21b 100644 --- a/src/main/cpp/base/UniqueIdGenerator.cpp +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -8,7 +8,7 @@ ROCKETMQ_NAMESPACE_BEGIN -const uint8_t UniqueIdGenerator::VERSION = 0; +const uint8_t UniqueIdGenerator::VERSION = 1; UniqueIdGenerator::UniqueIdGenerator() : prefix_(), since_custom_epoch_(std::chrono::system_clock::now() - customEpoch()), diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index da7cec74a..942ecb361 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -1,18 +1,18 @@ +#include "GHttpClient.h" #include "MixAll.h" #include "StsCredentialsProviderImpl.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "fmt/format.h" #include "ghc/filesystem.hpp" #include "google/protobuf/struct.pb.h" #include "google/protobuf/util/json_util.h" #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" -#include "GHttpClient.h" #include #include #include #include -#include "fmt/format.h" ROCKETMQ_NAMESPACE_BEGIN @@ -78,6 +78,7 @@ ConfigFileCredentialsProvider::ConfigFileCredentialsProvider() { if (fields.contains(ACCESS_SECRET_FIELD_NAME)) { access_secret_ = fields.at(ACCESS_SECRET_FIELD_NAME).string_value(); } + SPDLOG_DEBUG("Credentials for access_key={} loaded", access_key_); } else { SPDLOG_WARN("Failed to parse credential JSON config file. Message: {}", status.message().data()); } @@ -103,9 +104,7 @@ StsCredentialsProviderImpl::StsCredentialsProviderImpl(std::string ram_role_name http_client_->start(); } -StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { - http_client_->shutdown(); -} +StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { http_client_->shutdown(); } Credentials StsCredentialsProviderImpl::getCredentials() { if (std::chrono::system_clock::now() >= expiration_) { @@ -182,5 +181,4 @@ const char* StsCredentialsProviderImpl::FIELD_SESSION_TOKEN = "SecurityToken"; const char* StsCredentialsProviderImpl::FIELD_EXPIRATION = "Expiration"; const char* StsCredentialsProviderImpl::EXPIRATION_DATE_TIME_FORMAT = "%Y-%m-%d%ET%H:%H:%S%Ez"; - ROCKETMQ_NAMESPACE_END \ No newline at end of file From 6f2832a79a53144f87e3cfad9aaa6d2ebdfa722c Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 24 Aug 2021 18:51:46 +0800 Subject: [PATCH 050/133] Fetch topic route with name server list attached (#15) --- example/rocketmq/ExampleProducer.cpp | 12 +++++--- src/main/cpp/base/MQMessage.cpp | 2 +- src/main/cpp/base/MixAll.cpp | 5 +++ src/main/cpp/base/include/MixAll.h | 4 +++ src/main/cpp/log/LoggerImpl.cpp | 3 +- src/main/cpp/rocketmq/ClientImpl.cpp | 46 +++++++++++++++++++++++++--- src/test/cpp/ut/base/MixAllTest.cpp | 9 +++++- 7 files changed, 69 insertions(+), 12 deletions(-) diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 9d9d54062..c20ae4661 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -33,15 +33,19 @@ int main(int argc, char* argv[]) { logger.init(); DefaultMQProducer producer("TestGroup"); - producer.setNamesrvAddr("11.165.223.199:9876"); + + const char* topic = "cpp_sdk_standard"; + const char* name_server = "47.98.116.189:80"; + + producer.setNamesrvAddr(name_server); producer.compressBodyThreshold(256); - const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; - producer.setRegion("cn-hangzhou"); + const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; + producer.setRegion("cn-hangzhou-pre"); producer.arn(arn); producer.setCredentialsProvider(std::make_shared()); MQMessage message; - message.setTopic("yc001"); + message.setTopic(topic); message.setTags("TagA"); message.setKey("Yuck! Why-plural?"); diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 68b26bc0a..fdd51a6c9 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -51,7 +51,7 @@ const std::string& MQMessage::getMsgId() const { return impl_->system_attribute_ std::string MQMessage::getBornHost() const { return impl_->system_attribute_.born_host; } std::chrono::system_clock::time_point MQMessage::deliveryTimestamp() const { - absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); + return absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); } void MQMessage::setProperty(const std::string& name, const std::string& value) { diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index e9564d80a..a5595064d 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -30,6 +30,7 @@ const uint32_t MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE = 1; const int32_t MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS = 16; const RE2 MixAll::TOPIC_REGEX("[a-zA-Z0-9\\-_]{3,64}"); +const RE2 MixAll::IP_REGEX("\\d+\\.\\d+\\.\\d+\\.\\d+"); const std::chrono::duration MixAll::DEFAULT_INVISIBLE_TIME_ = std::chrono::seconds(30); @@ -221,4 +222,8 @@ bool MixAll::homeDirectory(std::string& home_dir) { #endif } +bool MixAll::isIPv4(absl::string_view host) { + return RE2::FullMatch(re2::StringPiece(host.data(), host.length()), IP_REGEX); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 5b3df864c..3b1762af2 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -1,5 +1,6 @@ #pragma once +#include "absl/strings/string_view.h" #include "re2/re2.h" #include "rocketmq/MQMessage.h" #include @@ -22,6 +23,7 @@ class MixAll { static const int32_t DEFAULT_MAX_DELIVERY_ATTEMPTS; static const RE2 TOPIC_REGEX; + static const RE2 IP_REGEX; /** * The amount of time required before a popped message is eligible to be consumed again. By default, 30s. @@ -100,6 +102,8 @@ class MixAll { static bool homeDirectory(std::string& home); + static bool isIPv4(absl::string_view host); + private: static bool hexCharValue(char c, uint8_t& value); }; diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp index 0afa7e97f..20643f811 100644 --- a/src/main/cpp/log/LoggerImpl.cpp +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -68,7 +68,8 @@ void LoggerImpl::init0() { setLevel(console_sink_, console_level_); console_sink_->set_pattern(pattern_); - file_sink_ = std::make_shared(log_home_, file_size_, file_count_, true); + file_sink_ = std::make_shared(log_home_, file_size_, file_count_, + /*rotate_on_open=*/false); file_sink_->set_pattern(pattern_); setLevel(file_sink_, level_); diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index f382e0362..041d608b4 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -6,11 +6,16 @@ #include "MessageAccessor.h" #include "Signature.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" #include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/MQMessageExt.h" +#include #include +#include +#include #include #include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -53,7 +58,7 @@ void ClientImpl::start() { }; name_server_update_handle_ = client_manager_->getScheduler().schedule(name_server_update_functor, UPDATE_NAME_SERVER_LIST_TASK_NAME, - std::chrono::milliseconds(0), std::chrono::seconds(30)); + std::chrono::milliseconds(0), std::chrono::seconds(30)); } auto route_update_functor = [ptr]() { @@ -64,7 +69,7 @@ void ClientImpl::start() { }; route_update_handle_ = client_manager_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, - std::chrono::seconds(10), std::chrono::seconds(30)); + std::chrono::seconds(10), std::chrono::seconds(30)); state_.store(State::STARTED); } @@ -236,6 +241,37 @@ void ClientImpl::fetchRouteFor(const std::string& topic, const std::functionset_arn(arn_); request.mutable_topic()->set_name(topic); + auto endpoints = request.mutable_endpoints()->mutable_addresses(); + std::vector> pairs; + { + absl::MutexLock lk(&name_server_list_mtx_); + for (const auto& name_server_item : name_server_list_) { + std::string::size_type pos = name_server_item.rfind(":"); + if (std::string::npos == pos) { + continue; + } + std::string host(name_server_item.substr(0, pos)); + std::string port(name_server_item.substr(pos + 1)); + pairs.emplace_back(std::make_pair(host, std::stoi(port))); + } + } + + if (!pairs.empty()) { + for (const auto& host_port : pairs) { + auto address = new rmq::Address(); + address->set_port(host_port.second); + address->set_host(host_port.first); + endpoints->AddAllocated(address); + } + + if (MixAll::isIPv4(pairs.begin()->first)) { + request.mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + } else if (absl::StrContains(pairs.begin()->first, ':')) { + request.mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv6); + } else { + request.mutable_endpoints()->set_scheme(rmq::AddressScheme::DOMAIN_NAME); + } + } absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -346,7 +382,7 @@ void ClientImpl::multiplexing(const std::string& target, const MultiplexingReque absl::flat_hash_map metadata; Signature::sign(this, metadata); client_manager_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), - std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); + std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); } void ClientImpl::onMultiplexingResponse(const InvocationContext* ctx) { @@ -359,7 +395,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextgetScheduler().schedule(multiplexingLater, task_name, std::chrono::seconds(3), - std::chrono::seconds(0)); + std::chrono::seconds(0)); return; } @@ -373,7 +409,7 @@ void ClientImpl::onMultiplexingResponse(const InvocationContext metadata; Signature::sign(this, metadata); client_manager_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); + std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); break; } diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index 95f5d8416..ee8b281dc 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -8,7 +8,7 @@ class MixAllTest : public testing::Test { public: static std::string toUpperCase(const std::string& s) { std::string result; - for (const char & c : s) { + for (const char& c : s) { if ('a' <= c && 'z' >= c) { result.push_back(static_cast('A' + (c - 'a'))); } else { @@ -94,4 +94,11 @@ TEST_F(MixAllTest, testCrc32) { std::cout << digest << std::endl; } +TEST_F(MixAllTest, testIsIpv4) { + const char* ip = "8.8.8.8"; + const char* host = "www.taobao.com"; + EXPECT_TRUE(MixAll::isIPv4(ip)); + EXPECT_FALSE(MixAll::isIPv4(host)); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file From cc64cc35a858da059e3e77ad546ef2ce7d970ccf Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 25 Aug 2021 10:35:17 +0800 Subject: [PATCH 051/133] Adjust logging --- src/main/cpp/client/LogInterceptor.cpp | 13 ++++++------- src/main/cpp/rocketmq/ProducerImpl.cpp | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 7238929b9..3497dddb6 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -14,16 +14,15 @@ void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* meth if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { std::multimap* metadata = methods->GetSendInitialMetadata(); if (metadata) { - SPDLOG_DEBUG("Request Headers of {}:\n{}", client_rpc_info_->method(), + SPDLOG_DEBUG("[Outbound]Headers of {}: \n{}", client_rpc_info_->method(), absl::StrJoin(*metadata, "\n", absl::PairFormatter(" --> "))); } } if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { - const void* message = methods->GetSendMessage(); - if (message) { - const auto* request = reinterpret_cast(message); - SPDLOG_DEBUG("[Outbound] {}\n{}", client_rpc_info_->method(), request->DebugString()); + grpc::ByteBuffer* buffer = methods->GetSerializedSendMessage(); + if (buffer) { + SPDLOG_DEBUG("[Outbound] {}: Buffer: {}bytes", client_rpc_info_->method(), buffer->Length()); } } @@ -31,11 +30,11 @@ void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* meth std::multimap* metadata = methods->GetRecvInitialMetadata(); if (metadata) { absl::flat_hash_map response_headers; - for (const auto & it : *metadata) { + for (const auto& it : *metadata) { response_headers.insert({absl::string_view(it.first.data(), it.first.length()), absl::string_view(it.second.data(), it.second.length())}); } - SPDLOG_DEBUG("Response Headers of {}:\n{}", client_rpc_info_->method(), + SPDLOG_DEBUG("[Inbound]Response Headers of {}:\n{}", client_rpc_info_->method(), absl::StrJoin(response_headers, "\n", absl::PairFormatter(" --> "))); } } diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index f6b520a8e..81d4c66f0 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -108,8 +108,7 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM std::string message_id = UniqueIdGenerator::instance().next(); system_attribute->set_message_id(message_id); system_attribute->set_partition_id(message_queue.getQueueId()); - - SPDLOG_DEBUG("SendMessageRequest: {}", request.DebugString()); + SPDLOG_TRACE("SendMessageRequest: {}", request.DebugString()); return message_id; } From a94d19372019d3769580c6f529ea6d238fef8ab2 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 27 Aug 2021 16:44:19 +0800 Subject: [PATCH 052/133] Test async producer example --- example/rocketmq/ExampleAsyncProducer.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index b59534eee..5b871eb2e 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -167,16 +167,18 @@ int main(int argc, char* argv[]) { logger.setLevel(Level::Debug); logger.init(); + const char* topic = "cpp_sdk_standard"; + const char* tag = "TagA"; DefaultMQProducer producer("TestGroup"); - producer.setNamesrvAddr("11.165.223.199:9876"); + producer.setNamesrvAddr("47.98.116.189:80"); producer.compressBodyThreshold(256); - const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; producer.setCredentialsProvider(std::make_shared()); producer.arn(arn); - producer.setRegion("cn-hangzhou"); + producer.setRegion("cn-hangzhou-pre"); MQMessage message; - message.setTopic("yc001"); - message.setTags("TagA"); + message.setTopic(topic); + message.setTags(tag); message.setKey("Yuck! Why-plural?"); message.setBody(randomString(body_size)); From 0d46de78e16b8d854b6bfaf0fb44e517398ee185 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 30 Aug 2021 15:57:17 +0800 Subject: [PATCH 053/133] Sync consumer (#16) * Set access point when querying assignment * Use third-party thread pool implementation --- bazel/rocketmq_deps.bzl | 10 +++ example/rocketmq/ExamplePushConsumer.cpp | 17 ++--- src/main/cpp/client/BUILD.bazel | 1 + src/main/cpp/client/ClientManagerImpl.cpp | 16 ++--- .../cpp/client/include/ClientManagerImpl.h | 25 +++---- src/main/cpp/rocketmq/ClientImpl.cpp | 65 ++++++++++--------- .../rocketmq/ConsumeFifoMessageService.cpp | 2 +- .../cpp/rocketmq/ConsumeMessageService.cpp | 4 +- .../ConsumeStandardMessageService.cpp | 2 +- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 5 ++ src/main/cpp/rocketmq/include/ClientImpl.h | 7 +- .../rocketmq/include/ConsumeMessageService.h | 4 +- src/test/cpp/ut/base/BUILD.bazel | 12 ++++ src/test/cpp/ut/base/ThreadPoolTest.cpp | 29 +++++++++ .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 6 +- third_party/ThreadPool.BUILD | 11 ++++ 16 files changed, 141 insertions(+), 75 deletions(-) create mode 100644 src/test/cpp/ut/base/ThreadPoolTest.cpp create mode 100644 third_party/ThreadPool.BUILD diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index 6b59155d7..ff39cdafb 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -107,4 +107,14 @@ def rocketmq_deps(): urls = [ "https://github.com/open-telemetry/opentelemetry-proto/archive/v0.6.0.tar.gz", ], + ) + + maybe( + http_archive, + name = "com_github_progschj_ThreadPool", + build_file = "@org_apache_rocketmq//third_party:ThreadPool.BUILD", + strip_prefix = "ThreadPool-1.0", + urls = [ + "https://github.com/lizhanhui/ThreadPool/archive/refs/tags/v1.0.tar.gz", + ], ) \ No newline at end of file diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index d7832f968..584353ac2 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -1,8 +1,8 @@ #include "rocketmq/DefaultMQPushConsumer.h" -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#include "rocketmq/Logger.h" #include "spdlog/spdlog.h" - #include +#include #include #include @@ -12,8 +12,8 @@ class SampleMQMessageListener : public StandardMessageListener { public: ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { - SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + SPDLOG_WARN("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); + std::this_thread::sleep_for(std::chrono::seconds(1)); } return ConsumeMessageResult::SUCCESS; } @@ -25,19 +25,20 @@ int main(int argc, char* argv[]) { logger.setLevel(Level::Debug); logger.init(); - const char* cid = "GID_group003"; - const char* topic = "yc001"; - const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* cid = "GID_cpp_sdk_standard"; + const char* topic = "cpp_sdk_standard"; + const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; DefaultMQPushConsumer push_consumer(cid); push_consumer.setArn(arn); push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr("11.165.223.199:9876"); + push_consumer.setNamesrvAddr("47.98.116.189:80"); MessageListener* listener = new SampleMQMessageListener; push_consumer.setGroupName(cid); push_consumer.setInstanceName("instance_0"); push_consumer.subscribe(topic, "*"); push_consumer.registerMessageListener(listener); + push_consumer.setConsumeThreadCount(2); push_consumer.start(); std::this_thread::sleep_for(std::chrono::seconds(300)); diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index a19626e8b..83f24d6c6 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -17,6 +17,7 @@ cc_library( "@com_google_absl//absl/container:flat_hash_set", "@com_github_grpc_grpc//:grpc_secure", "@com_github_grpc_grpc//:grpc++", + "@com_github_progschj_ThreadPool//:ThreadPool", "@boringssl//:ssl", "//external:gtest", ], diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index bd1a1e7c3..f047d0890 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -24,7 +24,8 @@ ROCKETMQ_NAMESPACE_BEGIN ClientManagerImpl::ClientManagerImpl(std::string arn) : arn_(std::move(arn)), state_(State::CREATED), completion_queue_(std::make_shared()), - callback_thread_pool_(grpc::CreateDefaultThreadPool()), latency_histogram_("Message-Latency", 11) { + callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), + latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); assignLabels(latency_histogram_); server_authorization_check_config_ = std::make_shared( @@ -296,7 +297,7 @@ void ClientManagerImpl::pollCompletionQueue() { SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); } auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; - callback_thread_pool_->Add(callback); + callback_thread_pool_->enqueue(callback); } SPDLOG_INFO("CompletionQueue is fully drained and shut down"); } @@ -397,12 +398,11 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met return true; } - /** * @brief Create a gRPC channel to target host. - * - * @param target_host - * @return std::shared_ptr + * + * @param target_host + * @return std::shared_ptr */ std::shared_ptr ClientManagerImpl::createChannel(const std::string& target_host) { std::vector> interceptor_factories; @@ -412,7 +412,6 @@ std::shared_ptr ClientManagerImpl::createChannel(const std::strin return channel; } - RpcClientSharedPtr ClientManagerImpl::getRpcClient(const std::string& target_host, bool need_heartbeat) { std::shared_ptr client; { @@ -663,11 +662,10 @@ void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_conte if (ReceiveMessageStatus::OK == status) { for (auto& item : response.messages()) { MQMessageExt message_ext; + MessageAccessor::setTargetEndpoint(message_ext, target_host); if (wrapMessage(item, message_ext)) { msg_found_list.emplace_back(message_ext); - MessageAccessor::setTargetEndpoint(message_ext, target_host); } else { - // TODO: NACK } } diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 4a80d18fa..2381cf698 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -1,12 +1,5 @@ #pragma once -#include -#include -#include -#include -#include -#include - #include "Client.h" #include "ClientManager.h" #include "HeartbeatDataCallback.h" @@ -17,6 +10,7 @@ #include "RpcClientImpl.h" #include "Scheduler.h" #include "SendMessageContext.h" +#include "ThreadPool.h" #include "TopAddressing.h" #include "TopicRouteChangeCallback.h" #include "TopicRouteData.h" @@ -25,18 +19,15 @@ #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" -#include "src/cpp/server/thread_pool_interface.h" - -#ifdef ENABLE_TRACING -#include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" -#include "opentelemetry/sdk/trace/batch_span_processor.h" -#include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/trace/provider.h" -#endif - #include "rocketmq/AsyncCallback.h" #include "rocketmq/CommunicationMode.h" #include "rocketmq/State.h" +#include +#include +#include +#include +#include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -234,7 +225,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::uintptr_t stats_handle_{0}; std::shared_ptr completion_queue_; - std::unique_ptr callback_thread_pool_; + std::unique_ptr callback_thread_pool_; std::thread completion_queue_thread_; diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 041d608b4..1645c2ce4 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -216,37 +216,12 @@ bool ClientImpl::selectNameServer(std::string& selected, bool change) { return true; } -void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { - std::string name_server; - if (!selectNameServer(name_server)) { - SPDLOG_WARN("No name server available"); - return; - } - - auto callback = [this, topic, name_server, cb](bool ok, const TopicRouteDataPtr& route) { - if (!ok || !route) { - SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); - std::string name_server_changed; - if (selectNameServer(name_server_changed, true)) { - SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); - } - cb(nullptr); - return; - } - - SPDLOG_DEBUG("Apply callback of fetchRouteFor({}) since a valid route is fetched", topic); - cb(route); - }; - - QueryRouteRequest request; - request.mutable_topic()->set_arn(arn_); - request.mutable_topic()->set_name(topic); - auto endpoints = request.mutable_endpoints()->mutable_addresses(); +void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { std::vector> pairs; { absl::MutexLock lk(&name_server_list_mtx_); for (const auto& name_server_item : name_server_list_) { - std::string::size_type pos = name_server_item.rfind(":"); + std::string::size_type pos = name_server_item.rfind(':'); if (std::string::npos == pos) { continue; } @@ -261,18 +236,46 @@ void ClientImpl::fetchRouteFor(const std::string& topic, const std::functionset_port(host_port.second); address->set_host(host_port.first); - endpoints->AddAllocated(address); + endpoints->mutable_addresses()->AddAllocated(address); } if (MixAll::isIPv4(pairs.begin()->first)) { - request.mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); + endpoints->set_scheme(rmq::AddressScheme::IPv4); } else if (absl::StrContains(pairs.begin()->first, ':')) { - request.mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv6); + endpoints->set_scheme(rmq::AddressScheme::IPv6); } else { - request.mutable_endpoints()->set_scheme(rmq::AddressScheme::DOMAIN_NAME); + endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME); } } +} +void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { + std::string name_server; + if (!selectNameServer(name_server)) { + SPDLOG_WARN("No name server available"); + return; + } + + auto callback = [this, topic, name_server, cb](bool ok, const TopicRouteDataPtr& route) { + if (!ok || !route) { + SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); + std::string name_server_changed; + if (selectNameServer(name_server_changed, true)) { + SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); + } + cb(nullptr); + return; + } + + SPDLOG_DEBUG("Apply callback of fetchRouteFor({}) since a valid route is fetched", topic); + cb(route); + }; + + QueryRouteRequest request; + request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_name(topic); + auto endpoints = request.mutable_endpoints(); + setAccessPoint(endpoints); absl::flat_hash_map metadata; Signature::sign(this, metadata); client_manager_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 43f6ec6d1..3d56c6f9e 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -51,7 +51,7 @@ void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr consume_task = std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); SPDLOG_DEBUG("Submit FIFO consume task to thread pool"); - pool_->Add(consume_task); + pool_->enqueue(consume_task); } void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { diff --git a/src/main/cpp/rocketmq/ConsumeMessageService.cpp b/src/main/cpp/rocketmq/ConsumeMessageService.cpp index 2bb7dc5b2..055305c18 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageService.cpp @@ -1,13 +1,13 @@ #include "ConsumeMessageService.h" -#include "PushConsumer.h" #include "LoggerImpl.h" +#include "PushConsumer.h" ROCKETMQ_NAMESPACE_BEGIN ConsumeMessageService::ConsumeMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener) : state_(State::CREATED), thread_count_(thread_count), - pool_(absl::make_unique(thread_count_)), consumer_(std::move(consumer)), + pool_(absl::make_unique(thread_count_)), consumer_(std::move(consumer)), message_listener_(message_listener) {} void ConsumeMessageService::start() { diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 874d64d81..c6c17e27b 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -83,7 +83,7 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& std::function consume_task = std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue_ptr, messages); SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); - pool_->Add(consume_task); + pool_->enqueue(consume_task); } } diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 75d4433fc..44347350c 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -5,8 +5,10 @@ #include "MessageAccessor.h" #include "MixAll.h" #include "ProcessQueueImpl.h" +#include "RpcClient.h" #include "Signature.h" #include "rocketmq/MQClientException.h" +#include #include ROCKETMQ_NAMESPACE_BEGIN @@ -202,7 +204,9 @@ void PushConsumerImpl::queryAssignment(const std::string& topic, } QueryAssignmentRequest request; + setAccessPoint(request.mutable_endpoints()); wrapQueryAssignmentRequest(topic, group_name_, clientId(), MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_, request); + SPDLOG_DEBUG("QueryAssignmentRequest: {}", request.DebugString()); absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -370,6 +374,7 @@ std::shared_ptr PushConsumerImpl::getConsumeMessageServic void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { const std::string& target_host = MessageAccessor::targetEndpoint(msg); + assert(!target_host.empty()); SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, msg.getTopic(), msg.getQueueId(), msg.getMsgId()); AckMessageRequest request; diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 83fea0b2c..b709ad2c0 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -6,12 +6,12 @@ #include "OtlpExporter.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" +#include #include ROCKETMQ_NAMESPACE_BEGIN -class ClientImpl : virtual public Client, - virtual public ClientConfigImpl { +class ClientImpl : virtual public Client, virtual public ClientConfigImpl { public: explicit ClientImpl(std::string group_name); @@ -104,6 +104,9 @@ class ClientImpl : virtual public Client, return resource_bundle; } +protected: + void setAccessPoint(rmq::Endpoints* endpoints) LOCKS_EXCLUDED(name_server_list_mtx_); + private: /** * This is a low-level API that fetches route data from name server through gRPC unary request/response. Once diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index 75879f789..a205705e4 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -6,10 +6,10 @@ #include "ProcessQueue.h" #include "RateLimiter.h" +#include "ThreadPool.h" #include "absl/container/flat_hash_map.h" #include "rocketmq/MessageListener.h" #include "rocketmq/State.h" -#include "src/cpp/server/dynamic_thread_pool.h" ROCKETMQ_NAMESPACE_BEGIN @@ -69,7 +69,7 @@ class ConsumeMessageService { std::atomic state_; int thread_count_; - std::unique_ptr pool_; + std::unique_ptr pool_; std::weak_ptr consumer_; absl::Mutex dispatch_mtx_; diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index b6ae3c465..85a14da32 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -122,4 +122,16 @@ cc_test( "//src/main/cpp/client:client_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "thread_pool_test", + srcs = [ + "ThreadPoolTest.cpp", + ], + deps = [ + "//api:rocketmq_interface", + "@com_github_progschj_ThreadPool//:ThreadPool", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp new file mode 100644 index 000000000..675a12792 --- /dev/null +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -0,0 +1,29 @@ +#include "ThreadPool.h" +#include "gtest/gtest.h" +#include "rocketmq/RocketMQ.h" +#include "absl/memory/memory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPoolTest : public testing::Test { +public: + void SetUp() override { + pool_ = absl::make_unique(2); + } + +protected: + std::unique_ptr pool_; +}; + +TEST_F(ThreadPoolTest, testBasics) { + auto task = [](int cnt) { + for (int i = 0; i < cnt; i++) { + std::cout << "It works" << std::endl; + } + }; + pool_->enqueue(task, 3); +} + +ROCKETMQ_NAMESPACE_END + + diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 2833833d9..0142950f8 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -2,6 +2,7 @@ #include "ClientManagerFactory.h" #include "ClientManagerMock.h" #include "InvocationContext.h" +#include "MessageAccessor.h" #include "grpc/grpc.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/RocketMQ.h" @@ -33,6 +34,7 @@ class PushConsumerImplTest : public testing::Test { int delay_level_{1}; std::shared_ptr> client_manager_; std::shared_ptr push_consumer_; + const std::string target_endpoint_{"localhost:10911"}; }; TEST_F(PushConsumerImplTest, testAck) { @@ -61,6 +63,7 @@ TEST_F(PushConsumerImplTest, testAck) { message.setTags(tag_); message.setKey(key_); message.setDelayTimeLevel(delay_level_); + MessageAccessor::setTargetEndpoint(message, target_endpoint_); push_consumer_->ack(message, callback); @@ -100,9 +103,8 @@ TEST_F(PushConsumerImplTest, testNack) { message.setTags(tag_); message.setKey(key_); message.setDelayTimeLevel(delay_level_); - + MessageAccessor::setTargetEndpoint(message, target_endpoint_); push_consumer_->nack(message, callback); - { absl::MutexLock lk(&mtx); if (!completed) { diff --git a/third_party/ThreadPool.BUILD b/third_party/ThreadPool.BUILD new file mode 100644 index 000000000..616a34ee9 --- /dev/null +++ b/third_party/ThreadPool.BUILD @@ -0,0 +1,11 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "ThreadPool", + hdrs = [ + "ThreadPool.h", + ], + visibility = [ + "//visibility:public" + ], +) \ No newline at end of file From 59fe254795bfba1f9c313c14f502ad2d75c1f4f2 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 31 Aug 2021 17:41:18 +0800 Subject: [PATCH 054/133] Update CMakeLists.txt --- CMakeLists.txt | 14 +++++++------- src/main/cpp/client/CMakeLists.txt | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e18cd5b2..fa08c2eb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,12 +27,6 @@ FetchContent_Declare( GIT_TAG v1.8.1 ) -FetchContent_Declare( - uv - GIT_REPOSITORY https://github.com/libuv/libuv.git - GIT_TAG v1.34.0 -) - FetchContent_Declare( fmt GIT_REPOSITORY https://github.com/fmtlib/fmt.git @@ -51,6 +45,12 @@ FetchContent_Declare( GIT_TAG v1.0.0-rc4 ) +FetchContent_Declare( + ThreadPool + GIT_REPOSITORY https://github.com/lizhanhui/ThreadPool.git + GIT_TAG v1.1 +) + set(FETCHCONTENT_QUIET OFF) option(INSTALL_GTEST OFF) set(BUILD_TESTING OFF) @@ -60,7 +60,7 @@ if (BUILD_TESTS) FetchContent_MakeAvailable(googletest) endif () -FetchContent_MakeAvailable(fmt gRPC spdlog ghc opentelemetry) +FetchContent_MakeAvailable(fmt gRPC spdlog ghc ThreadPool opentelemetry) option(ENABLE_TRACING OFF) if (ENABLE_TRACING) diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt index 79037efdc..f01219b72 100644 --- a/src/main/cpp/client/CMakeLists.txt +++ b/src/main/cpp/client/CMakeLists.txt @@ -23,6 +23,7 @@ target_link_libraries(client_obj crypto scheduler_library ssl + ThreadPool PRIVATE gtest) if (ENABLE_TRACING) @@ -48,7 +49,8 @@ target_link_libraries(client_library crypto mq_proto_library scheduler_library - ssl) + ssl + ThreadPool) if (ENABLE_TRACING) target_link_libraries(client_library PUBLIC From 7e131dc7707b41290cf5f0ba3e273bc22e81b1b0 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 13 Sep 2021 20:31:50 +0800 Subject: [PATCH 055/133] Notify client termination (#17) * Sync with latest PB protocols --- api/rocketmq/DefaultMQProducer.h | 2 +- api/rocketmq/DefaultMQPullConsumer.h | 10 +- api/rocketmq/DefaultMQPushConsumer.h | 6 +- bazel/rocketmq_deps.bzl | 18 +- example/rocketmq/ExampleAsyncProducer.cpp | 4 +- .../rocketmq/ExampleBroadcastPushConsumer.cpp | 8 +- example/rocketmq/ExampleProducer.cpp | 4 +- example/rocketmq/ExamplePullConsumer.cpp | 5 +- example/rocketmq/ExamplePushConsumer.cpp | 4 +- proto/apache/rocketmq/v1/admin.proto | 2 +- proto/apache/rocketmq/v1/definition.proto | 324 ++++++++---------- proto/apache/rocketmq/v1/service.proto | 115 +++---- src/main/cpp/base/MetadataConstants.cpp | 2 +- src/main/cpp/base/MixAll.cpp | 2 +- src/main/cpp/base/include/MetadataConstants.h | 2 +- src/main/cpp/base/include/Protocol.h | 2 +- src/main/cpp/client/ClientManagerFactory.cpp | 16 +- src/main/cpp/client/ClientManagerImpl.cpp | 48 ++- src/main/cpp/client/RpcClientImpl.cpp | 6 + src/main/cpp/client/Signature.cpp | 4 +- src/main/cpp/client/TopicAssignmentInfo.cpp | 21 +- src/main/cpp/client/include/Assignment.h | 27 +- src/main/cpp/client/include/BrokerData.h | 53 --- src/main/cpp/client/include/ClientConfig.h | 2 +- .../cpp/client/include/ClientConfigImpl.h | 10 +- src/main/cpp/client/include/ClientManager.h | 6 + .../cpp/client/include/ClientManagerFactory.h | 2 +- .../cpp/client/include/ClientManagerImpl.h | 18 +- .../cpp/client/include/ConsumeMessageType.h | 4 +- src/main/cpp/client/include/RpcClient.h | 6 + src/main/cpp/client/include/RpcClientImpl.h | 3 + src/main/cpp/client/include/Topic.h | 15 +- .../client/mocks/include/ClientConfigMock.h | 2 +- .../client/mocks/include/ClientManagerMock.h | 5 + .../cpp/client/mocks/include/RpcClientMock.h | 7 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 11 +- src/main/cpp/rocketmq/ClientImpl.cpp | 51 ++- .../ConsumeStandardMessageService.cpp | 4 +- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 4 +- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 2 +- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 2 +- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 25 +- src/main/cpp/rocketmq/ProducerImpl.cpp | 15 +- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 29 +- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 107 ++++-- src/main/cpp/rocketmq/include/ClientImpl.h | 7 +- .../rocketmq/include/ClientResourceBundle.h | 2 +- src/main/cpp/rocketmq/include/Consumer.h | 8 +- src/main/cpp/rocketmq/include/ProcessQueue.h | 2 - .../cpp/rocketmq/include/ProcessQueueImpl.h | 13 +- .../cpp/rocketmq/include/PullConsumerImpl.h | 3 + src/main/cpp/rocketmq/include/PushConsumer.h | 6 +- .../cpp/rocketmq/include/PushConsumerImpl.h | 13 +- .../rocketmq/include/ReceiveMessageAction.h | 25 ++ .../cpp/rocketmq/mocks/include/ConsumerMock.h | 2 + .../rocketmq/mocks/include/ProcessQueueMock.h | 2 - .../rocketmq/mocks/include/PushConsumerMock.h | 4 +- src/test/cpp/it/CMakeLists.txt | 8 - src/test/cpp/it/ClientInstanceTest.cpp | 12 - .../DefaultMQPushConsumerIntegrationTest.cpp | 46 --- src/test/cpp/it/RpcClientIntegrationTest.cpp | 263 -------------- src/test/cpp/it/RpcClientTest.cpp | 56 +-- src/test/cpp/it/TopicPublishInfoTest.cpp | 8 +- .../ut/client/ClientManagerFactoryTest.cpp | 6 +- src/test/cpp/ut/client/ClientManagerTest.cpp | 10 +- .../cpp/ut/client/TopicAssignmentInfoTest.cpp | 57 ++- src/test/cpp/ut/logger/BUILD.bazel | 3 - src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 6 +- .../ConsumeStandardMessageServiceTest.cpp | 4 +- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 6 +- src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 16 +- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 8 +- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 10 +- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 19 +- .../cpp/ut/rocketmq/include/MQClientTest.h | 8 +- 75 files changed, 668 insertions(+), 980 deletions(-) delete mode 100644 src/main/cpp/client/include/BrokerData.h create mode 100644 src/main/cpp/rocketmq/include/ReceiveMessageAction.h delete mode 100644 src/test/cpp/it/ClientInstanceTest.cpp delete mode 100644 src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp delete mode 100644 src/test/cpp/it/RpcClientIntegrationTest.cpp diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 5384701f0..d3fca1b69 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -106,7 +106,7 @@ class DefaultMQProducer { uint32_t compressBodyThreshold() const; void compressBodyThreshold(uint32_t threshold); - void arn(const std::string& arn); + void setResourceNamespace(const std::string& resource_namespace); void setCredentialsProvider(CredentialsProviderPtr credentials_provider); diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index 3f29a5920..e0fd33163 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -1,16 +1,16 @@ #pragma once -#include +#include +#include #include +#include #include -#include #include "AsyncCallback.h" #include "ConsumeType.h" #include "CredentialsProvider.h" #include "MQMessageExt.h" #include "MQMessageQueue.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -32,7 +32,7 @@ class DefaultMQPullConsumer { void pull(const PullMessageQuery& request, PullCallback* callback); - void setArn(const std::string& arn); + void setResourceNamespace(const std::string& resource_namespace); void setNamesrvAddr(const std::string& name_srv); @@ -42,6 +42,4 @@ class DefaultMQPullConsumer { std::shared_ptr impl_; }; - ROCKETMQ_NAMESPACE_END - diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index 9ec51b926..20af32172 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -10,8 +10,8 @@ #include "Logger.h" #include "MQMessageQueue.h" #include "MessageListener.h" -#include "rocketmq/MessageModel.h" #include "rocketmq/Executor.h" +#include "rocketmq/MessageModel.h" ROCKETMQ_NAMESPACE_BEGIN @@ -80,9 +80,9 @@ class DefaultMQPushConsumer { /** * Set abstract-resource-namespace, in which canonical name of topic, group remains unique. - * @param arn Abstract resource namespace. + * @param resource_namespace Abstract resource namespace. */ - void setArn(const char *arn); + void setResourceNamespace(const char* resource_namespace); void setCredentialsProvider(CredentialsProviderPtr credentials_provider); diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index ff39cdafb..e56885de1 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -33,10 +33,10 @@ def rocketmq_deps(): if "com_github_gabime_spdlog" not in native.existing_rules(): http_archive( name = "com_github_gabime_spdlog", - strip_prefix = "spdlog-1.8.2", - sha256 = "e20e6bd8f57e866eaf25a5417f0a38a116e537f1a77ac7b5409ca2b180cec0d5", + strip_prefix = "spdlog-1.9.2", + sha256 = "6fff9215f5cb81760be4cc16d033526d1080427d236e86d70bb02994f85e3d38", urls = [ - "https://github.com/gabime/spdlog/archive/v1.8.2.tar.gz", + "https://github.com/gabime/spdlog/archive/refs/tags/v1.9.2.tar.gz", ], build_file = "@org_apache_rocketmq//third_party:spdlog.BUILD", ) @@ -44,10 +44,10 @@ def rocketmq_deps(): if "com_github_fmtlib_fmt" not in native.existing_rules(): http_archive( name = "com_github_fmtlib_fmt", - sha256 = "5cae7072042b3043e12d53d50ef404bbb76949dad1de368d7f993a15c8c05ecc", - strip_prefix = "fmt-7.1.3", + strip_prefix = "fmt-8.0.1", + sha256 = "b06ca3130158c625848f3fb7418f235155a4d389b2abc3a6245fb01cb0eb1e01", urls = [ - "https://github.com/fmtlib/fmt/archive/7.1.3.tar.gz", + "https://github.com/fmtlib/fmt/archive/refs/tags/8.0.1.tar.gz", ], build_file = "@org_apache_rocketmq//third_party:fmtlib.BUILD", ) @@ -83,9 +83,9 @@ def rocketmq_deps(): if "com_github_grpc_grpc" not in native.existing_rules(): http_archive( name = "com_github_grpc_grpc", - strip_prefix = "grpc-1.38.0", - sha256 = "abd9e52c69000f2c051761cfa1f12d52d8b7647b6c66828a91d462e796f2aede", - urls = ["https://github.com/grpc/grpc/archive/v1.38.0.tar.gz"], + strip_prefix = "grpc-1.40.0", + sha256 = "13e7c6460cd979726e5b3b129bb01c34532f115883ac696a75eb7f1d6a9765ed", + urls = ["https://github.com/grpc/grpc/archive/v1.40.0.tar.gz"], ) if "io_opentelemetry_cpp" not in native.existing_rules(): diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 5b871eb2e..a40dd02ac 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -172,9 +172,9 @@ int main(int argc, char* argv[]) { DefaultMQProducer producer("TestGroup"); producer.setNamesrvAddr("47.98.116.189:80"); producer.compressBodyThreshold(256); - const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; producer.setCredentialsProvider(std::make_shared()); - producer.arn(arn); + producer.setResourceNamespace(resource_namespace); producer.setRegion("cn-hangzhou-pre"); MQMessage message; message.setTopic(topic); diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index c14245fbc..807988258 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -1,5 +1,9 @@ #include "rocketmq/DefaultMQPushConsumer.h" + +#ifndef SPDLOG_ACTIVE_LEVEL #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#endif + #include "spdlog/spdlog.h" #include @@ -27,11 +31,11 @@ int main(int argc, char* argv[]) { const char* cid = "GID_group003"; const char *topic = "yc001"; - const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; DefaultMQPushConsumer push_consumer(cid); push_consumer.setMessageModel(MessageModel::BROADCASTING); - push_consumer.setArn(arn); + push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); push_consumer.setNamesrvAddr("11.165.223.199:9876"); MessageListener* listener = new SampleMQMessageListener; diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index c20ae4661..b7fed59a4 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -39,9 +39,9 @@ int main(int argc, char* argv[]) { producer.setNamesrvAddr(name_server); producer.compressBodyThreshold(256); - const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; producer.setRegion("cn-hangzhou-pre"); - producer.arn(arn); + producer.setResourceNamespace(resource_namespace); producer.setCredentialsProvider(std::make_shared()); MQMessage message; diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp index 0858186df..b758e5454 100644 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -2,12 +2,11 @@ #include "rocketmq/DefaultMQPullConsumer.h" #include "rocketmq/Logger.h" #include -#include int main(int argc, char* argv[]) { const char* group = "GID_group003"; const char* topic = "yc001"; - const char* arn = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; const char* name_server_list = "ipv4:11.165.223.199:9876"; rocketmq::Logger& logger = rocketmq::getLogger(); @@ -15,7 +14,7 @@ int main(int argc, char* argv[]) { logger.init(); rocketmq::DefaultMQPullConsumer pull_consumer(group); - pull_consumer.setArn(arn); + pull_consumer.setResourceNamespace(resource_namespace); pull_consumer.setCredentialsProvider(std::make_shared()); pull_consumer.setNamesrvAddr(name_server_list); pull_consumer.start(); diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 584353ac2..166d16863 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -27,10 +27,10 @@ int main(int argc, char* argv[]) { const char* cid = "GID_cpp_sdk_standard"; const char* topic = "cpp_sdk_standard"; - const char* arn = "MQ_INST_1080056302921134_BXuIbML7"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; DefaultMQPushConsumer push_consumer(cid); - push_consumer.setArn(arn); + push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); push_consumer.setNamesrvAddr("47.98.116.189:80"); MessageListener* listener = new SampleMQMessageListener; diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v1/admin.proto index 4358c4bd8..bd7b203d4 100644 --- a/proto/apache/rocketmq/v1/admin.proto +++ b/proto/apache/rocketmq/v1/admin.proto @@ -90,7 +90,7 @@ message DeleteConsumerGroupRequest { } message UpdateConsumerGroupRequest { - ConsumerGroup consumer_group = 1; + ConsumerData consumer_data = 1; google.protobuf.FieldMask update_mask = 2; } diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index 2655dfb17..c7e79278e 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -14,20 +14,20 @@ option java_string_check_utf8 = true; option java_outer_classname = "MQDomain"; enum Permission { - NONE = 0; - READ = 1; - WRITE = 2; - READ_WRITE = 3; + NONE = 0; + READ = 1; + WRITE = 2; + READ_WRITE = 3; } enum FilterType { - TAG = 0; - SQL = 1; + TAG = 0; + SQL = 1; } message FilterExpression { - FilterType type = 1; - string expression = 2; + FilterType type = 1; + string expression = 2; } // Dead lettering is done on a best effort basis. The same message might be @@ -36,262 +36,224 @@ message FilterExpression { // If validation on any of the fields fails at subscription creation/update, // the create/update subscription request will fail. message DeadLetterPolicy { - // The maximum number of delivery attempts for any message. The value must be - // between 5 and 100. - // - // This field will be honored on a best effort basis. - // - // If this parameter is 0, a default value of 5 is used. - int32 max_delivery_attempts = 1; + // The maximum number of delivery attempts for any message. + // + // This field will be honored on a best effort basis. + // + // If this parameter is 0, a default value of 5 is used. + int32 max_delivery_attempts = 1; } message Resource { - // Abstract resource namespace - string arn = 1; + string resource_namespace = 1; - // Resource name identifier, which remains unique within the abstract resource namespace. - string name = 2; + // Resource name identifier, which remains unique within the abstract resource + // namespace. + string name = 2; } enum ConsumeModel { - CLUSTERING = 0; - BROADCASTING = 1; + CLUSTERING = 0; + BROADCASTING = 1; } -message ProducerGroup { - Resource group = 1; -} +message ProducerData { Resource group = 1; } enum ConsumePolicy { - RESUME = 0; - PLAYBACK = 1; - DISCARD = 2; - TARGET_TIMESTAMP = 3; + RESUME = 0; + PLAYBACK = 1; + DISCARD = 2; + TARGET_TIMESTAMP = 3; } -message ConsumerGroup { - Resource group = 1; +enum ConsumeMessageType { + ACTIVE = 0; + PASSIVE = 1; +} - repeated SubscriptionEntry subscriptions = 2; +message ConsumerData { + Resource group = 1; - ConsumeModel consume_model = 3; + repeated SubscriptionEntry subscriptions = 2; - ConsumePolicy consume_policy = 4; + ConsumeModel consume_model = 3; - DeadLetterPolicy dead_letter_policy = 5; + ConsumePolicy consume_policy = 4; - ConsumeMessageType consume_type = 6; -} + DeadLetterPolicy dead_letter_policy = 5; -message SubscriptionEntry { - Resource topic = 1; - FilterExpression expression = 2; + ConsumeMessageType consume_type = 6; } -message HeartbeatEntry { - string client_id = 1; - oneof client_data { - ProducerGroup producer_group = 2; - ConsumerGroup consumer_group = 3; - } - bool need_rebalance = 4; +message SubscriptionEntry { + Resource topic = 1; + FilterExpression expression = 2; } enum AddressScheme { - IPv4 = 0; - IPv6 = 1; - DOMAIN_NAME = 2; + IPv4 = 0; + IPv6 = 1; + DOMAIN_NAME = 2; } message Address { - string host = 1; - int32 port = 2; + string host = 1; + int32 port = 2; } message Endpoints { - AddressScheme scheme = 1; - repeated Address addresses = 2; + AddressScheme scheme = 1; + repeated Address addresses = 2; } message Broker { - // Name of the broker - string name = 1; - - // Broker index. Canonically, index = 0 implies that the broker is playing leader role while brokers with index > 0 - // play follower role. - int32 id = 2; - - // Address of the broker, complying with the following scheme - // 1. dns:[//authority/]host[:port] - // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses - // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses - Endpoints endpoints = 3; + // Name of the broker + string name = 1; + + // Broker index. Canonically, index = 0 implies that the broker is playing + // leader role while brokers with index > 0 play follower role. + int32 id = 2; + + // Address of the broker, complying with the following scheme + // 1. dns:[//authority/]host[:port] + // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses + // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses + Endpoints endpoints = 3; } message Partition { - Resource topic = 1; - int32 id = 2; - Permission permission = 3; - Broker broker = 4; -} - -// Language of the client -enum Language { - JAVA = 0; - CPP = 1; - C_SHARP = 2; - PYTHON = 3; - DELPHI = 4; - ERLANG = 5; - RUBY = 6; - GO = 7; - PHP = 8; - RUST = 9; - C = 10; - PERL = 11; - OBJECTIVE_C = 12; - DART = 13; - KOTLIN = 14; - NODE_JS = 15; + Resource topic = 1; + int32 id = 2; + Permission permission = 3; + Broker broker = 4; } enum MessageType { - NORMAL = 0; + NORMAL = 0; - // Sequenced message - FIFO = 1; + // Sequenced message + FIFO = 1; - // Messages that are delivered after the specified duration. - DELAY = 2; + // Messages that are delivered after the specified duration. + DELAY = 2; - // Messages that are transactional. Only committed messages are delivered to subscribers. - TRANSACTION = 3; + // Messages that are transactional. Only committed messages are delivered to + // subscribers. + TRANSACTION = 3; } enum DigestType { - // CRC algorithm achieves goal of detecting random data error with lowest computation overhead. - CRC32 = 0; + // CRC algorithm achieves goal of detecting random data error with lowest + // computation overhead. + CRC32 = 0; - // MD5 algorithm achieves good balance between collision rate and computation overhead. - MD5 = 1; + // MD5 algorithm achieves good balance between collision rate and computation + // overhead. + MD5 = 1; - // SHA-family has substantially fewer collision with fair amount of computation. - SHA1 = 2; + // SHA-family has substantially fewer collision with fair amount of + // computation. + SHA1 = 2; } message Digest { - DigestType type = 1; - string checksum = 2; + DigestType type = 1; + string checksum = 2; } enum Encoding { - IDENTITY = 0; - GZIP = 1; - SNAPPY = 2; + IDENTITY = 0; + GZIP = 1; } message SystemAttribute { - // Tag - string tag = 1; - - // Message keys - repeated string keys = 2; + // Tag + string tag = 1; - // Message identifier, client-side generated, remains unique. - string message_id = 3; + // Message keys + repeated string keys = 2; - // Message body digest - Digest body_digest = 4; + // Message identifier, client-side generated, remains unique. + string message_id = 3; - // Message body encoding. Candidate options are identity, gzip, snappy etc. - Encoding body_encoding = 5; + // Message body digest + Digest body_digest = 4; - // Message type, normal, FIFO or transactional. - MessageType message_type = 6; + // Message body encoding. Candidate options are identity, gzip, snappy etc. + Encoding body_encoding = 5; - // Message born time-point. - google.protobuf.Timestamp born_timestamp = 7; + // Message type, normal, FIFO or transactional. + MessageType message_type = 6; - // Message born host. Valid options are IPv4, IPv6 or client host domain name. - string born_host = 8; + // Message born time-point. + google.protobuf.Timestamp born_timestamp = 7; - // Time-point at which the message is stored in the broker. - google.protobuf.Timestamp store_timestamp = 9; + // Message born host. Valid options are IPv4, IPv6 or client host domain name. + string born_host = 8; - // The broker that stores this message. It may be name, IP or arbitrary identifier that uniquely identify the broker. - string store_host = 10; + // Time-point at which the message is stored in the broker. + google.protobuf.Timestamp store_timestamp = 9; - oneof timed_delivery { - // Time-point at which broker delivers to clients. - google.protobuf.Timestamp delivery_timestamp = 11; + // The broker that stores this message. It may be name, IP or arbitrary + // identifier that uniquely identify the broker. + string store_host = 10; - // Level-based delay strategy. - int32 delay_level = 12; - } + oneof timed_delivery { + // Time-point at which broker delivers to clients. + google.protobuf.Timestamp delivery_timestamp = 11; - // If a message is acquired by way of POP, this field holds the receipt. Clients use the receipt to - // acknowledge or negatively acknowledge the message. - string receipt_handle = 13; + // Level-based delay strategy. + int32 delay_level = 12; + } - // Partition identifier in which a message is physically stored. - int32 partition_id = 14; + // If a message is acquired by way of POP, this field holds the receipt. + // Clients use the receipt to acknowledge or negatively acknowledge the + // message. + string receipt_handle = 13; - // Partition offset at which a message is stored. - int64 partition_offset = 15; + // Partition identifier in which a message is physically stored. + int32 partition_id = 14; - // Period of time servers would remain invisible once a message is acquired. - google.protobuf.Duration invisible_period = 16; + // Partition offset at which a message is stored. + int64 partition_offset = 15; - // Business code may failed to process messages for the moment. Hence, clients may request servers to deliver them - // again using certain back-off strategy, the attempt is 1 not 0 if message is delivered first time. - int32 delivery_attempt = 17; + // Period of time servers would remain invisible once a message is acquired. + google.protobuf.Duration invisible_period = 16; - // Message's max delivery attempts count, if message's delivery count exceed this value, it will be sent to dlq topic. - int32 max_delivery_attempts = 18; + // Business code may failed to process messages for the moment. Hence, clients + // may request servers to deliver them again using certain back-off strategy, + // the attempt is 1 not 0 if message is delivered first time. + int32 delivery_attempt = 17; - // Message producer load-balance group if applicable. - Resource producer_group = 19; + // Message producer load-balance group if applicable. + Resource producer_group = 18; - string message_group = 20; + string message_group = 19; - // Trace context. - string trace_context = 21; + // Trace context. + string trace_context = 20; - // Delay time of first resolve orphaned transaction request from server. - google.protobuf.Duration orphaned_transaction_recovery_period = 22; + // Delay time of first resolve orphaned transaction request from server. + google.protobuf.Duration orphaned_transaction_recovery_period = 21; } message Message { - Resource topic = 1; - // User defined key-value pairs. - map user_attribute = 2; - SystemAttribute system_attribute = 3; - bytes body = 4; + Resource topic = 1; + // User defined key-value pairs. + map user_attribute = 2; + SystemAttribute system_attribute = 3; + bytes body = 4; } -enum ConsumeMessageType { - // Partition-based pull. Client is responsible to first seek to a position in the partition and then - // initiate a sequence of pull request to fetch messages within given range. - PULL = 0; - - // Offsets are implicitly managed by brokers. Clients generally perform stateless pop requests from - // logical queues pertaining to the specified topic. - POP = 1; -} - -message Assignment { - Partition Partition = 1; - // to delete. - ConsumeMessageType mode = 2; -} +message Assignment { Partition Partition = 1; } enum QueryOffsetPolicy { - // Use this option if client wishes to playback all existing messages. - BEGINNING = 0; + // Use this option if client wishes to playback all existing messages. + BEGINNING = 0; - // Use this option if client wishes to skip all existing messages. - END = 1; + // Use this option if client wishes to skip all existing messages. + END = 1; - // Use this option if time-based seek is targeted. - TIME_POINT = 2; + // Use this option if time-based seek is targeted. + TIME_POINT = 2; } \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 5d90ca8e7..ac7af1ca9 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -55,7 +55,7 @@ message QueryAssignmentRequest { Resource topic = 1; Resource group = 2; string client_id = 3; - + // Service access point Endpoints endpoints = 4; } @@ -96,9 +96,7 @@ message AckMessageRequest { string message_id = 6; } -message AckMessageResponse { - ResponseCommon common = 1; -} +message AckMessageResponse { ResponseCommon common = 1; } message NackMessageRequest { Resource group = 1; @@ -108,13 +106,9 @@ message NackMessageRequest { string message_id = 5; int32 delivery_attempt = 6; int32 max_delivery_attempts = 7; - // to delete. - ConsumeModel consume_model = 8; } -message NackMessageResponse { - ResponseCommon common = 1; -} +message NackMessageResponse { ResponseCommon common = 1; } message ForwardMessageToDeadLetterQueueRequest { Resource group = 1; @@ -126,28 +120,28 @@ message ForwardMessageToDeadLetterQueueRequest { int32 max_delivery_attempts = 7; } -message ForwardMessageToDeadLetterQueueResponse { - ResponseCommon common = 1; -} +message ForwardMessageToDeadLetterQueueResponse { ResponseCommon common = 1; } message HeartbeatRequest { - repeated HeartbeatEntry heartbeats = 1; + string client_id = 1; + oneof client_data { + ProducerData producer_data = 2; + ConsumerData consumer_data = 3; + } + bool fifo_flag = 4; } -message HeartbeatResponse { - ResponseCommon common = 1; -} +message HeartbeatResponse { ResponseCommon common = 1; } message HealthCheckRequest { - string client_host = 1; + Resource group = 1; + string client_host = 2; } -message HealthCheckResponse { - ResponseCommon common = 1; -} +message HealthCheckResponse { ResponseCommon common = 1; } message EndTransactionRequest { - Resource producer_group = 1; + Resource group = 1; string message_id = 2; string transaction_id = 3; enum TransactionResolution { @@ -163,9 +157,7 @@ message EndTransactionRequest { string trace_context = 6; } -message EndTransactionResponse { - ResponseCommon common = 1; -} +message EndTransactionResponse { ResponseCommon common = 1; } message QueryOffsetRequest { Partition partition = 1; @@ -202,11 +194,9 @@ message UpdateOffsetRequest { int64 partition_offset = 3; } -message UpdateOffsetResponse { - ResponseCommon common = 1; -} +message UpdateOffsetResponse { ResponseCommon common = 1; } -message ClientResourceBundle { +message GenericPollingRequest { string client_id = 1; repeated Resource topics = 2; oneof group { @@ -215,17 +205,9 @@ message ClientResourceBundle { } } -message GenericPollingRequest { - ClientResourceBundle client_resource_bundle = 1; -} +message GenericPollingResponse { ResponseCommon common = 1; } -message GenericPollingResponse { - ResponseCommon common = 1; -} - -message PrintThreadStackRequest { - string mid = 1; -} +message PrintThreadStackRequest { string mid = 1; } message PrintThreadStackResponse { ResponseCommon common = 1; @@ -235,8 +217,7 @@ message PrintThreadStackResponse { message VerifyMessageConsumptionRequest { string mid = 1; - Partition Partition = 2; - Message message = 3; + Message message = 2; } message VerifyMessageConsumptionResponse { @@ -266,46 +247,44 @@ message MultiplexingResponse { } } +message NotifyClientTerminationRequest { + Resource group = 1; + string client_id = 2; +} + +message NotifyClientTerminationResponse { ResponseCommon common = 1; } + service MessagingService { - rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { - } + rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) {} - rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { - } + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) {} - rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { - } + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) {} - rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { - } + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) {} - rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { - } + rpc QueryAssignment(QueryAssignmentRequest) + returns (QueryAssignmentResponse) {} - rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { - } + rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) {} - rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { - } + rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) {} - rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { - } + rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) {} - rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) returns (ForwardMessageToDeadLetterQueueResponse) { - } + rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) + returns (ForwardMessageToDeadLetterQueueResponse) {} - rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { - } + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) {} - rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { - } + rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) {} - rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { - } + rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) {} - rpc UpdateOffset(UpdateOffsetRequest) returns (UpdateOffsetResponse) { - } + rpc UpdateOffset(UpdateOffsetRequest) returns (UpdateOffsetResponse) {} - rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) { - } + rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) {} + + rpc NotifyClientTermination(NotifyClientTerminationRequest) + returns (NotifyClientTerminationResponse) {} } \ No newline at end of file diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp index 718cdbac1..42777244c 100644 --- a/src/main/cpp/base/MetadataConstants.cpp +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -3,7 +3,7 @@ ROCKETMQ_NAMESPACE_BEGIN const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; -const char* MetadataConstants::ARN_KEY = "x-mq-arn"; +const char* MetadataConstants::NAMESPACE_KEY = "x-mq-namespace"; const char* MetadataConstants::AUTHORIZATION = "authorization"; const char* MetadataConstants::STS_SESSION_TOKEN = "x-mq-session-token"; const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index a5595064d..433b36d99 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -56,7 +56,7 @@ const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; // Span attribute name list const char* MixAll::SPAN_ATTRIBUTE_ACCESS_KEY = "ak"; -const char* MixAll::SPAN_ATTRIBUTE_ARN = "arn"; +const char* MixAll::SPAN_ATTRIBUTE_ARN = "setResourceNamespace"; const char* MixAll::SPAN_ATTRIBUTE_KEYS = "keys"; const char* MixAll::SPAN_ATTRIBUTE_MESSAGE_TYPE = "msgType"; const char* MixAll::SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP = "deliveryTimestamp"; diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index 61260d278..bb5c2c947 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -7,7 +7,7 @@ class MetadataConstants { public: static const char* REQUEST_ID_KEY; static const char* TENANT_ID_KEY; - static const char* ARN_KEY; + static const char* NAMESPACE_KEY; static const char* AUTHORIZATION; static const char* STS_SESSION_TOKEN; static const char* DATE_TIME_KEY; diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index a25db9a61..5f4aa6d23 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -43,7 +43,7 @@ struct Resource { /** * Abstract resource namespace */ - std::string arn; + std::string resource_namespace; /** * Resource name, which remains unique within given abstract resource namespace. diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 86bde48bd..982128b39 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -11,27 +11,27 @@ ClientManagerFactory& ClientManagerFactory::getInstance() { ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& client_config) { { absl::MutexLock lock(&client_manager_table_mtx_); - auto search = client_manager_table_.find(client_config.arn()); + auto search = client_manager_table_.find(client_config.resourceNamespace()); if (search != client_manager_table_.end()) { ClientManagerPtr client_manager = search->second.lock(); if (client_manager) { - SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.arn()); + SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.resourceNamespace()); return client_manager; } else { - client_manager_table_.erase(client_config.arn()); + client_manager_table_.erase(client_config.resourceNamespace()); } } - ClientManagerPtr client_manager = std::make_shared(client_config.arn()); + ClientManagerPtr client_manager = std::make_shared(client_config.resourceNamespace()); std::weak_ptr client_instance_weak_ptr(client_manager); - client_manager_table_.insert_or_assign(client_config.arn(), client_instance_weak_ptr); - SPDLOG_INFO("Created a new client manager[ARN={}]", client_config.arn()); + client_manager_table_.insert_or_assign(client_config.resourceNamespace(), client_instance_weak_ptr); + SPDLOG_INFO("Created a new client manager[ARN={}]", client_config.resourceNamespace()); return client_manager; } } -void ClientManagerFactory::addClientManager(const std::string& arn, const ClientManagerPtr& client_manager) { +void ClientManagerFactory::addClientManager(const std::string& resource_namespace, const ClientManagerPtr& client_manager) { absl::MutexLock lk(&client_manager_table_mtx_); - client_manager_table_.insert_or_assign(arn, std::weak_ptr(client_manager)); + client_manager_table_.insert_or_assign(resource_namespace, std::weak_ptr(client_manager)); } ClientManagerFactory::ClientManagerFactory() : admin_server_(admin::AdminFacade::getServer()) { admin_server_.start(); } diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index f047d0890..5bac83e3a 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -9,6 +9,7 @@ #include "MixAll.h" #include "Partition.h" #include "Protocol.h" +#include "RpcClient.h" #include "RpcClientImpl.h" #include "TlsHelper.h" #include "UtilAll.h" @@ -16,14 +17,16 @@ #include "rocketmq/ErrorCode.h" #include "rocketmq/MQMessageExt.h" #include +#include #include #include #include ROCKETMQ_NAMESPACE_BEGIN -ClientManagerImpl::ClientManagerImpl(std::string arn) - : arn_(std::move(arn)), state_(State::CREATED), completion_queue_(std::make_shared()), +ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) + : resource_namespace_(std::move(resource_namespace)), state_(State::CREATED), + completion_queue_(std::make_shared()), callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); @@ -44,12 +47,12 @@ ClientManagerImpl::ClientManagerImpl(std::string arn) tls_channel_credential_options_.watch_root_certs(); tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - SPDLOG_INFO("ClientManager[ARN={}] created", arn_); + SPDLOG_INFO("ClientManager[ARN={}] created", resource_namespace_); } ClientManagerImpl::~ClientManagerImpl() { shutdown(); - SPDLOG_INFO("ClientManager[ARN={}] destructed", arn_); + SPDLOG_INFO("ClientManager[ARN={}] destructed", resource_namespace_); } void ClientManagerImpl::start() { @@ -509,7 +512,7 @@ void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metad std::vector topic_partitions; for (const auto& partition : partitions) { - Topic t(partition.topic().arn(), partition.topic().name()); + Topic t(partition.topic().resource_namespace(), partition.topic().name()); auto& broker = partition.broker(); AddressScheme scheme = AddressScheme::IPv4; @@ -723,7 +726,7 @@ void ClientManagerImpl::processPullResult(const grpc::ClientContext& client_cont } bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { - assert(item.topic().arn() == arn_); + assert(item.topic().resource_namespace() == resource_namespace_); // base message_ext.setTopic(item.topic().name()); @@ -823,10 +826,6 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess message_ext.setBody(uncompressed); break; } - case rmq::Encoding::SNAPPY: { - SPDLOG_WARN("Snappy encoding is not supported yet"); - break; - } case rmq::Encoding::IDENTITY: { message_ext.setBody(item.body()); break; @@ -1157,6 +1156,35 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); } +bool ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) { + auto client = getRpcClient(target_host); + if (!client) { + return false; + } + + grpc::ClientContext context; + context.set_deadline(std::chrono::system_clock::now() + timeout); + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + NotifyClientTerminationResponse response; + grpc::Status status = client->notifyClientTermination(&context, request, &response); + if (!status.ok()) { + return false; + } + + if (google::rpc::Code::OK == response.common().status().code()) { + SPDLOG_INFO("Notify client termination to {}", target_host); + return true; + } + + SPDLOG_WARN("Failed to notify client termination to {}", target_host); + return false; +} + void ClientManagerImpl::logStats() { std::string stats; latency_histogram_.reportAndReset(stats); diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 7cb89a489..2337f1a07 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -123,6 +123,12 @@ void RpcClientImpl::asyncMultiplexingCall(const MultiplexingRequest& request, invocation_context); } +grpc::Status RpcClientImpl::notifyClientTermination(grpc::ClientContext* context, + const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) { + return stub_->NotifyClientTermination(context, request, response); +} + void RpcClientImpl::asyncQueryOffset(const QueryOffsetRequest& request, InvocationContext* invocation_context) { assert(invocation_context); diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index e72c1237d..3aee816d6 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -19,8 +19,8 @@ void Signature::sign(ClientConfig* client, absl::flat_hash_maptenantId()}); } - if (!client->arn().empty()) { - metadata.insert({MetadataConstants::ARN_KEY, client->arn()}); + if (!client->resourceNamespace().empty()) { + metadata.insert({MetadataConstants::NAMESPACE_KEY, client->resourceNamespace()}); } absl::Time now = absl::Now(); diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index 162304217..aed343d78 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -6,8 +6,7 @@ ROCKETMQ_NAMESPACE_BEGIN thread_local uint32_t TopicAssignment::query_which_broker_ = 0; -TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) - : debug_string_(response.DebugString()) { +TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) : debug_string_(response.DebugString()) { if (response.common().status().code() != google::rpc::Code::OK) { SPDLOG_WARN("QueryAssignmentResponse#code is not SUCCESS. Keep assignment info intact. QueryAssignmentResponse: {}", response.DebugString()); @@ -56,23 +55,7 @@ TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) } } message_queue.serviceAddress(service_address); - - ConsumeMessageType mode; - switch (item.mode()) { - case rmq::ConsumeMessageType::PULL: { - mode = ConsumeMessageType::PULL; - break; - } - case rmq::ConsumeMessageType::POP: { - mode = ConsumeMessageType::POP; - break; - } - default: { - SPDLOG_WARN("Unknown message request mode: {}, default to pop", item.mode()); - mode = ConsumeMessageType::POP; - } - } - assignment_list_.emplace_back(Assignment(message_queue, mode)); + assignment_list_.emplace_back(Assignment(message_queue)); } std::sort(assignment_list_.begin(), assignment_list_.end()); } diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index ddc67f7d1..17cfdb3fa 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -1,7 +1,7 @@ #pragma once -#include "rocketmq/MQMessageQueue.h" #include "ConsumeMessageType.h" +#include "rocketmq/MQMessageQueue.h" #include #include @@ -9,35 +9,24 @@ ROCKETMQ_NAMESPACE_BEGIN class Assignment { public: - Assignment(MQMessageQueue message_queue, ConsumeMessageType consume_type) - : message_queue_(std::move(message_queue)), consume_type_(consume_type) {} + Assignment(MQMessageQueue message_queue) + : message_queue_(std::move(message_queue)) {} - bool operator==(const Assignment& rhs) const { + bool operator==(const Assignment &rhs) const { if (this == &rhs) { return true; } - return message_queue_ == rhs.message_queue_ && consume_type_ == rhs.consume_type_; + return message_queue_ == rhs.message_queue_; } - bool operator<(const Assignment& rhs) const { - if (message_queue_ != rhs.message_queue_) { - return message_queue_ < rhs.message_queue_; - } - - if (consume_type_ != rhs.consume_type_) { - return consume_type_ < rhs.consume_type_; - } - - return false; + bool operator<(const Assignment &rhs) const { + return message_queue_ < rhs.message_queue_; } - const MQMessageQueue& messageQueue() const { return message_queue_; } - - ConsumeMessageType consumeType() const { return consume_type_; } + const MQMessageQueue &messageQueue() const { return message_queue_; } private: MQMessageQueue message_queue_; - ConsumeMessageType consume_type_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/BrokerData.h b/src/main/cpp/client/include/BrokerData.h deleted file mode 100644 index 50c215f26..000000000 --- a/src/main/cpp/client/include/BrokerData.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "RpcClient.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "rocketmq/RocketMQ.h" - -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -namespace rmq = apache::rocketmq::v1; - -class BrokerData { -public: - explicit BrokerData(const rmq::BrokerData& broker_data) - : cluster_(broker_data.cluster()), broker_name_(broker_data.broker_name()), - debug_string_(broker_data.DebugString()) { - for (const auto& entry : broker_data.addresses()) { - addresses_.insert(std::make_pair(entry.first, entry.second)); - } - } - - bool operator==(const BrokerData& rhs) const { - return cluster_ == rhs.cluster_ && broker_name_ == rhs.broker_name_ && addresses_ == rhs.addresses_; - } - - bool operator<(const BrokerData& rhs) const { - if (cluster_ != rhs.cluster_) { - return cluster_ < rhs.cluster_; - } - - return broker_name_ < rhs.broker_name_; - } - - const std::string& cluster() const { return cluster_; } - - const std::string& brokerName() const { return broker_name_; } - - const std::map& addresses() const { return addresses_; } - - const std::string& debugString() const { return debug_string_; } - -private: - std::string cluster_; - std::string broker_name_; - std::map addresses_; - - std::string debug_string_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 0e71cb271..b5ed3d15a 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -15,7 +15,7 @@ class ClientConfig { virtual const std::string& serviceName() const = 0; - virtual const std::string& arn() const = 0; + virtual const std::string& resourceNamespace() const = 0; virtual CredentialsProviderPtr credentialsProvider() = 0; diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index a15444cea..f106d5805 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -20,9 +20,11 @@ class ClientConfigImpl : virtual public ClientConfig { ~ClientConfigImpl() override = default; - const std::string& arn() const override { return arn_; } + const std::string& resourceNamespace() const override { return resource_namespace_; } - void arn(absl::string_view arn) { arn_ = std::string(arn.data(), arn.length()); } + void resourceNamespace(absl::string_view resource_namespace) { + resource_namespace_ = std::string(resource_namespace.data(), resource_namespace.length()); + } std::string clientId() const override; @@ -72,9 +74,9 @@ class ClientConfigImpl : virtual public ClientConfig { std::string region_; /** - * Abstract Resource Namespace, in which topic/group_name remain unique. + * RocketMQ instance namespace, in which topic, consumer group and any other abstract resources remain unique. */ - std::string arn_; + std::string resource_namespace_; /** * Tenant identifier. diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 0df570b52..154ad9469 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -2,10 +2,12 @@ #include "Client.h" #include "ReceiveMessageCallback.h" +#include "RpcClient.h" #include "Scheduler.h" #include "TopAddressing.h" #include "TopicRouteData.h" #include "rocketmq/MQMessageExt.h" +#include #include ROCKETMQ_NAMESPACE_BEGIN @@ -82,6 +84,10 @@ class ClientManager { virtual void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, const std::function*)>& cb) = 0; + + virtual bool notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) = 0; }; using ClientManagerPtr = std::shared_ptr; diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h index a50e3d0ba..98405f32f 100644 --- a/src/main/cpp/client/include/ClientManagerFactory.h +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -19,7 +19,7 @@ class ClientManagerFactory { ClientManagerPtr getClientManager(const ClientConfig& client_config) LOCKS_EXCLUDED(client_manager_table_mtx_); // For test purpose only - void addClientManager(const std::string& arn, const ClientManagerPtr& client_manager) + void addClientManager(const std::string& resource_namespace, const ClientManagerPtr& client_manager) LOCKS_EXCLUDED(client_manager_table_mtx_); private: diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 2381cf698..faec6e49b 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -7,6 +7,7 @@ #include "InvocationContext.h" #include "OrphanTransactionCallback.h" #include "ReceiveMessageCallback.h" +#include "RpcClient.h" #include "RpcClientImpl.h" #include "Scheduler.h" #include "SendMessageContext.h" @@ -37,9 +38,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share * @brief Construct a new Client Manager Impl object * TODO: Make it protected such that instantiating it through ClientManagerFactory only, achieving Singleton * effectively. - * @param arn Abstract resource namespace, in which this client manager lives. + * @param resource_namespace Abstract resource namespace, in which this client manager lives. */ - explicit ClientManagerImpl(std::string arn); + explicit ClientManagerImpl(std::string resource_namespace); ~ClientManagerImpl() override; @@ -176,10 +177,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::chrono::milliseconds timeout, const std::function*)>& cb) override; -#ifdef ENABLE_TRACING - nostd::shared_ptr getTracer(); - void updateTraceProvider(); -#endif + bool notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) override; void trace(bool trace) { trace_ = trace; } @@ -206,11 +206,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share static const char* STATS_TASK_NAME; static const char* HEALTH_CHECK_TASK_NAME; - /** - * Abstract resource namespace. Each user may have one or more instances and each each instance has an independent - * abstract resource namespace. - */ - std::string arn_; + std::string resource_namespace_; std::atomic state_; diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h index b27c347c2..3f649ad37 100644 --- a/src/main/cpp/client/include/ConsumeMessageType.h +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -7,8 +7,8 @@ ROCKETMQ_NAMESPACE_BEGIN enum class ConsumeMessageType : int8_t { - PULL = 0, - POP = 1, + ACTIVE = 0, + PASSIVE = 1, }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index 9d23b35cd..e9b9d71f9 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -55,6 +55,8 @@ using PullMessageRequest = rmq::PullMessageRequest; using PullMessageResponse = rmq::PullMessageResponse; using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; +using NotifyClientTerminationRequest = rmq::NotifyClientTerminationRequest; +using NotifyClientTerminationResponse = rmq::NotifyClientTerminationResponse; class RpcClient { public: @@ -104,6 +106,10 @@ class RpcClient { const ForwardMessageToDeadLetterQueueRequest& request, InvocationContext* invocation_context) = 0; + virtual grpc::Status notifyClientTermination(grpc::ClientContext* context, + const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) = 0; + /** * Indicate if heartbeat is required. * @return true if periodic heartbeat is required; false otherwise. diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index aa79930b9..cc0439d69 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -61,6 +61,9 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; + grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, + NotifyClientTerminationResponse* response) override; + bool needHeartbeat() override; void needHeartbeat(bool need_heartbeat) override; diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h index 118e504ea..8778d9263 100644 --- a/src/main/cpp/client/include/Topic.h +++ b/src/main/cpp/client/include/Topic.h @@ -8,18 +8,21 @@ ROCKETMQ_NAMESPACE_BEGIN class Topic { public: - Topic(std::string arn, std::string name) : arn_(std::move(arn)), name_(std::move(name)) {} + Topic(std::string resource_namespace, std::string name) + : resource_namespace_(std::move(resource_namespace)), name_(std::move(name)) {} - const std::string& arn() const { return arn_; } + const std::string& resourceNamespace() const { return resource_namespace_; } const std::string& name() const { return name_; } - bool operator==(const Topic& other) const { return arn_ == other.arn_ && name_ == other.name_; } + bool operator==(const Topic& other) const { + return resource_namespace_ == other.resource_namespace_ && name_ == other.name_; + } bool operator<(const Topic& other) const { - if (arn_ < other.arn_) { + if (resource_namespace_ < other.resource_namespace_) { return true; - } else if (arn_ > other.arn_) { + } else if (resource_namespace_ > other.resource_namespace_) { return false; } @@ -32,7 +35,7 @@ class Topic { } private: - std::string arn_; + std::string resource_namespace_; std::string name_; }; diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h index 7dfd9777c..0e5315eb6 100644 --- a/src/main/cpp/client/mocks/include/ClientConfigMock.h +++ b/src/main/cpp/client/mocks/include/ClientConfigMock.h @@ -10,7 +10,7 @@ class ClientConfigMock : virtual public ClientConfig { ~ClientConfigMock() override = default; MOCK_METHOD(const std::string&, region, (), (const override)); MOCK_METHOD(const std::string&, serviceName, (), (const override)); - MOCK_METHOD(const std::string&, arn, (), (const overide)); + MOCK_METHOD(const std::string&, resourceNamespace, (), (const overide)); MOCK_METHOD(CredentialsProviderPtr, credentialsProvider, (), (override)); MOCK_METHOD(const std::string&, tenantId, (), (const override)); MOCK_METHOD(absl::Duration, getIoTimeout, (), (const override)); diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 56e874828..c3c0d4c10 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -2,6 +2,7 @@ #include "ClientManager.h" #include "gmock/gmock.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -87,6 +88,10 @@ class ClientManagerMock : public ClientManager { (const std::string&, const Metadata&, const PullMessageRequest&, std::chrono::milliseconds, (const std::function*)>&)), (override)); + + MOCK_METHOD(bool, notifyClientTermination, + (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), + (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index 1e80802bd..d3f925290 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -44,7 +44,12 @@ class RpcClientMock : public RpcClient { MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); MOCK_METHOD(void, asyncForwardMessageToDeadLetterQueue, - (const ForwardMessageToDeadLetterQueueRequest&, InvocationContext*), + (const ForwardMessageToDeadLetterQueueRequest&, + InvocationContext*), + (override)); + + MOCK_METHOD(grpc::Status, notifyClientTermination, + (grpc::ClientContext*, const NotifyClientTerminationRequest&, NotifyClientTerminationResponse* response), (override)); MOCK_METHOD(bool, needHeartbeat, (), (override)); diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index 9309f8dcc..654b4e665 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -23,6 +23,8 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { return; } + auto receive_message_action = process_queue_shared_ptr->getConsumer().lock()->receiveMessageAction(); + switch (result.status()) { case ReceiveMessageStatus::OK: SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", @@ -30,13 +32,13 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { process_queue_shared_ptr->cacheMessages(result.getMsgFoundList()); impl->getConsumeMessageService()->signalDispatcher(); - if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::PULL) { + if (ReceiveMessageAction::PULL == receive_message_action) { process_queue_shared_ptr->nextOffset(result.next_offset_); } checkThrottleThenReceive(); break; case ReceiveMessageStatus::DATA_CORRUPTED: - if (process_queue_shared_ptr->consumeType() == ConsumeMessageType::POP) { + if (ReceiveMessageAction::POLLING == receive_message_action) { process_queue_shared_ptr->cacheMessages(result.messages_); impl->getConsumeMessageService()->signalDispatcher(); } @@ -44,7 +46,7 @@ void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { break; case ReceiveMessageStatus::OUT_OF_RANGE: - assert(ConsumeMessageType::PULL == process_queue_shared_ptr->consumeType()); + assert(ReceiveMessageAction::PULL == receive_message_action); process_queue_shared_ptr->nextOffset(result.next_offset_); checkThrottleThenReceive(); break; @@ -138,8 +140,7 @@ void AsyncReceiveMessageCallback::receiveMessageImmediately() { process_queue_shared_ptr->simpleName()); return; } - impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression(), - process_queue_shared_ptr->consumeType()); + impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression()); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 1645c2ce4..d91250aed 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -83,6 +83,8 @@ void ClientImpl::shutdown() { client_manager_->getScheduler().cancel(route_update_handle_); } + notifyClientTermination(); + client_manager_.reset(); } @@ -272,7 +274,7 @@ void ClientImpl::fetchRouteFor(const std::string& topic, const std::functionset_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(topic); auto endpoints = request.mutable_endpoints(); setAccessPoint(endpoints); @@ -319,10 +321,6 @@ void ClientImpl::heartbeat() { HeartbeatRequest request; prepareHeartbeatData(request); - if (request.heartbeats().empty()) { - SPDLOG_INFO("No heartbeat entries to send. Skip."); - return; - } absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -534,25 +532,42 @@ void ClientImpl::onHealthCheckResponse(const std::string& endpoint, const Invoca void ClientImpl::fillGenericPollingRequest(MultiplexingRequest& request) { auto&& resource_bundle = resourceBundle(); - auto protocol_bundle = request.mutable_polling_request()->mutable_client_resource_bundle(); - protocol_bundle->set_client_id(resource_bundle.client_id); - for (auto& item : resource_bundle.topics) { - auto topic = new rmq::Resource; - topic->set_arn(resource_bundle.arn); - topic->set_name(item); - protocol_bundle->mutable_topics()->AddAllocated(topic); - } - + auto polling_request = request.mutable_polling_request(); + polling_request->set_client_id(clientId()); switch (resource_bundle.group_type) { case GroupType::PUBLISHER: - protocol_bundle->mutable_producer_group()->set_arn(resource_bundle.arn); - protocol_bundle->mutable_producer_group()->set_name(resource_bundle.group); + polling_request->mutable_producer_group()->set_resource_namespace(resource_namespace_); + polling_request->mutable_producer_group()->set_name(group_name_); break; + case GroupType::SUBSCRIBER: - protocol_bundle->mutable_consumer_group()->set_arn(resource_bundle.arn); - protocol_bundle->mutable_consumer_group()->set_name(resource_bundle.group); + polling_request->mutable_consumer_group()->set_resource_namespace(resource_namespace_); + polling_request->mutable_consumer_group()->set_name(group_name_); break; } + auto topics = polling_request->mutable_topics(); + for (const auto& item : resource_bundle.topics) { + auto topic = new rmq::Resource(); + topic->set_resource_namespace(resource_namespace_); + topic->set_name(item); + topics->AddAllocated(topic); + } +} + +void ClientImpl::notifyClientTermination() { + absl::flat_hash_set endpoints; + endpointsInUse(endpoints); + + Metadata metadata; + Signature::sign(this, metadata); + NotifyClientTerminationRequest request; + request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_name(group_name_); + request.set_client_id(clientId()); + + for (const auto& endpoint : endpoints) { + client_manager_->notifyClientTermination(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); + } } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index c6c17e27b..d96b9866e 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -127,7 +127,7 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); @@ -161,7 +161,7 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index e70e8a3ce..cdae0c06e 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -97,7 +97,9 @@ uint32_t DefaultMQProducer::compressBodyThreshold() const { return impl_->compre void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { impl_->compressBodyThreshold(threshold); } -void DefaultMQProducer::arn(const std::string& arn) { impl_->arn(arn); } +void DefaultMQProducer::setResourceNamespace(const std::string& resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { impl_->setCredentialsProvider(std::move(credentials_provider)); diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index 23add520c..041066244 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -28,7 +28,7 @@ void DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullCallback* ca impl_->pull(query, callback); } -void DefaultMQPullConsumer::setArn(const std::string& arn) { impl_->arn(arn); } +void DefaultMQPullConsumer::setResourceNamespace(const std::string& resource_namespace) { impl_->resourceNamespace(resource_namespace); } void DefaultMQPullConsumer::setCredentialsProvider(std::shared_ptr credentials_provider) { impl_->setCredentialsProvider(std::move(credentials_provider)); diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index a7b4223fa..c6f7daa05 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -67,7 +67,7 @@ void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t thres impl_->setThrottle(topic, threshold); } -void DefaultMQPushConsumer::setArn(const char* arn) { impl_->arn(arn); } +void DefaultMQPushConsumer::setResourceNamespace(const char* resource_namespace) { impl_->resourceNamespace(resource_namespace); } void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { impl_->setCredentialsProvider(std::move(credentials_provider)); diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index dbce90cf4..900af1f81 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -14,10 +14,9 @@ using namespace std::chrono; ROCKETMQ_NAMESPACE_BEGIN ProcessQueueImpl::ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, - ConsumeMessageType consume_type, std::weak_ptr consumer, - std::shared_ptr client_instance) + std::weak_ptr consumer, std::shared_ptr client_instance) : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), - consume_type_(consume_type), invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), + invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), simple_name_(message_queue_.simpleName()), consumer_(std::move(consumer)), client_manager_(std::move(client_instance)), cached_message_quantity_(0), cached_message_memory_(0) { SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); @@ -69,11 +68,17 @@ bool ProcessQueueImpl::shouldThrottle() const { } void ProcessQueueImpl::receiveMessage() { - switch (consume_type_) { - case ConsumeMessageType::POP: + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + auto policy = consumer->receiveMessageAction(); + switch (policy) { + case ReceiveMessageAction::POLLING: popMessage(); break; - case ConsumeMessageType::PULL: + case ReceiveMessageAction::PULL: pullMessage(); break; } @@ -262,10 +267,10 @@ void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_mapclientId()); request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_arn(consumer->arn()); + request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); request.mutable_partition()->set_id(message_queue_.getQueueId()); request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); wrapFilterExpression(request.mutable_filter_expression()); @@ -289,10 +294,10 @@ void ProcessQueueImpl::wrapPullMessageRequest(absl::flat_hash_mapclientId()); request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_arn(consumer->arn()); + request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); request.mutable_partition()->set_id(message_queue_.getQueueId()); request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(consumer->arn()); + request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); request.set_offset(next_offset_); request.set_batch_size(consumer->receiveBatchSize()); diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 81d4c66f0..fe6bd3dc8 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -63,7 +63,7 @@ void ProducerImpl::validate(const MQMessage& message) {} std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, const MQMessageQueue& message_queue) { - request.mutable_message()->mutable_topic()->set_arn(arn_); + request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_message()->mutable_topic()->set_name(message.getTopic()); auto system_attribute = request.mutable_message()->mutable_system_attribute(); @@ -76,7 +76,7 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM system_attribute->set_born_host(UtilAll::hostname()); - system_attribute->mutable_producer_group()->set_arn(arn_); + system_attribute->mutable_producer_group()->set_resource_namespace(resource_namespace_); system_attribute->mutable_producer_group()->set_name(group_name_); // Set system flag if the message is transactional @@ -298,7 +298,7 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, message.getTopic()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message.getMsgId()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); @@ -375,7 +375,7 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, arn()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message_id); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); @@ -606,10 +606,9 @@ std::vector ProducerImpl::getTopicMessageQueueInfo(const std::st } void ProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - rmq::HeartbeatEntry entry; - entry.mutable_producer_group()->mutable_group()->set_arn(arn_); - entry.mutable_producer_group()->mutable_group()->set_name(group_name_); - request.mutable_heartbeats()->Add(std::move(entry)); + request.set_client_id(clientId()); + request.mutable_producer_data()->mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_data()->mutable_group()->set_name(group_name_); } void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index 11f526451..df4514677 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -2,6 +2,8 @@ #include "ClientManagerFactory.h" #include "InvocationContext.h" #include "Signature.h" +#include "rocketmq/MessageModel.h" +#include "apache/rocketmq/v1/definition.pb.h" ROCKETMQ_NAMESPACE_BEGIN @@ -73,7 +75,7 @@ std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { } request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(arn_); + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_partition()->set_id(query.message_queue.getQueueId()); @@ -93,7 +95,7 @@ std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { }; client_manager_->queryOffset(query.message_queue.serviceAddress(), metadata, request, - absl::ToChronoMilliseconds(io_timeout_), callback); + absl::ToChronoMilliseconds(io_timeout_), callback); return promise_ptr->get_future(); } @@ -105,10 +107,10 @@ void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { request.mutable_await_time()->set_seconds(seconds); request.mutable_await_time()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); request.mutable_group()->set_name(group_name_); - request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_resource_namespace(resource_namespace_); request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); - request.mutable_partition()->mutable_topic()->set_arn(arn_); + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_partition()->set_id(query.message_queue.getQueueId()); request.set_client_id(clientId()); @@ -144,14 +146,23 @@ void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_manager_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), callback); + client_manager_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + callback); } void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - rmq::HeartbeatEntry entry; - entry.mutable_producer_group()->mutable_group()->set_arn(arn_); - entry.mutable_producer_group()->mutable_group()->set_name(group_name_); - request.mutable_heartbeats()->Add(std::move(entry)); + request.set_client_id(clientId()); + auto consumer_data = request.mutable_consumer_data(); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + consumer_data->mutable_group()->set_name(group_name_); + switch (message_model_) { + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; + } } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 44347350c..09fdb35d9 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -8,8 +8,11 @@ #include "RpcClient.h" #include "Signature.h" #include "rocketmq/MQClientException.h" +#include "rocketmq/MessageModel.h" +#include #include #include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -25,6 +28,19 @@ void PushConsumerImpl::start() { return; } + if (!message_listener_) { + SPDLOG_ERROR("Message listener is nullptr"); + abort(); + return; + } + + if (MessageListenerType::STANDARD == message_listener_->listenerType() && + MessageModel::CLUSTERING == message_model_) { + receive_message_policy_ = ReceiveMessageAction::POLLING; + } else { + receive_message_policy_ = ReceiveMessageAction::PULL; + } + client_manager_->addClientObserver(shared_from_this()); fetchRoutes(); @@ -171,9 +187,9 @@ void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, cons const std::string& client_id, const std::string& strategy_name, QueryAssignmentRequest& request) { request.mutable_topic()->set_name(topic); - request.mutable_topic()->set_arn(arn()); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); request.mutable_group()->set_name(consumer_group); - request.mutable_group()->set_arn(arn()); + request.mutable_group()->set_resource_namespace(resourceNamespace()); request.set_client_id(client_id); } @@ -191,7 +207,7 @@ void PushConsumerImpl::queryAssignment(const std::string& topic, std::vector assignments; assignments.reserve(topic_route->partitions().size()); for (const auto& partition : topic_route->partitions()) { - assignments.emplace_back(Assignment(partition.asMessageQueue(), ConsumeMessageType::PULL)); + assignments.emplace_back(Assignment(partition.asMessageQueue())); } topic_assignment = std::make_shared(std::move(assignments)); cb(topic_assignment); @@ -277,10 +293,7 @@ void PushConsumerImpl::syncProcessQueue(const std::string& topic, [&](const MQMessageQueue& item) { return item == message_queue; })) { SPDLOG_INFO("Start to receive message from {} according to latest assignment info from load balancer", message_queue.simpleName()); - // Clustering message model is implemented through POP while broadcasting is implemented through classic PULL. - ConsumeMessageType consume_type = - MessageModel::CLUSTERING == message_model_ ? ConsumeMessageType::POP : ConsumeMessageType::PULL; - if (!receiveMessage(message_queue, filter_expression, consume_type)) { + if (!receiveMessage(message_queue, filter_expression)) { if (!active()) { SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", message_queue.simpleName()); // TODO: remove it from current assignment such that a second attempt will be made again in the next round. @@ -291,8 +304,7 @@ void PushConsumerImpl::syncProcessQueue(const std::string& topic, } ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression, - ConsumeMessageType consume_type) { + const FilterExpression& filter_expression) { ProcessQueueSharedPtr process_queue; { absl::MutexLock lock(&process_queue_table_mtx_); @@ -306,8 +318,8 @@ ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQ } else { SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); // create ProcessQueue - process_queue = std::make_shared(message_queue, filter_expression, consume_type, - shared_from_this(), client_manager_); + process_queue = + std::make_shared(message_queue, filter_expression, shared_from_this(), client_manager_); std::shared_ptr receive_callback = std::make_shared(process_queue); process_queue->callback(receive_callback); @@ -317,14 +329,13 @@ ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQ return process_queue; } -bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, - ConsumeMessageType consume_type) { +bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) { if (!active()) { SPDLOG_INFO("PushConsumer has stopped. Drop further receive message request"); return false; } - ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression, consume_type); + ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression); if (!process_queue_ptr) { SPDLOG_INFO("Consumer has stopped. Stop creating processQueue"); return false; @@ -336,13 +347,13 @@ bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const return false; } - switch (consume_type) { - case ConsumeMessageType::PULL: { + switch (receive_message_policy_) { + case ReceiveMessageAction::PULL: { int64_t offset = -1; if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { // Query latest offset from server. QueryOffsetRequest request; - request.mutable_partition()->mutable_topic()->set_arn(arn_); + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); request.mutable_partition()->set_id(message_queue.getQueueId()); request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); @@ -363,7 +374,7 @@ bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const } break; } - case ConsumeMessageType::POP: + case ReceiveMessageAction::POLLING: process_queue_ptr->receiveMessage(); break; } @@ -393,10 +404,10 @@ void PushConsumerImpl::nack(const MQMessageExt& msg, const std::functionset_arn(arn_); + request.mutable_group()->set_resource_namespace(resource_namespace_); request.mutable_group()->set_name(group_name_); // Topic - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(msg.getTopic()); request.set_client_id(clientId()); request.set_receipt_handle(msg.receiptHandle()); @@ -415,10 +426,10 @@ void PushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, con Signature::sign(this, metadata); ForwardMessageToDeadLetterQueueRequest request; - request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_resource_namespace(resource_namespace_); request.mutable_group()->set_name(group_name_); - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(message.getTopic()); request.set_client_id(clientId()); @@ -432,9 +443,9 @@ void PushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, con } void PushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { - request.mutable_group()->set_arn(arn_); + request.mutable_group()->set_resource_namespace(resource_namespace_); request.mutable_group()->set_name(group_name_); - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(msg.getTopic()); request.set_client_id(clientId()); request.set_message_id(msg.getMsgId()); @@ -537,18 +548,41 @@ void PushConsumerImpl::fetchRoutes() { } void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - auto heartbeat = new rmq::HeartbeatEntry(); - heartbeat->set_client_id(clientId()); - auto consumer_group = heartbeat->mutable_consumer_group(); - consumer_group->mutable_group()->set_arn(arn()); - consumer_group->mutable_group()->set_name(group_name_); - consumer_group->set_consume_model(rmq::ConsumeModel::CLUSTERING); + request.set_client_id(clientId()); + + if (message_listener_) { + switch (message_listener_->listenerType()) { + case MessageListenerType::FIFO: + request.set_fifo_flag(true); + break; + case MessageListenerType::STANDARD: + request.set_fifo_flag(false); + break; + } + } + + auto consumer_data = request.mutable_consumer_data(); + consumer_data->mutable_group()->set_name(group_name_); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + + switch (message_model_) { + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; + default: + break; + } + + auto subscriptions = consumer_data->mutable_subscriptions(); { absl::MutexLock lk(&topic_filter_expression_table_mtx_); for (const auto& entry : topic_filter_expression_table_) { auto subscription = new rmq::SubscriptionEntry; - subscription->mutable_topic()->set_arn(arn_); + subscription->mutable_topic()->set_resource_namespace(resource_namespace_); subscription->mutable_topic()->set_name(entry.first); subscription->mutable_expression()->set_expression(entry.second.content_); switch (entry.second.type_) { @@ -559,24 +593,21 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { subscription->mutable_expression()->set_type(rmq::FilterType::SQL); break; } - consumer_group->mutable_subscriptions()->AddAllocated(subscription); + subscriptions->AddAllocated(subscription); } } - - // Add heartbeat - request.mutable_heartbeats()->AddAllocated(heartbeat); } ClientResourceBundle PushConsumerImpl::resourceBundle() { - ClientResourceBundle resource_bundle = ClientImpl::resourceBundle(); + auto&& resource_bundle = ClientImpl::resourceBundle(); + resource_bundle.group_type = GroupType::SUBSCRIBER; { absl::MutexLock lk(&topic_filter_expression_table_mtx_); for (auto& item : topic_filter_expression_table_) { resource_bundle.topics.emplace_back(item.first); } } - - return resource_bundle; + return std::move(resource_bundle); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index b709ad2c0..4a2dc6e9c 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -4,9 +4,9 @@ #include "ClientResourceBundle.h" #include "InvocationContext.h" #include "OtlpExporter.h" +#include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" -#include #include ROCKETMQ_NAMESPACE_BEGIN @@ -100,13 +100,14 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { virtual ClientResourceBundle resourceBundle() { ClientResourceBundle resource_bundle; resource_bundle.client_id = clientId(); - resource_bundle.arn = arn_; + resource_bundle.resource_namespace = resource_namespace_; return resource_bundle; } -protected: void setAccessPoint(rmq::Endpoints* endpoints) LOCKS_EXCLUDED(name_server_list_mtx_); + virtual void notifyClientTermination(); + private: /** * This is a low-level API that fetches route data from name server through gRPC unary request/response. Once diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h index f7bac1df9..a4be5d214 100644 --- a/src/main/cpp/rocketmq/include/ClientResourceBundle.h +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -13,7 +13,7 @@ enum class GroupType : int8_t { struct ClientResourceBundle { std::string client_id; std::vector topics; - std::string arn; + std::string resource_namespace; std::string group; GroupType group_type{GroupType::PUBLISHER}; }; diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index 0b86312eb..860a34248 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -2,6 +2,7 @@ #include "Client.h" #include "FilterExpression.h" +#include "ReceiveMessageAction.h" #include "absl/container/flat_hash_map.h" #include "absl/types/optional.h" @@ -11,13 +12,16 @@ class Consumer : virtual public Client { public: ~Consumer() override = default; - virtual absl::optional getFilterExpression(const std::string& topic) const = 0; - + virtual absl::optional + getFilterExpression(const std::string &topic) const = 0; + virtual uint32_t maxCachedMessageQuantity() const = 0; virtual uint64_t maxCachedMessageMemory() const = 0; virtual int32_t receiveBatchSize() const = 0; + + virtual ReceiveMessageAction receiveMessageAction() const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index f5ab21fea..61ee74d5b 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -40,8 +40,6 @@ class ProcessQueue { virtual void release(uint64_t body_size, int64_t offset) = 0; - virtual ConsumeMessageType consumeType() const = 0; - virtual void cacheMessages(const std::vector& messages) = 0; virtual bool shouldThrottle() const = 0; diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index ff6fac962..a598a0bf8 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -55,7 +55,7 @@ class PushConsumer; */ class ProcessQueueImpl : virtual public ProcessQueue { public: - ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, ConsumeMessageType consume_type, + ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, std::weak_ptr consumer, std::shared_ptr client_instance); ~ProcessQueueImpl() override; @@ -107,15 +107,6 @@ class ProcessQueueImpl : virtual public ProcessQueue { void syncIdleState() override { idle_since_ = std::chrono::steady_clock::now(); } - ConsumeMessageType consumeType() const override { return consume_type_; } - - /** - * @brief Expose for test purpose only. - * - * @param consume_type - */ - void consumeType(ConsumeMessageType consume_type) { consume_type_ = consume_type; } - void nextOffset(int64_t next_offset) override { assert(next_offset >= 0); next_offset_ = next_offset; @@ -145,8 +136,6 @@ class ProcessQueueImpl : virtual public ProcessQueue { */ const FilterExpression filter_expression_; - ConsumeMessageType consume_type_{ConsumeMessageType::POP}; - std::chrono::milliseconds invisible_time_; std::chrono::steady_clock::time_point idle_since_{std::chrono::steady_clock::now()}; diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index 6eb5c6c37..2b8cc533c 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -8,6 +8,7 @@ #include "ClientManagerImpl.h" #include "rocketmq/ConsumeType.h" #include "rocketmq/MQMessageQueue.h" +#include "rocketmq/MessageModel.h" ROCKETMQ_NAMESPACE_BEGIN @@ -29,6 +30,8 @@ class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this< protected: std::shared_ptr self() override { return shared_from_this(); } + + MessageModel message_model_{MessageModel::CLUSTERING}; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index 848bd448a..183c0bd06 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -4,6 +4,7 @@ #include "Consumer.h" #include "ProcessQueue.h" #include "rocketmq/Executor.h" +#include "rocketmq/MessageListener.h" #include "rocketmq/MessageModel.h" #include #include @@ -34,8 +35,9 @@ class PushConsumer : virtual public Consumer { virtual std::shared_ptr getConsumeMessageService() = 0; - virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, - ConsumeMessageType consume_type) = 0; + virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) = 0; + + virtual MessageListener* messageListener() = 0; }; using PushConsumerSharedPtr = std::shared_ptr; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 6190c354d..64d2f5f73 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -76,12 +76,11 @@ class PushConsumerImpl : virtual public ClientImpl, const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); ProcessQueueSharedPtr getOrCreateProcessQueue(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression, - ConsumeMessageType consume_type) + const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); - bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression, - ConsumeMessageType consume_type) override LOCKS_EXCLUDED(process_queue_table_mtx_); + bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) override + LOCKS_EXCLUDED(process_queue_table_mtx_); uint32_t consumeThreadPoolSize() const; @@ -155,6 +154,10 @@ class PushConsumerImpl : virtual public ClientImpl, void iterateProcessQueue(const std::function& callback) override; + MessageListener* messageListener() override { return message_listener_; } + + ReceiveMessageAction receiveMessageAction() const override { return receive_message_policy_; } + protected: std::shared_ptr self() override { return shared_from_this(); } @@ -194,6 +197,8 @@ class PushConsumerImpl : virtual public ClientImpl, MessageModel message_model_{MessageModel::CLUSTERING}; + ReceiveMessageAction receive_message_policy_{ReceiveMessageAction::POLLING}; + mutable std::unique_ptr offset_store_; void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); diff --git a/src/main/cpp/rocketmq/include/ReceiveMessageAction.h b/src/main/cpp/rocketmq/include/ReceiveMessageAction.h new file mode 100644 index 000000000..7805e61a0 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ReceiveMessageAction.h @@ -0,0 +1,25 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" + +#include + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ReceiveMessageAction : std::int8_t { + /** + * @brief Use Receive and Ack strategy. Progress of message consumption is + * managed by the server. + */ + POLLING = 0, + + /** + * @brief A consumer client dynamically binds one or multiple message queues + * through user-defined-allocation algorithm. The client then range-scans the + * specified message queue and selectively ack through persisting offset to + * brokers. + */ + PULL = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h index da9f4e700..df2435805 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -14,6 +14,8 @@ class ConsumerMock : virtual public Consumer, virtual public ClientMock { MOCK_METHOD(uint64_t, maxCachedMessageMemory, (), (const override)); MOCK_METHOD(int32_t, receiveBatchSize, (), (const override)); + + MOCK_METHOD(ReceiveMessageAction, receiveMessageAction, (), (const override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h index ae7ff1722..b56b2a93a 100644 --- a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h @@ -27,8 +27,6 @@ class ProcessQueueMock : public ProcessQueue { MOCK_METHOD(void, release, (uint64_t, int64_t), (override)); - MOCK_METHOD(ConsumeMessageType, consumeType, (), (const override)); - MOCK_METHOD(void, cacheMessages, (const std::vector&), (override)); MOCK_METHOD(bool, shouldThrottle, (), (const override)); diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 7c82f650d..feb93677b 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -27,7 +27,9 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); - MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&, ConsumeMessageType), (override)); + MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&), (override)); + + MOCK_METHOD(MessageListener*, messageListener, (), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/CMakeLists.txt b/src/test/cpp/it/CMakeLists.txt index cd534ac23..9d48d3a9c 100644 --- a/src/test/cpp/it/CMakeLists.txt +++ b/src/test/cpp/it/CMakeLists.txt @@ -5,14 +5,6 @@ target_link_libraries(client_instance_test gtest_main) add_test(it_client_instance client_instance_test) -add_executable(rpc_client_integration_test RpcClientIntegrationTest.cpp) -target_link_libraries(rpc_client_integration_test - PRIVATE - client_library - rpc_client_mock - gtest_main) -add_test(it_rpc_client rpc_client_integration_test) - add_executable(rpc_client_test RpcClientTest.cpp) target_link_libraries(rpc_client_test PRIVATE diff --git a/src/test/cpp/it/ClientInstanceTest.cpp b/src/test/cpp/it/ClientInstanceTest.cpp deleted file mode 100644 index 710e6acb6..000000000 --- a/src/test/cpp/it/ClientInstanceTest.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "ClientManagerFactory.h" -#include "ClientManagerImpl.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ClientInstanceTest : public testing::Test { -protected: - std::string name_server_{"grpc.dev:9876"}; -}; - -ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp b/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp deleted file mode 100644 index 84ef7b0c5..000000000 --- a/src/test/cpp/it/DefaultMQPushConsumerIntegrationTest.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "rocketmq/DefaultMQPushConsumer.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" - -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN -class SampleMQMessageListener : public StandardMessageListener { -public: - ConsumeStatus consumeMessage(const std::vector& msgs) override { - for (const MQMessageExt& msg : msgs) { - std::cout << "Topic=" << msg.getTopic() << ", MsgId=" << msg.getMsgId() << ", Body=" << msg.getBody() - << std::endl; - } - return ConsumeStatus::CONSUME_SUCCESS; - } -}; - -class DefaultMQPushConsumerTest : public testing::Test { -public: - DefaultMQPushConsumerTest() : push_consumer_("CID_sample"), listener_(new SampleMQMessageListener) {} - - void SetUp() override { - push_consumer_.setGroupName("CID_sample"); - push_consumer_.setInstanceName("CID_sample_member_0"); - push_consumer_.subscribe("TopicTest", "*"); - push_consumer_.setNamesrvAddr("localhost:9886"); - push_consumer_.registerMessageListener(listener_); - } - - void TearDown() override { push_consumer_.shutdown(); } - - ~DefaultMQPushConsumerTest() override { delete listener_; } - -protected: - DefaultMQPushConsumer push_consumer_; - MessageListener* listener_; -}; - -TEST_F(DefaultMQPushConsumerTest, testStartupAndShutdown) { - push_consumer_.setConsumeThreadCount(1); - push_consumer_.start(); - std::this_thread::sleep_for(std::chrono::minutes(10)); -} -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientIntegrationTest.cpp b/src/test/cpp/it/RpcClientIntegrationTest.cpp deleted file mode 100644 index e2df326b2..000000000 --- a/src/test/cpp/it/RpcClientIntegrationTest.cpp +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include -#include - -#include "ClientConfigImpl.h" -#include "MixAll.h" -#include "Protocol.h" -#include "RpcClientImpl.h" -#include "absl/strings/string_view.h" -#include "gtest/gtest.h" - -using namespace testing; - -ROCKETMQ_NAMESPACE_BEGIN - -class RpcClientIntegrationTest : public testing::Test { -protected: - RpcClientIntegrationTest() - : topic_name("TestTopic"), group_name("CID_sample"), client_id("client_id_01"), tag_name("TagA"), - completion_queue_(std::make_shared()), - rpcClient(completion_queue_, grpc::CreateChannel(target, grpc::InsecureChannelCredentials())) {} - - void SetUp() override { - { - heartbeat_request.mutable_heartbeats()->Clear(); - - auto heartbeat_entry = new rmq::HeartbeatEntry; - heartbeat_entry->set_client_id(client_id); - heartbeat_entry->mutable_consumer_group()->mutable_group()->set_name(group_name); - auto subscription_entry = new rmq::SubscriptionEntry; - subscription_entry->mutable_topic()->set_name(topic_name); - subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); - subscription_entry->mutable_expression()->set_expression(tag_name); - heartbeat_entry->mutable_consumer_group()->mutable_subscriptions()->AddAllocated(subscription_entry); - } - - { - query_assignment_request.mutable_topic()->set_name(topic_name); - query_assignment_request.set_client_id(client_id); - query_assignment_request.mutable_group()->set_name(group_name); - } - - { - receive_message_request.mutable_group()->set_name(group_name); - receive_message_request.set_client_id(client_id); - receive_message_request.mutable_partition()->set_id(0); - receive_message_request.mutable_partition()->mutable_topic()->set_name(topic_name); - receive_message_request.mutable_partition()->mutable_broker()->set_name("broker-a"); - auto endpoint = receive_message_request.mutable_partition()->mutable_broker()->mutable_endpoints(); - endpoint->set_scheme(rmq::AddressScheme::IPv4); - auto address = new rmq::Address; - address->set_host("127.0.0.1"); - address->set_port(10911); - endpoint->mutable_addresses()->AddAllocated(address); - receive_message_request.set_batch_size(32); - receive_message_request.mutable_filter_expression()->set_type(rmq::FilterType::TAG); - receive_message_request.mutable_filter_expression()->set_expression(tag_name); - receive_message_request.set_consume_policy(rmq::ConsumePolicy::RESUME); - receive_message_request.mutable_invisible_duration()->set_seconds(30); - receive_message_request.mutable_await_time()->set_seconds(10); - } - } - - std::shared_ptr completion_queue_; - std::string target{"127.0.0.1:10921"}; - std::string topic_name; - std::string group_name; - std::string client_id; - std::string tag_name; - RpcClientImpl rpcClient; - rmq::HeartbeatRequest heartbeat_request; - rmq::QueryAssignmentRequest query_assignment_request; - rmq::ReceiveMessageRequest receive_message_request; -}; - -TEST_F(RpcClientIntegrationTest, testQueryAssignment_without_heartbeat) { - QueryAssignmentResponse response; - auto status = rpcClient.queryAssignment(query_assignment_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(response.common().status().code(), google::rpc::Code::OK); - EXPECT_EQ(8, response.assignments().size()); - for (auto& assignment : response.assignments()) { - auto& partition = assignment.partition(); - std::cout << partition.topic().name() << " --> " << partition.broker().name() << " --> " << partition.id() - << std::endl; - } -} - -TEST_F(RpcClientIntegrationTest, testQueryAssignment_with_prior_heartbeat) { - - { - HeartbeatResponse response; - grpc::ClientContext client_context; - auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - } - - QueryAssignmentResponse response; - - auto status = rpcClient.queryAssignment(query_assignment_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - EXPECT_FALSE(response.assignments().empty()); -} - -TEST_F(RpcClientIntegrationTest, testQueryAssignment_with_mutiple_clients) { - std::string consumer_group("CID_sample"); - std::string client_id_0("CID_sample_member_0"); - std::string client_id_1("CID_sample_member_1"); - std::string topic("TopicTest"); - - { - // Send heartbeat to broker - HeartbeatResponse response; - grpc::ClientContext client_context; - auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - } - - { - HeartbeatResponse response; - grpc::ClientContext client_context; - auto status = rpcClient.heartbeat(client_context, heartbeat_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - } - - { - QueryAssignmentResponse response; - - auto status = rpcClient.queryAssignment(query_assignment_request, &response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(google::rpc::Code::OK, response.common().status().code()); - EXPECT_FALSE(response.assignments().empty()); - } -} - -TEST_F(RpcClientIntegrationTest, testPop) { - - auto invocation_context = new InvocationContext; - - auto callback = [](const grpc::Status& status, const grpc::ClientContext& client_context, - const ReceiveMessageResponse& response) { - for (int i = 0; i < response.messages_size(); ++i) { - auto msg = response.messages().at(i); - std::cout << msg.system_attribute().message_id() << std::endl; - const std::string& receipt_handle = msg.system_attribute().receipt_handle(); - EXPECT_FALSE(receipt_handle.empty()); - } - }; - - invocation_context->callback = callback; - auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); - invocation_context->context.set_deadline(deadline); - - rpcClient.asyncReceive(receive_message_request, invocation_context); - void* tag; - bool ok; - bool stop = false; - while (!stop) { - const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); - grpc::CompletionQueue::NextStatus status = rpcClient.completionQueue()->AsyncNext(&tag, &ok, deadline); - EXPECT_FALSE(grpc::CompletionQueue::SHUTDOWN == status); - switch (status) { - case grpc::CompletionQueue::SHUTDOWN: - stop = true; - break; - case grpc::CompletionQueue::GOT_EVENT: { - stop = true; - auto invocation_ctx = reinterpret_cast*>(tag); - EXPECT_TRUE(nullptr != invocation_ctx); - invocation_ctx->onCompletion(ok); - break; - } - case grpc::CompletionQueue::TIMEOUT: - break; - } - } -} - -TEST_F(RpcClientIntegrationTest, testPopAndAck) { - auto invocation_context = new InvocationContext; - - auto callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, - const ReceiveMessageResponse& response) { - for (int i = 0; i < response.messages_size(); ++i) { - auto msg = response.messages().at(i); - const std::string& message_id = msg.system_attribute().message_id(); - const std::string& receipt_handle = msg.system_attribute().receipt_handle(); - - EXPECT_FALSE(message_id.empty()); - EXPECT_FALSE(receipt_handle.empty()); - - AckMessageRequest ack_request; - ack_request.set_client_id(client_id); - ack_request.mutable_group()->set_name(group_name); - ack_request.mutable_topic()->set_name(topic_name); - ack_request.set_message_id(message_id); - ack_request.set_receipt_handle(receipt_handle); - InvocationContext invocation_context; - - std::mutex ack_mtx; - std::condition_variable ack_cv; - grpc::Status ack_rpc_status = grpc::Status::CANCELLED; - google::rpc::Status ack_status; - std::atomic_bool ack_done(false); - - invocation_context.callback = [&](const grpc::Status& status, const grpc::ClientContext& client_context, - const AckMessageResponse& response) { - ack_rpc_status = status; - ack_status = response.common().status(); - ack_done.store(true); - { - std::unique_lock lk(ack_mtx); - ack_cv.notify_all(); - } - }; - rpcClient.asyncAck(ack_request, &invocation_context); - - // Wait for Ack completion. - { - std::unique_lock lk(ack_mtx); - ack_cv.wait(lk, [&]() { return ack_done.load(); }); - } - - // Verify gRPC is OK - EXPECT_TRUE(ack_rpc_status.ok()); - EXPECT_EQ(google::rpc::Code::OK, ack_status.code()); - } - }; - - invocation_context->callback = callback; - auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(10); - invocation_context->context.set_deadline(deadline); - - rpcClient.asyncReceive(receive_message_request, invocation_context); - void* tag; - bool ok; - bool stop = false; - while (!stop) { - const auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10); - grpc::CompletionQueue::NextStatus status = rpcClient.completionQueue()->AsyncNext(&tag, &ok, deadline); - EXPECT_FALSE(grpc::CompletionQueue::SHUTDOWN == status); - switch (status) { - case grpc::CompletionQueue::SHUTDOWN: - stop = true; - break; - case grpc::CompletionQueue::GOT_EVENT: { - stop = true; - auto invocation_ctx = reinterpret_cast*>(tag); - EXPECT_TRUE(nullptr != invocation_ctx); - invocation_ctx->onCompletion(ok); - break; - } - case grpc::CompletionQueue::TIMEOUT: - break; - } - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index aa2f90c07..fc9729a21 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -5,17 +5,17 @@ #include "RpcClientImpl.h" #include "Signature.h" #include "TlsHelper.h" -#include "UtilAll.h" #include "UniqueIdGenerator.h" -#include "rocketmq/Logger.h" +#include "UtilAll.h" #include "absl/container/flat_hash_set.h" #include "apache/rocketmq/v1/service.pb.h" #include "rocketmq/CredentialsProvider.h" +#include "rocketmq/Logger.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" -#include #include #include +#include using namespace testing; @@ -47,7 +47,7 @@ class RpcClientTest : public ::testing::Test { void SetUp() override { getLogger().setLevel(Level::Debug); client_config_.setCredentialsProvider(std::make_shared()); - client_config_.arn(arn_); + client_config_.resourceNamespace(resource_namespace_); client_config_.region(region_id_); client_config_.tenantId(tenant_id_); @@ -117,7 +117,7 @@ class RpcClientTest : public ::testing::Test { } void fillSendMessageRequest(SendMessageRequest& send_message_request) { - send_message_request.mutable_message()->mutable_topic()->set_arn(arn_); + send_message_request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); send_message_request.mutable_message()->mutable_topic()->set_name(topic_); std::unordered_map props; props["key"] = "value"; @@ -149,25 +149,26 @@ class RpcClientTest : public ::testing::Test { } void fillHeartbeatRequest(HeartbeatRequest& heartbeat_request) { - auto heartbeat_entry = new rmq::HeartbeatEntry; - heartbeat_entry->set_client_id("client_id_0"); - heartbeat_entry->mutable_consumer_group()->mutable_group()->set_name(group_); - heartbeat_entry->mutable_consumer_group()->mutable_group()->set_arn(arn_); + heartbeat_request.set_client_id("client_id_0"); + auto consumer_data = heartbeat_request.mutable_consumer_data(); + consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); + consumer_data->mutable_group()->set_name(topic_); + auto subscription_entry = new rmq::SubscriptionEntry; subscription_entry->mutable_topic()->set_name(topic_); - subscription_entry->mutable_topic()->set_arn(arn_); + subscription_entry->mutable_topic()->set_resource_namespace(resource_namespace_); subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); subscription_entry->mutable_expression()->set_expression("*"); - heartbeat_entry->mutable_consumer_group()->mutable_subscriptions()->AddAllocated(subscription_entry); - heartbeat_request.mutable_heartbeats()->AddAllocated(heartbeat_entry); + consumer_data->mutable_subscriptions()->AddAllocated(subscription_entry); + heartbeat_request.set_fifo_flag(false); } - std::string name_server_target_{"11.165.223.199:9876"}; + std::string name_server_target_{"47.98.116.189:80"}; std::shared_ptr completion_queue_; std::shared_ptr name_server_channel_; - std::string topic_{"yc001"}; - std::string group_{"GID_group003"}; - std::string arn_{"MQ_INST_1973281269661160_BXmPlOA6"}; + std::string topic_{"cpp_sdk_standard"}; + std::string group_{"GID_cpp_sdk_standard"}; + std::string resource_namespace_{"MQ_INST_1080056302921134_BXuIbML7"}; std::string tenant_id_{"sample-tenant"}; std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; @@ -186,21 +187,22 @@ TEST_F(RpcClientTest, testRouteInfo) { RpcClientImpl client(completion_queue_, name_server_channel_); QueryRouteRequest request; request.mutable_topic()->set_name(topic_); - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); auto invocation_context = new InvocationContext(); bool completed = false; absl::Mutex mtx; absl::CondVar cv; auto callback = [&](const InvocationContext* invocation_context) { if (!invocation_context->status.ok()) { - std::cout << "code: " << invocation_context->status.error_code() - << ", message: " << invocation_context->status.error_message() << std::endl; + SPDLOG_ERROR("Status not OK"); } ASSERT_TRUE(invocation_context->status.ok()); EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); EXPECT_FALSE(invocation_context->response.partitions().empty()); - absl::MutexLock lk(&mtx); - cv.SignalAll(); + { + absl::MutexLock lk(&mtx); + cv.SignalAll(); + } }; invocation_context->callback = callback; @@ -216,7 +218,7 @@ TEST_F(RpcClientTest, testRouteInfo) { } } -TEST_F(RpcClientTest, testSendMessageAsync) { +TEST_F(RpcClientTest, DISABLED_testSendMessageAsync) { SendMessageRequest request; fillSendMessageRequest(request); auto client = brokerRpcClient(); @@ -248,7 +250,7 @@ TEST_F(RpcClientTest, testSendMessageAsync) { } } -TEST_F(RpcClientTest, testHeartbeat) { +TEST_F(RpcClientTest, DISABLED_testHeartbeat) { auto client = brokerRpcClient(); HeartbeatRequest heartbeat_request; HeartbeatResponse response; @@ -280,12 +282,12 @@ TEST_F(RpcClientTest, testHeartbeat) { } } -TEST_F(RpcClientTest, testQueryAssignment) { +TEST_F(RpcClientTest, DISABLED_testQueryAssignment) { auto client = brokerRpcClient(); QueryAssignmentRequest request; request.mutable_topic()->set_name(topic_); - request.mutable_topic()->set_arn(arn_); - request.mutable_group()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_group()->set_resource_namespace(resource_namespace_); request.mutable_group()->set_name(group_); QueryAssignmentResponse response; @@ -315,7 +317,7 @@ TEST_F(RpcClientTest, testQueryAssignment) { } } -TEST_F(RpcClientTest, testHealthCheck) { +TEST_F(RpcClientTest, DISABLED_testHealthCheck) { auto client = brokerRpcClient(); HealthCheckRequest request; const std::string& client_host = UtilAll::hostname(); diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index 5c1bc0bfc..ce3257417 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -45,7 +45,7 @@ class TopicPublishInfoTest : public ::testing::Test { target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); client_ = std::make_shared(completion_queue_, channel); - client_config_.arn(arn_); + client_config_.resourceNamespace(resource_namespace_); client_config_.setCredentialsProvider(std::make_shared()); client_config_.setIoTimeout(absl::Seconds(3)); @@ -56,7 +56,7 @@ class TopicPublishInfoTest : public ::testing::Test { std::string topic_{"yc001"}; std::string group_{"yc001"}; - std::string arn_{"MQ_INST_1973281269661160_BXmPlOA6"}; + std::string resource_namespace_{"MQ_INST_1973281269661160_BXmPlOA6"}; std::string tenant_id_{"sample-tenant"}; std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; @@ -76,7 +76,7 @@ class TopicPublishInfoTest : public ::testing::Test { TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { rmq::QueryRouteResponse response; rmq::QueryRouteRequest request; - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(topic_); auto invocation_context = new InvocationContext(); invocation_context->context.set_deadline(std::chrono::system_clock::now() + @@ -96,7 +96,7 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { std::vector partitions; for (const auto& item : invocation_context->response.partitions()) { - Topic topic(arn_, topic_); + Topic topic(resource_namespace_, topic_); Permission permission; switch (item.permission()) { case rmq::Permission::READ: diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index 273c6eec7..74cc5469b 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -1,6 +1,6 @@ -#include "gtest/gtest.h" #include "ClientManagerFactory.h" #include "ClientConfigMock.h" +#include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN @@ -12,11 +12,11 @@ class ClientManagerFactoryTest : public testing::Test { protected: testing::NiceMock client_config_; - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; }; TEST_F(ClientManagerFactoryTest, testGetClientManager) { - EXPECT_CALL(client_config_, arn).Times(testing::AtLeast(1)).WillRepeatedly(testing::ReturnRef(arn_)); + EXPECT_CALL(client_config_, resourceNamespace).Times(testing::AtLeast(1)).WillRepeatedly(testing::ReturnRef(resource_namespace_)); ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); EXPECT_TRUE(client_manager); } diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index 1400b0469..8cfa79ef0 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -10,7 +10,7 @@ ROCKETMQ_NAMESPACE_BEGIN class ClientManagerTest : public testing::Test { public: void SetUp() override { - client_manager_ = std::make_shared(arn_); + client_manager_ = std::make_shared(resource_namespace_); client_manager_->start(); rpc_client_ = std::make_shared>(); ON_CALL(*rpc_client_, ok).WillByDefault(testing::Return(true)); @@ -23,7 +23,7 @@ class ClientManagerTest : public testing::Test { void TearDown() override { client_manager_->shutdown(); } protected: - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::string topic_{"TestTopic"}; std::string target_host_{"ipv4:10.0.0.0:10911"}; std::shared_ptr client_manager_; @@ -43,7 +43,7 @@ TEST_F(ClientManagerTest, testBasic) { TEST_F(ClientManagerTest, testResolveRoute) { auto rpc_cb = [](const QueryRouteRequest& request, InvocationContext* invocation_context) { auto partition = new rmq::Partition(); - partition->mutable_topic()->set_arn(request.topic().arn()); + partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); partition->mutable_topic()->set_name(request.topic().name()); partition->mutable_broker()->set_name("broker-0"); partition->mutable_broker()->set_id(0); @@ -63,7 +63,7 @@ TEST_F(ClientManagerTest, testResolveRoute) { absl::CondVar cv; QueryRouteRequest request; - request.mutable_topic()->set_arn(arn_); + request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(topic_); auto callback = [&](bool, const TopicRouteDataPtr&) { absl::MutexLock lk(&mtx); @@ -305,7 +305,7 @@ TEST_F(ClientManagerTest, processPullResult) { grpc::ClientContext client_context; PullMessageResponse response; auto message = new rmq::Message; - message->mutable_topic()->set_arn(arn_); + message->mutable_topic()->set_resource_namespace(resource_namespace_); message->mutable_topic()->set_name(topic_); message->set_body(message_body_); message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp index 3f464dcde..521ea64bb 100644 --- a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp +++ b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp @@ -7,25 +7,24 @@ ROCKETMQ_NAMESPACE_BEGIN class QueryAssignmentInfoTest : public testing::Test { protected: - std::string arn{"arn:mq://test"}; - std::string topic{"TopicTest"}; - std::string broker_name{"broker-a"}; - int broker_id = 0; - int total = 16; + std::string resource_namespace_{"mq://test"}; + std::string topic_{"TopicTest"}; + std::string broker_name_{"broker-a"}; + int broker_id_ = 0; + int total_ = 16; }; TEST_F(QueryAssignmentInfoTest, testCtor) { QueryAssignmentResponse response; - for (int i = 0; i < total; i++) { + for (int i = 0; i < total_; i++) { auto assignment = new rmq::Assignment; - assignment->set_mode(rmq::ConsumeMessageType::POP); - assignment->mutable_partition()->mutable_topic()->set_arn(arn); - assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); assignment->mutable_partition()->set_id(i); assignment->mutable_partition()->set_permission(rmq::Permission::READ); auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name); - broker->set_id(broker_id); + broker->set_name(broker_name_); + broker->set_id(broker_id_); broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); auto address = new rmq::Address; @@ -35,26 +34,24 @@ TEST_F(QueryAssignmentInfoTest, testCtor) { response.mutable_assignments()->AddAllocated(assignment); } TopicAssignment assignment(response); - EXPECT_EQ(total, assignment.assignmentList().size()); + EXPECT_EQ(total_, assignment.assignmentList().size()); const auto& item = *assignment.assignmentList().begin(); - EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name); - EXPECT_EQ(item.messageQueue().getTopic(), topic); - EXPECT_EQ(item.consumeType(), ConsumeMessageType::POP); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); + EXPECT_EQ(item.messageQueue().getTopic(), topic_); EXPECT_TRUE(item.messageQueue().getQueueId() < 16); } TEST_F(QueryAssignmentInfoTest, testCtor2) { QueryAssignmentResponse response; - for (int i = 0; i < total; i++) { + for (int i = 0; i < total_; i++) { auto assignment = new rmq::Assignment; - assignment->set_mode(rmq::ConsumeMessageType::POP); - assignment->mutable_partition()->mutable_topic()->set_arn(arn); - assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); assignment->mutable_partition()->set_id(i); assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name); - broker->set_id(broker_id); + broker->set_name(broker_name_); + broker->set_id(broker_id_); broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); auto address = new rmq::Address; @@ -64,26 +61,24 @@ TEST_F(QueryAssignmentInfoTest, testCtor2) { response.mutable_assignments()->AddAllocated(assignment); } TopicAssignment assignment(response); - EXPECT_EQ(total, assignment.assignmentList().size()); + EXPECT_EQ(total_, assignment.assignmentList().size()); const auto& item = *assignment.assignmentList().begin(); - EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name); - EXPECT_EQ(item.messageQueue().getTopic(), topic); - EXPECT_EQ(item.consumeType(), ConsumeMessageType::POP); + EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); + EXPECT_EQ(item.messageQueue().getTopic(), topic_); EXPECT_TRUE(item.messageQueue().getQueueId() < 16); } TEST_F(QueryAssignmentInfoTest, testCtor3) { QueryAssignmentResponse response; - for (int i = 0; i < total; i++) { + for (int i = 0; i < total_; i++) { auto assignment = new rmq::Assignment; - assignment->set_mode(rmq::ConsumeMessageType::POP); - assignment->mutable_partition()->mutable_topic()->set_arn(arn); - assignment->mutable_partition()->mutable_topic()->set_name(topic); + assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + assignment->mutable_partition()->mutable_topic()->set_name(topic_); assignment->mutable_partition()->set_id(i); assignment->mutable_partition()->set_permission(rmq::Permission::NONE); auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name); - broker->set_id(broker_id); + broker->set_name(broker_name_); + broker->set_id(broker_id_); broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); auto address = new rmq::Address; diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/test/cpp/ut/logger/BUILD.bazel index 5fb6ac1d0..1f092b6ec 100644 --- a/src/test/cpp/ut/logger/BUILD.bazel +++ b/src/test/cpp/ut/logger/BUILD.bazel @@ -10,7 +10,4 @@ cc_test( "//src/main/cpp/log:log_library", "@com_google_googletest//:gtest_main", ], - defines = [ - "BAZEL", - ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 7d0fe6565..8cfdd6bb7 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -25,7 +25,7 @@ class ClientImplTest : public testing::Test { void SetUp() override { grpc_init(); client_manager_ = std::make_shared>(); - ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); client_ = std::make_shared(group_); } @@ -33,7 +33,7 @@ class ClientImplTest : public testing::Test { void TearDown() override { grpc_shutdown(); } protected: - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::string group_{"Group-0"}; std::shared_ptr> client_manager_; Scheduler scheduler_; @@ -73,7 +73,7 @@ TEST_F(ClientImplTest, testBasic) { top_addressing_.injectHttpClient(std::move(http_client)); ON_CALL(*client_manager_, topAddressing).WillByDefault(testing::ReturnRef(top_addressing_)); - client_->arn(arn_); + client_->resourceNamespace(resource_namespace_); client_->start(); { absl::MutexLock lk(&mtx); diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp index 873296ed7..77a5f9709 100644 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -37,7 +37,7 @@ class ConsumeStandardMessageServiceTest : public testing::Test { ON_CALL(*process_queue_, getConsumer).WillByDefault(testing::Return(consumer)); ON_CALL(*consumer_, customExecutor).WillByDefault(testing::ReturnRef(executor_)); ON_CALL(*consumer_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); - ON_CALL(*consumer_, arn).WillByDefault(testing::ReturnRef(arn_)); + ON_CALL(*consumer_, resourceNamespace).WillByDefault(testing::ReturnRef(resource_namespace_)); ON_CALL(*consumer_, getGroupName).WillByDefault(testing::ReturnRef(group_name_)); } @@ -48,7 +48,7 @@ class ConsumeStandardMessageServiceTest : public testing::Test { std::string topic_{"TestTopic"}; std::string tag_{"TagA"}; std::string body_{"Body Content"}; - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::string group_name_{"CID_Test"}; uint32_t consume_batch_size_; std::shared_ptr> consumer_; diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 3adf054e5..c458519c8 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -131,7 +131,7 @@ class UnitTestSendCallback : public SendCallback { TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { auto producer = std::make_shared(group_name_); - producer->arn(arn_); + producer->resourceNamespace(resource_namespace_); producer->setNameServerList(name_server_list_); producer->setCredentialsProvider(credentials_provider_); producer->start(); @@ -154,7 +154,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { TEST_F(DefaultMQProducerUnitTest, testSendMessage) { auto producer = std::make_shared(group_name_); - producer->arn(arn_); + producer->resourceNamespace(resource_namespace_); producer->setNameServerList(name_server_list_); producer->setCredentialsProvider(credentials_provider_); producer->start(); @@ -166,7 +166,7 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { auto producer = std::make_shared(group_name_); - producer->arn(arn_); + producer->resourceNamespace(resource_namespace_); producer->setNameServerList(name_server_list_); producer->setCredentialsProvider(credentials_provider_); producer->start(); diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index b9fdc8137..725b8d6b9 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -8,15 +8,11 @@ #include "ReceiveMessageCallbackMock.h" #include "ReceiveMessageResult.h" #include "RpcClientMock.h" -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/CredentialsProvider.h" #include "rocketmq/MQMessageExt.h" #include "gtest/gtest.h" #include #include -#include #include #include #include @@ -35,8 +31,7 @@ class ProcessQueueTest : public testing::Test { credentials_provider_ = std::make_shared(access_key_, access_secret_); consumer_ = std::make_shared>(); auto consumer = std::dynamic_pointer_cast(consumer_); - process_queue_ = std::make_shared(message_queue_, filter_expression_, ConsumeMessageType::POP, - consumer, client_manager_); + process_queue_ = std::make_shared(message_queue_, filter_expression_, consumer, client_manager_); receive_message_callback_ = std::make_shared>(); process_queue_->callback(receive_message_callback_); } @@ -64,7 +59,7 @@ class ProcessQueueTest : public testing::Test { std::shared_ptr> consumer_; std::shared_ptr process_queue_; std::shared_ptr> receive_message_callback_; - std::string arn_{"arn:test"}; + std::string resource_namespace_{"mq://test"}; std::string message_body_{"Sample body"}; uint32_t threshold_quantity_{32}; @@ -269,13 +264,14 @@ TEST_F(ProcessQueueTest, testOffset) { TEST_F(ProcessQueueTest, testReceiveMessage_POP) { EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); - EXPECT_CALL(*consumer_, arn).WillRepeatedly(testing::ReturnRef(arn_)); + EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + EXPECT_CALL(*consumer_, receiveMessageAction).WillRepeatedly(testing::Return(ReceiveMessageAction::POLLING)); auto optional = absl::make_optional(filter_expression_); @@ -307,15 +303,15 @@ TEST_F(ProcessQueueTest, testReceiveMessage_POP) { } TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { - process_queue_->consumeType(ConsumeMessageType::PULL); EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); - EXPECT_CALL(*consumer_, arn).WillRepeatedly(testing::ReturnRef(arn_)); + EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); + EXPECT_CALL(*consumer_, receiveMessageAction).WillRepeatedly(testing::Return(ReceiveMessageAction::PULL)); auto optional = absl::make_optional(filter_expression_); diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index 1f8d848d7..d9c76555b 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -19,15 +19,15 @@ class ProducerImplTest : public testing::Test { void SetUp() override { grpc_init(); client_manager_ = std::make_shared>(); - ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); producer_ = std::make_shared(group_); - producer_->arn(arn_); + producer_->resourceNamespace(resource_namespace_); producer_->setNameServerList(name_server_list_); producer_->setCredentialsProvider(credentials_provider_); { std::vector partitions; - Topic topic(arn_, topic_); + Topic topic(resource_namespace_, topic_); std::vector
broker_addresses{Address(broker_host_, broker_port_)}; ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); Broker broker(broker_name_, broker_id_, service_address); @@ -44,7 +44,7 @@ class ProducerImplTest : public testing::Test { std::shared_ptr> client_manager_; std::shared_ptr producer_; std::vector name_server_list_{"10.0.0.1:9876"}; - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::string group_{"CID_test"}; std::string topic_{"Topic0"}; int queue_id_{1}; diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index a5d562db9..86cee143c 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -20,15 +20,15 @@ class PullConsumerImplTest : public testing::Test { grpc_init(); client_manager_ = std::make_shared>(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); - ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); pull_consumer_ = std::make_shared(group_); pull_consumer_->setNameServerList(name_server_list_); - pull_consumer_->arn(arn_); + pull_consumer_->resourceNamespace(resource_namespace_); { std::vector partitions; - Topic topic(arn_, topic_); + Topic topic(resource_namespace_, topic_); std::vector
broker_addresses{Address(broker_host_, broker_port_)}; ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); Broker broker(broker_name_, broker_id_, service_address); @@ -42,7 +42,7 @@ class PullConsumerImplTest : public testing::Test { void TearDown() override { grpc_shutdown(); } protected: - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::vector name_server_list_{"10.0.0.1:9876"}; std::string group_{"Group-0"}; std::string topic_{"Test"}; @@ -128,7 +128,7 @@ TEST_F(PullConsumerImplTest, testPull) { message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); message->mutable_system_attribute()->mutable_body_digest()->set_checksum(md5); - message->mutable_topic()->set_arn(arn_); + message->mutable_topic()->set_resource_namespace(resource_namespace_); message->mutable_topic()->set_name(topic_); message->mutable_system_attribute()->set_tag(tag_); diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 0142950f8..3b4e915d4 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -5,27 +5,39 @@ #include "MessageAccessor.h" #include "grpc/grpc.h" #include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" #include ROCKETMQ_NAMESPACE_BEGIN +class TestStandardMessageListener : public StandardMessageListener { +public: + ConsumeMessageResult consumeMessage(const std::vector& msgs) override { + return ConsumeMessageResult::SUCCESS; + } +}; + class PushConsumerImplTest : public testing::Test { +public: + PushConsumerImplTest() : message_listener_(absl::make_unique()) {} + void SetUp() override { grpc_init(); client_manager_ = std::make_shared>(); - ClientManagerFactory::getInstance().addClientManager(arn_, client_manager_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); push_consumer_ = std::make_shared(group_); - push_consumer_->arn(arn_); + push_consumer_->resourceNamespace(resource_namespace_); push_consumer_->setNameServerList(name_server_list_); + push_consumer_->registerMessageListener(message_listener_.get()); } void TearDown() override { grpc_shutdown(); } protected: std::vector name_server_list_{"10.0.0.1:9876"}; - std::string arn_{"arn:mq://test"}; + std::string resource_namespace_{"mq://test"}; std::string group_{"CID_test"}; std::string topic_{"Topic0"}; std::string tag_{"TagA"}; @@ -33,6 +45,7 @@ class PushConsumerImplTest : public testing::Test { std::string message_body_{"Message Body Content"}; int delay_level_{1}; std::shared_ptr> client_manager_; + std::unique_ptr message_listener_; std::shared_ptr push_consumer_; const std::string target_endpoint_{"localhost:10911"}; }; diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 9e363d2fa..405aca3f5 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -18,7 +18,7 @@ class MQClientTest : public testing::Test { void SetUp() override { grpc_init(); name_server_list_.emplace_back(name_server_address_); - client_instance_ = std::make_shared(arn_); + client_instance_ = std::make_shared(resource_namespace_); rpc_client_ns_ = std::make_shared>(); ON_CALL(*rpc_client_ns_, needHeartbeat()).WillByDefault(testing::Return(false)); ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); @@ -26,7 +26,7 @@ class MQClientTest : public testing::Test { .WillByDefault(testing::Invoke( std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); - ClientManagerFactory::getInstance().addClientManager(arn_, client_instance_); + ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_instance_); } void TearDown() override { @@ -45,7 +45,7 @@ class MQClientTest : public testing::Test { std::string name_server_address_{"ipv4:127.0.0.1:9876"}; std::string group_name_{"CID_Test"}; std::string topic_{"Topic_Test"}; - std::string arn_{"arn:mq::test"}; + std::string resource_namespace_{"mq://test"}; google::rpc::Code ok_{google::rpc::Code::OK}; bool client_ok_{true}; std::vector name_server_list_; @@ -60,7 +60,7 @@ class MQClientTest : public testing::Test { partition->set_id(i % avg_partition_per_host_); partition->set_permission(rmq::Permission::READ_WRITE); partition->mutable_topic()->set_name(request.topic().name()); - partition->mutable_topic()->set_arn(request.topic().arn()); + partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); std::string broker_name{"broker-"}; broker_name.push_back('a' + i / avg_partition_per_host_); partition->mutable_broker()->set_name(broker_name); From 27af767276984a919e135895823664752de674c6 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Wed, 15 Sep 2021 17:34:41 +0800 Subject: [PATCH 056/133] Make code compile on Windows (#18) * Make code work on Windows platform --- .bazelrc | 2 + bazel/curl.bzl | 201 ++++++++++++++++++ bazel/rocketmq_deps.bzl | 23 +- example/rocketmq/ExamplePushConsumer.cpp | 2 +- src/main/cpp/base/BUILD.bazel | 1 + src/main/cpp/base/MixAll.cpp | 21 +- src/main/cpp/base/ThreadPoolImpl.cpp | 62 ++++++ src/main/cpp/base/UniqueIdGenerator.cpp | 10 +- src/main/cpp/base/UtilAll.cpp | 55 +++-- src/main/cpp/base/include/MixAll.h | 2 +- src/main/cpp/base/include/ThreadPool.h | 19 ++ src/main/cpp/base/include/ThreadPoolImpl.h | 41 ++++ src/main/cpp/client/BUILD.bazel | 1 - src/main/cpp/client/ClientConfigImpl.cpp | 3 + src/main/cpp/client/ClientManagerFactory.cpp | 7 +- src/main/cpp/client/ClientManagerImpl.cpp | 40 ++-- .../cpp/client/include/ClientManagerImpl.h | 15 +- .../rocketmq/ConsumeFifoMessageService.cpp | 2 +- .../cpp/rocketmq/ConsumeMessageService.cpp | 8 +- .../ConsumeStandardMessageService.cpp | 2 +- .../cpp/rocketmq/include/ProcessQueueImpl.h | 2 +- src/main/cpp/scheduler/BUILD.bazel | 12 +- src/main/cpp/scheduler/Scheduler.cpp | 116 ---------- src/main/cpp/scheduler/SchedulerImpl.cpp | 127 +++++++++++ src/main/cpp/scheduler/include/Scheduler.h | 51 +---- .../cpp/scheduler/include/SchedulerImpl.h | 66 ++++++ src/test/cpp/it/RpcClientTest.cpp | 5 +- src/test/cpp/it/TopAddressingTest.cpp | 18 +- src/test/cpp/it/TopicPublishInfoTest.cpp | 5 +- src/test/cpp/ut/base/BUILD.bazel | 3 +- src/test/cpp/ut/base/HostInfoTest.cpp | 22 +- src/test/cpp/ut/base/ThreadPoolTest.cpp | 49 ++++- src/test/cpp/ut/base/UtilAllTest.cpp | 14 ++ src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 10 +- .../ut/rocketmq/CredentialsProviderTest.cpp | 15 ++ src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 21 +- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 8 +- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 12 +- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 89 ++++++-- third_party/asio.BUILD | 15 ++ third_party/curl.BUILD | 35 +++ 41 files changed, 929 insertions(+), 283 deletions(-) create mode 100644 bazel/curl.bzl create mode 100644 src/main/cpp/base/ThreadPoolImpl.cpp create mode 100644 src/main/cpp/base/include/ThreadPool.h create mode 100644 src/main/cpp/base/include/ThreadPoolImpl.h delete mode 100644 src/main/cpp/scheduler/Scheduler.cpp create mode 100644 src/main/cpp/scheduler/SchedulerImpl.cpp create mode 100644 src/main/cpp/scheduler/include/SchedulerImpl.h create mode 100644 third_party/asio.BUILD create mode 100644 third_party/curl.BUILD diff --git a/.bazelrc b/.bazelrc index d0aaed6c7..5dc7579e3 100644 --- a/.bazelrc +++ b/.bazelrc @@ -19,6 +19,8 @@ build --javabase=@bazel_tools//tools/jdk:remote_jdk11 # --config=windows on Windows, etc. build --enable_platform_specific_config +build:windows --cxxopt=/std:c++14 + build:macos --cxxopt=-std=c++11 build:macos --strip=never build:macos --copt -g diff --git a/bazel/curl.bzl b/bazel/curl.bzl new file mode 100644 index 000000000..b83d054b8 --- /dev/null +++ b/bazel/curl.bzl @@ -0,0 +1,201 @@ +# Copyright 2018, OpenCensus Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Compiler options for building libcurl. + +BASE_CURL_COPTS = [ + # Disable everything else except HTTP protocol. + "-DHTTP_ONLY=1", + "-DENABLE_IPV6=1", + "-DGETHOSTNAME_TYPE_ARG2=size_t", + "-DGETSERVBYPORT_R_ARGS=6", + "-DGETSERVBYPORT_R_BUFSIZE=4096", + "-DHAVE_ALARM=1", + "-DHAVE_ALLOCA_H=1", + "-DHAVE_ARPA_INET_H=1", + "-DHAVE_ARPA_TFTP_H=1", + "-DHAVE_ASSERT_H=1", + "-DHAVE_BASENAME=1", + "-DHAVE_BOOL_T=1", + "-DHAVE_CLOCK_GETTIME_MONOTONIC=1", + "-DHAVE_CONNECT=1", + "-DHAVE_DLFCN_H=1", + "-DHAVE_ENGINE_LOAD_BUILTIN_ENGINES=1", + "-DHAVE_ERRNO_H=1", + "-DHAVE_FCNTL=1", + "-DHAVE_FCNTL_H=1", + "-DHAVE_FCNTL_O_NONBLOCK=1", + "-DHAVE_FDOPEN=1", + "-DHAVE_FREEADDRINFO=1", + "-DHAVE_FREEIFADDRS=1", + "-DHAVE_FSETXATTR=1", + "-DHAVE_FSETXATTR_5=1", + "-DHAVE_FTRUNCATE=1", + "-DHAVE_GAI_STRERROR=1", + "-DHAVE_GETADDRINFO=1", + "-DHAVE_GETADDRINFO_THREADSAFE=1", + "-DHAVE_GETEUID=1", + "-DHAVE_GETHOSTBYADDR=1", + "-DHAVE_GETHOSTBYADDR_R=1", + "-DHAVE_GETHOSTBYADDR_R_8=1", + "-DHAVE_GETHOSTBYNAME=1", + "-DHAVE_GETHOSTBYNAME_R=1", + "-DHAVE_GETHOSTBYNAME_R_6=1", + "-DHAVE_GETHOSTNAME=1", + "-DHAVE_GETIFADDRS=1", + "-DHAVE_GETNAMEINFO=1", + "-DHAVE_GETPPID=1", + "-DHAVE_GETPWUID=1", + "-DHAVE_GETPWUID_R=1", + "-DHAVE_GETRLIMIT=1", + "-DHAVE_GETSERVBYPORT_R=1", + "-DHAVE_GETTIMEOFDAY=1", + "-DHAVE_GMTIME_R=1", + "-DHAVE_IFADDRS_H=1", + "-DHAVE_IF_NAMETOINDEX=1", + "-DHAVE_INET_NTOP=1", + "-DHAVE_INET_PTON=1", + "-DHAVE_INTTYPES_H=1", + "-DHAVE_IOCTL=1", + "-DHAVE_IOCTL_FIONBIO=1", + "-DHAVE_IOCTL_SIOCGIFADDR=1", + "-DHAVE_LIBGEN_H=1", + "-DHAVE_LL=1", + "-DHAVE_LOCALE_H=1", + "-DHAVE_LOCALTIME_R=1", + "-DHAVE_LONGLONG=1", + "-DHAVE_MALLOC_H=1", + "-DHAVE_MEMORY_H=1", + "-DHAVE_NETDB_H=1", + "-DHAVE_NETINET_IN_H=1", + "-DHAVE_NETINET_TCP_H=1", + "-DHAVE_NET_IF_H=1", + "-DHAVE_PIPE=1", + "-DHAVE_POLL=1", + "-DHAVE_POLL_FINE=1", + "-DHAVE_POLL_H=1", + "-DHAVE_POSIX_STRERROR_R=1", + "-DHAVE_PTHREAD_H=1", + "-DHAVE_PWD_H=1", + "-DHAVE_RECV=1", + "-DHAVE_SELECT=1", + "-DHAVE_SEND=1", + "-DHAVE_SETJMP_H=1", + "-DHAVE_SETLOCALE=1", + "-DHAVE_SETRLIMIT=1", + "-DHAVE_SETSOCKOPT=1", + "-DHAVE_SGTTY_H=1", + "-DHAVE_SIGACTION=1", + "-DHAVE_SIGINTERRUPT=1", + "-DHAVE_SIGNAL=1", + "-DHAVE_SIGNAL_H=1", + "-DHAVE_SIGSETJMP=1", + "-DHAVE_SIG_ATOMIC_T=1", + "-DHAVE_SOCKADDR_IN6_SIN6_SCOPE_ID=1", + "-DHAVE_SOCKET=1", + "-DHAVE_SOCKETPAIR=1", + "-DHAVE_STDBOOL_H=1", + "-DHAVE_STDINT_H=1", + "-DHAVE_STDIO_H=1", + "-DHAVE_STDLIB_H=1", + "-DHAVE_STRCASECMP=1", + "-DHAVE_STRDUP=1", + "-DHAVE_STRERROR_R=1", + "-DHAVE_STRINGS_H=1", + "-DHAVE_STRING_H=1", + "-DHAVE_STRNCASECMP=1", + "-DHAVE_STRSTR=1", + "-DHAVE_STRTOK_R=1", + "-DHAVE_STRTOLL=1", + "-DHAVE_STRUCT_SOCKADDR_STORAGE=1", + "-DHAVE_STRUCT_TIMEVAL=1", + "-DHAVE_SYS_IOCTL_H=1", + "-DHAVE_SYS_PARAM_H=1", + "-DHAVE_SYS_POLL_H=1", + "-DHAVE_SYS_RESOURCE_H=1", + "-DHAVE_SYS_SELECT_H=1", + "-DHAVE_SYS_SOCKET_H=1", + "-DHAVE_SYS_STAT_H=1", + "-DHAVE_SYS_TIME_H=1", + "-DHAVE_SYS_TYPES_H=1", + "-DHAVE_SYS_UIO_H=1", + "-DHAVE_SYS_UN_H=1", + "-DHAVE_SYS_WAIT_H=1", + "-DHAVE_SYS_XATTR_H=1", + "-DHAVE_TERMIOS_H=1", + "-DHAVE_TERMIO_H=1", + "-DHAVE_TIME_H=1", + "-DHAVE_UNISTD_H=1", + "-DHAVE_UTIME=1", + "-DHAVE_UTIMES=1", + "-DHAVE_UTIME_H=1", + "-DHAVE_VARIADIC_MACROS_C99=1", + "-DHAVE_VARIADIC_MACROS_GCC=1", + "-DHAVE_WRITABLE_ARGV=1", + "-DHAVE_WRITEV=1", + "-DRECV_TYPE_ARG1=int", + "-DRECV_TYPE_ARG2=void*", + "-DRECV_TYPE_ARG3=size_t", + "-DRECV_TYPE_ARG4=int", + "-DRECV_TYPE_RETV=ssize_t", + "-DRETSIGTYPE=void", + "-DSELECT_QUAL_ARG5=", + "-DSELECT_TYPE_ARG1=int", + "-DSELECT_TYPE_ARG234=fd_set*", + "-DSELECT_TYPE_RETV=int", + "-DSEND_QUAL_ARG2=const", + "-DSEND_TYPE_ARG1=int", + "-DSEND_TYPE_ARG2=void*", + "-DSEND_TYPE_ARG3=size_t", + "-DSEND_TYPE_ARG4=int", + "-DSEND_TYPE_RETV=ssize_t", + "-DSIZEOF_CURL_OFF_T=8", + "-DSIZEOF_INT=4", + "-DSIZEOF_LONG=8", + "-DSIZEOF_OFF_T=8", + "-DSIZEOF_SHORT=2", + "-DSIZEOF_SIZE_T=8", + "-DSIZEOF_TIME_T=8", + "-DSTDC_HEADERS=1", + "-DSTRERROR_R_TYPE_ARG3=size_t", + "-DTIME_WITH_SYS_TIME=1", + "-DUSE_THREADS_POSIX=1", + "-DUSE_UNIX_SOCKETS=1", + + # Extra defines needed by curl + "-DBUILDING_LIBCURL", + "-DCURL_HIDDEN_SYMBOLS", +] + +LINUX_CURL_COPTS = [ + "-DHAVE_LINUX_TCP_H=1", + "-DHAVE_MSG_NOSIGNAL=1", +] + +CURL_COPTS = select({ + "//:windows": [ + # Disable everything else except HTTP protocol. + "/DHTTP_ONLY=1", + "/DCURL_STATICLIB", + "/DWIN32", + "/DBUILDING_LIBCURL", + "/DUSE_WIN32_IDN", + "/DWANT_IDN_PROTOTYPES", + "/DUSE_IPV6", + "/DUSE_WINDOWS_SSPI", + "/DUSE_SCHANNEL", + ], + "//:osx": BASE_CURL_COPTS, + "//conditions:default": BASE_CURL_COPTS + LINUX_CURL_COPTS, +}) diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index e56885de1..a17955973 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -83,9 +83,9 @@ def rocketmq_deps(): if "com_github_grpc_grpc" not in native.existing_rules(): http_archive( name = "com_github_grpc_grpc", - strip_prefix = "grpc-1.40.0", - sha256 = "13e7c6460cd979726e5b3b129bb01c34532f115883ac696a75eb7f1d6a9765ed", - urls = ["https://github.com/grpc/grpc/archive/v1.40.0.tar.gz"], + strip_prefix = "grpc-1.39.0", + sha256 = "b16992aa1c949c10d5d5ce2a62f9d99fa7de77da2943e643fb66dcaf075826d6", + urls = ["https://github.com/grpc/grpc/archive/v1.39.0.tar.gz"], ) if "io_opentelemetry_cpp" not in native.existing_rules(): @@ -111,10 +111,19 @@ def rocketmq_deps(): maybe( http_archive, - name = "com_github_progschj_ThreadPool", - build_file = "@org_apache_rocketmq//third_party:ThreadPool.BUILD", - strip_prefix = "ThreadPool-1.0", + name = "asio", + build_file = "@org_apache_rocketmq//third_party:asio.BUILD", + strip_prefix = "asio-1.18.2", urls = [ - "https://github.com/lizhanhui/ThreadPool/archive/refs/tags/v1.0.tar.gz", + "https://github.com/lizhanhui/asio/archive/refs/tags/v1.18.2.tar.gz", ], + ) + + # Curl library + maybe( + http_archive, + name = "com_github_curl", + build_file = "@org_apache_rocketmq//third_party:curl.BUILD", + strip_prefix = "curl-master", + urls = ["https://github.com/curl/curl/archive/master.zip"], ) \ No newline at end of file diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 166d16863..40c1c14f1 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -12,7 +12,7 @@ class SampleMQMessageListener : public StandardMessageListener { public: ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { - SPDLOG_WARN("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); + SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); std::this_thread::sleep_for(std::chrono::seconds(1)); } return ConsumeMessageResult::SUCCESS; diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index a5a3fdd52..88aecd04d 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -64,5 +64,6 @@ cc_library( "@boringssl//:crypto", "@boringssl//:ssl", "//external:madler_zlib", + "@asio//:asio", ], ) \ No newline at end of file diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 433b36d99..b64320307 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -1,19 +1,22 @@ #include "MixAll.h" #include +#include #include "absl/random/random.h" #include "absl/strings/str_split.h" - #include "fmt/format.h" - -#include -#include - +#include "openssl/md5.h" +#include "openssl/sha.h" #include "zlib.h" + +#ifdef _WIN32 +#include +#else #include #include #include +#endif ROCKETMQ_NAMESPACE_BEGIN @@ -219,6 +222,14 @@ bool MixAll::homeDirectory(std::string& home_dir) { } } return false; +#else + char* home = getenv("USERPROFILE"); + if (home) { + home_dir.clear(); + home_dir.append(home, strlen(home)); + return true; + } + return false; #endif } diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp new file mode 100644 index 000000000..dee83ceee --- /dev/null +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -0,0 +1,62 @@ +#include "ThreadPoolImpl.h" +#include "absl/memory/memory.h" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/post.hpp" +#include "rocketmq/RocketMQ.h" +#include "rocketmq/State.h" +#include "spdlog/spdlog.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +ThreadPoolImpl::ThreadPoolImpl(std::uint16_t workers) + : work_guard_( + absl::make_unique>(context_.get_executor())), + workers_(workers) {} + +void ThreadPoolImpl::start() { + for (std::uint16_t i = 0; i < workers_; i++) { + std::thread worker([this]() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTED, std::memory_order_relaxed)) { + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + context_.run(); + }); + threads_.emplace_back(std::move(worker)); + } + + { + absl::MutexLock lk(&start_mtx_); + if (State::CREATED == state_.load(std::memory_order_relaxed)) { + start_cv_.Wait(&start_mtx_); + } + } +} + +void ThreadPoolImpl::shutdown() { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + work_guard_->reset(); + context_.stop(); + for (auto& thread : threads_) { + if (thread.joinable()) { + thread.join(); + } + } + state_.store(State::STOPPED, std::memory_order_relaxed); + } +} + +void ThreadPoolImpl::submit(std::function task) { + if (State::STARTED == state_.load(std::memory_order_relaxed)) { + asio::post(context_, task); + } else { + SPDLOG_WARN("State of ThreadPool is not STARTED"); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp index 0b23dd21b..05f8a5db6 100644 --- a/src/main/cpp/base/UniqueIdGenerator.cpp +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -4,7 +4,10 @@ #include "UtilAll.h" #include "absl/base/internal/endian.h" #include -#include + +#ifdef _WIN32 +#include +#endif ROCKETMQ_NAMESPACE_BEGIN @@ -20,7 +23,12 @@ UniqueIdGenerator::UniqueIdGenerator() SPDLOG_WARN("Failed to get network interface MAC address"); } +#ifdef _WIN32 + int pid = _getpid(); +#else pid_t pid = getpid(); +#endif + uint32_t big_endian_pid = absl::big_endian::FromHost32(pid); // Copy the lower 2 bytes memcpy(prefix_.data() + 6, reinterpret_cast(&big_endian_pid) + 2, sizeof(uint16_t)); diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index f2cc38dab..e4400d5c0 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -1,12 +1,9 @@ #include "UtilAll.h" #include "LoggerImpl.h" -#include "absl/strings/str_split.h" #include "src/core/lib/iomgr/gethostname.h" #include "zlib.h" -#include #include -#include #if defined(__APPLE__) #include @@ -17,26 +14,25 @@ #include #define AF_FAMILY AF_PACKET #elif defined(_WIN32) - +#include +#include +#pragma comment(lib, "iphlpapi.lib") #endif +#ifndef _WIN32 +#include #include #include #include #include +#endif -ROCKETMQ_NAMESPACE_BEGIN +#include "asio.hpp" -#if defined(__APPLE__) -#define AF_FAMILY AF_LINK -#elif defined(__linux__) -#define AF_FAMILY AF_PACKET -#elif defined(_WIN32) -#endif +ROCKETMQ_NAMESPACE_BEGIN std::string UtilAll::hostname() { - std::string host_name(grpc_gethostname()); - return host_name; + return asio::ip::host_name(); } bool UtilAll::macAddress(std::vector& mac) { @@ -48,6 +44,7 @@ bool UtilAll::macAddress(std::vector& mac) { return true; } +#ifndef _WIN32 struct ifaddrs *head = nullptr, *node; if (getifaddrs(&head)) { return false; @@ -82,6 +79,38 @@ bool UtilAll::macAddress(std::vector& mac) { } freeifaddrs(head); return true; +#else + PIP_ADAPTER_INFO adaptor_info; + DWORD buf_len = sizeof(IP_ADAPTER_INFO); + char mac_address[18]; + adaptor_info = (IP_ADAPTER_INFO*) malloc(buf_len); + if (!adaptor_info) { + // TODO: running out of memroy + } + + if(GetAdaptersInfo(adaptor_info, &buf_len) == NO_ERROR) { + PIP_ADAPTER_INFO item = adaptor_info; + do { + bool all_zero = true; + for (auto& b : item->Address) { + if (b != 0) { + all_zero = false; + break; + } + } + if (!all_zero) { + cache.resize(6); + memcpy(cache.data(), item->Address, 6); + mac_cached = true; + mac = cache; + break; + } + item = item->Next; + } while (item); + } else { + free(adaptor_info); + } +#endif } bool UtilAll::compress(const std::string& src, std::string& dst) { diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 3b1762af2..11aac599d 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -3,10 +3,10 @@ #include "absl/strings/string_view.h" #include "re2/re2.h" #include "rocketmq/MQMessage.h" + #include #include #include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/ThreadPool.h b/src/main/cpp/base/include/ThreadPool.h new file mode 100644 index 000000000..4e6044ec3 --- /dev/null +++ b/src/main/cpp/base/include/ThreadPool.h @@ -0,0 +1,19 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPool { +public: + virtual ~ThreadPool() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual void submit(std::function task) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/ThreadPoolImpl.h b/src/main/cpp/base/include/ThreadPoolImpl.h new file mode 100644 index 000000000..43090065e --- /dev/null +++ b/src/main/cpp/base/include/ThreadPoolImpl.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ThreadPool.h" +#include "absl/synchronization/mutex.h" +#include "asio/io_context.hpp" +#include "rocketmq/RocketMQ.h" +#include "rocketmq/State.h" +#include +#include +#include +#include +#include +#include + +#include "asio.hpp" + +ROCKETMQ_NAMESPACE_BEGIN + +class ThreadPoolImpl : public ThreadPool { +public: + ThreadPoolImpl(std::uint16_t workers); + + ~ThreadPoolImpl() override = default; + + void start() override; + + void shutdown() override; + + void submit(std::function task) override; + +private: + asio::io_context context_; + std::unique_ptr> work_guard_; + std::uint16_t workers_; + std::vector threads_; + std::atomic state_{State::CREATED}; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index 83f24d6c6..a19626e8b 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -17,7 +17,6 @@ cc_library( "@com_google_absl//absl/container:flat_hash_set", "@com_github_grpc_grpc//:grpc_secure", "@com_github_grpc_grpc//:grpc++", - "@com_github_progschj_ThreadPool//:ThreadPool", "@boringssl//:ssl", "//external:gtest", ], diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index 9c31ddd95..650305e58 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -1,7 +1,10 @@ #include "ClientConfigImpl.h" #include "UtilAll.h" #include + +#ifndef _WIN32 #include +#endif ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 982128b39..53ab81e84 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -15,7 +15,7 @@ ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& clie if (search != client_manager_table_.end()) { ClientManagerPtr client_manager = search->second.lock(); if (client_manager) { - SPDLOG_DEBUG("Re-use existing client_instance[ARN={}]", client_config.resourceNamespace()); + SPDLOG_DEBUG("Re-use existing client_manager[resource_namespace={}]", client_config.resourceNamespace()); return client_manager; } else { client_manager_table_.erase(client_config.resourceNamespace()); @@ -24,12 +24,13 @@ ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& clie ClientManagerPtr client_manager = std::make_shared(client_config.resourceNamespace()); std::weak_ptr client_instance_weak_ptr(client_manager); client_manager_table_.insert_or_assign(client_config.resourceNamespace(), client_instance_weak_ptr); - SPDLOG_INFO("Created a new client manager[ARN={}]", client_config.resourceNamespace()); + SPDLOG_INFO("Created a new client manager[resource_namespace={}]", client_config.resourceNamespace()); return client_manager; } } -void ClientManagerFactory::addClientManager(const std::string& resource_namespace, const ClientManagerPtr& client_manager) { +void ClientManagerFactory::addClientManager(const std::string& resource_namespace, + const ClientManagerPtr& client_manager) { absl::MutexLock lk(&client_manager_table_mtx_); client_manager_table_.insert_or_assign(resource_namespace, std::weak_ptr(client_manager)); } diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 5bac83e3a..69f2c6d30 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -27,7 +27,7 @@ ROCKETMQ_NAMESPACE_BEGIN ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) : resource_namespace_(std::move(resource_namespace)), state_(State::CREATED), completion_queue_(std::make_shared()), - callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), + callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); assignLabels(latency_histogram_); @@ -36,8 +36,10 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) // Make use of encryption only at the moment. std::vector identity_key_cert_list; - grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, - .certificate_chain = TlsHelper::client_certificate_chain}; + grpc::experimental::IdentityKeyCertPair pair{}; + pair.private_key = TlsHelper::client_private_key; + pair.certificate_chain = TlsHelper::client_certificate_chain; + identity_key_cert_list.emplace_back(pair); certificate_provider_ = std::make_shared(TlsHelper::CA, identity_key_cert_list); @@ -62,6 +64,9 @@ void ClientManagerImpl::start() { } state_.store(State::STARTING, std::memory_order_relaxed); + callback_thread_pool_->start(); + scheduler_.start(); + std::weak_ptr client_instance_weak_ptr = shared_from_this(); auto health_check_functor = [client_instance_weak_ptr]() { @@ -70,15 +75,15 @@ void ClientManagerImpl::start() { client_instance->doHealthCheck(); } }; - health_check_handle_ = scheduler_.schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), - std::chrono::seconds(5)); + health_check_task_id_ = scheduler_.schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), + std::chrono::seconds(5)); auto heartbeat_functor = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); if (client_instance) { client_instance->doHeartbeat(); } }; - heartbeat_handle_ = + heartbeat_task_id_ = scheduler_.schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); completion_queue_thread_ = std::thread(std::bind(&ClientManagerImpl::pollCompletionQueue, this)); @@ -89,7 +94,7 @@ void ClientManagerImpl::start() { client_instance->logStats(); } }; - stats_handle_ = + stats_task_id_ = scheduler_.schedule(stats_functor_, STATS_TASK_NAME, std::chrono::seconds(0), std::chrono::seconds(10)); state_.store(State::STARTED, std::memory_order_relaxed); } @@ -100,19 +105,22 @@ void ClientManagerImpl::shutdown() { SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); return; } - state_.store(STOPPING, std::memory_order_relaxed); - if (health_check_handle_) { - scheduler_.cancel(health_check_handle_); + + callback_thread_pool_->shutdown(); + + if (health_check_task_id_) { + scheduler_.cancel(health_check_task_id_); } - if (heartbeat_handle_) { - scheduler_.cancel(heartbeat_handle_); + if (heartbeat_task_id_) { + scheduler_.cancel(heartbeat_task_id_); } - if (stats_handle_) { - scheduler_.cancel(stats_handle_); + if (stats_task_id_) { + scheduler_.cancel(stats_task_id_); } + scheduler_.shutdown(); { absl::MutexLock lk(&rpc_clients_mtx_); @@ -300,7 +308,7 @@ void ClientManagerImpl::pollCompletionQueue() { SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); } auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; - callback_thread_pool_->enqueue(callback); + callback_thread_pool_->submit(callback); } SPDLOG_INFO("CompletionQueue is fully drained and shut down"); } @@ -836,7 +844,7 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess } } - timeval tv{.tv_sec = 0, .tv_usec = 0}; + timeval tv{}; // Message-type MessageType message_type; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index faec6e49b..f50ecc6ba 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -9,9 +9,9 @@ #include "ReceiveMessageCallback.h" #include "RpcClient.h" #include "RpcClientImpl.h" -#include "Scheduler.h" +#include "SchedulerImpl.h" #include "SendMessageContext.h" -#include "ThreadPool.h" +#include "ThreadPoolImpl.h" #include "TopAddressing.h" #include "TopicRouteChangeCallback.h" #include "TopicRouteData.h" @@ -25,6 +25,7 @@ #include "rocketmq/State.h" #include #include +#include #include #include #include @@ -200,7 +201,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void logStats(); - Scheduler scheduler_; + SchedulerImpl scheduler_; static const char* HEARTBEAT_TASK_NAME; static const char* STATS_TASK_NAME; @@ -216,12 +217,12 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share absl::flat_hash_map> rpc_clients_ GUARDED_BY(rpc_clients_mtx_); absl::Mutex rpc_clients_mtx_; // protects rpc_clients_ - std::uintptr_t heartbeat_handle_{0}; - std::uintptr_t health_check_handle_{0}; - std::uintptr_t stats_handle_{0}; + std::uint32_t heartbeat_task_id_{0}; + std::uint32_t health_check_task_id_{0}; + std::uint32_t stats_task_id_{0}; std::shared_ptr completion_queue_; - std::unique_ptr callback_thread_pool_; + std::unique_ptr callback_thread_pool_; std::thread completion_queue_thread_; diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 3d56c6f9e..1049055a8 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -51,7 +51,7 @@ void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr consume_task = std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); SPDLOG_DEBUG("Submit FIFO consume task to thread pool"); - pool_->enqueue(consume_task); + pool_->submit(consume_task); } void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { diff --git a/src/main/cpp/rocketmq/ConsumeMessageService.cpp b/src/main/cpp/rocketmq/ConsumeMessageService.cpp index 055305c18..8ca682781 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageService.cpp @@ -1,18 +1,19 @@ #include "ConsumeMessageService.h" #include "LoggerImpl.h" #include "PushConsumer.h" +#include "ThreadPoolImpl.h" ROCKETMQ_NAMESPACE_BEGIN ConsumeMessageService::ConsumeMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener) - : state_(State::CREATED), thread_count_(thread_count), - pool_(absl::make_unique(thread_count_)), consumer_(std::move(consumer)), - message_listener_(message_listener) {} + : state_(State::CREATED), thread_count_(thread_count), pool_(absl::make_unique(thread_count_)), + consumer_(std::move(consumer)), message_listener_(message_listener) {} void ConsumeMessageService::start() { State expected = State::CREATED; if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + pool_->start(); dispatch_thread_ = std::thread([this] { State current_state = state_.load(std::memory_order_relaxed); while (State::STOPPED != current_state && State::STOPPING != current_state) { @@ -45,6 +46,7 @@ void ConsumeMessageService::throttle(const std::string& topic, uint32_t threshol void ConsumeMessageService::shutdown() { State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { + pool_->shutdown(); { absl::MutexLock lk(&dispatch_mtx_); dispatch_cv_.SignalAll(); diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index d96b9866e..c08eac8e5 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -83,7 +83,7 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& std::function consume_task = std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue_ptr, messages); SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); - pool_->enqueue(consume_task); + pool_->submit(consume_task); } } diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index a598a0bf8..dcda003bf 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -36,7 +36,7 @@ ROCKETMQ_NAMESPACE_END namespace std { template <> struct less { - bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) { + bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) const { return lhs.offset_ < rhs.offset_; } }; diff --git a/src/main/cpp/scheduler/BUILD.bazel b/src/main/cpp/scheduler/BUILD.bazel index 23ee433e5..f0b9158bb 100644 --- a/src/main/cpp/scheduler/BUILD.bazel +++ b/src/main/cpp/scheduler/BUILD.bazel @@ -1,20 +1,16 @@ load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "scheduler_library", - hdrs = [ - "include/Scheduler.h", - ], - srcs = [ - "Scheduler.cpp", - ], + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), strip_include_prefix = "//src/main/cpp/scheduler/include", deps = [ "//api:rocketmq_interface", "//src/main/cpp/log:log_library", "@com_google_absl//absl/base", "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/container:flat_hash_set", - "@com_github_grpc_grpc//:grpc++", + "@com_google_absl//absl/container:flat_hash_map", + "@asio//:asio", ], visibility = ["//visibility:public"], ) \ No newline at end of file diff --git a/src/main/cpp/scheduler/Scheduler.cpp b/src/main/cpp/scheduler/Scheduler.cpp deleted file mode 100644 index 0ebcb86df..000000000 --- a/src/main/cpp/scheduler/Scheduler.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "Scheduler.h" - -#include "LoggerImpl.h" -#include "absl/memory/memory.h" -#include "src/core/lib/iomgr/closure.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -void Scheduler::clear(TimerTask* timer_task) { - absl::MutexLock lk(&tasks_mtx_); - tasks_.erase(reinterpret_cast(timer_task)); -} - -void Scheduler::onTrigger(void* arg, grpc_error_handle error) { - auto timer_task = reinterpret_cast(arg); - if (GRPC_ERROR_CANCELLED == error) { - SPDLOG_INFO("Task[{}] has been cancelled", timer_task->task_name); - timer_task->scheduler->clear(timer_task); - delete timer_task; - return; - } - - /** - * Consider the following fact that grpc_timer_cancel treats fail-to-cancel-in-flight task as success. - * Tasks without proper handles are deleted directly. - * ================================================================================================== - * Cancel an *timer. - * There are three cases: - * 1. We normally cancel the timer - * 2. The timer has already run - * 3. We can't cancel the timer because it is "in flight". - * - * In all of these cases, the cancellation is still considered successful. - * They are essentially distinguished in that the timer_cb will be run - * exactly once from either the cancellation (with error GRPC_ERROR_CANCELLED) - * or from the activation (with error GRPC_ERROR_NONE). - * - * Note carefully that the callback function MAY occur in the same callstack - * as grpc_timer_cancel. It's expected that most timers will be cancelled (their - * primary use is to implement deadlines), and so this code is optimized such - * that cancellation costs as little as possible. Making callbacks run inline - * matches this aim. - * - * Requires: cancel() must happen after init() on a given timer. - * ================================================================================================== - */ - { - absl::MutexLock lk(&timer_task->scheduler->tasks_mtx_); - uintptr_t ptr = reinterpret_cast(timer_task); - if (!timer_task->scheduler->tasks_.contains(ptr)) { - SPDLOG_WARN("Task[{}] should have been cancelled", timer_task->task_name); - delete timer_task; - return; - } - } - - SPDLOG_DEBUG("Execute timer-task: {}", timer_task->task_name); - timer_task->callback(); - - if (!timer_task->interval) { - SPDLOG_DEBUG("Completed single-shot timer task[{}]", timer_task->task_name); - timer_task->scheduler->clear(timer_task); - delete timer_task; - return; - } - - SPDLOG_DEBUG("Periodic task[name={}] is scheduled for the next round", timer_task->task_name); - grpc_core::ExecCtx exec_ctx; - grpc_timer_init(&timer_task->g_timer, grpc_core::ExecCtx::Get()->Now() + timer_task->interval, - GRPC_CLOSURE_CREATE(&Scheduler::onTrigger, timer_task, grpc_schedule_on_exec_ctx)); -} - -Scheduler::Scheduler() { spdlog::set_level(spdlog::level::debug); } - -Scheduler::~Scheduler() {} - -std::uintptr_t Scheduler::schedule(const std::function& functor, const std::string& task_name, - std::chrono::milliseconds delay, std::chrono::milliseconds interval) { - auto timer_task = new TimerTask(); - timer_task->callback = std::move(functor); - timer_task->scheduler = this; - timer_task->interval = interval.count(); - timer_task->task_name = task_name; - - { - absl::MutexLock lk(&tasks_mtx_); - tasks_.insert(reinterpret_cast(timer_task)); - } - - grpc_core::ExecCtx exec_ctx; - grpc_timer_init(&timer_task->g_timer, grpc_core::ExecCtx::Get()->Now() + delay.count(), - GRPC_CLOSURE_CREATE(&Scheduler::onTrigger, timer_task, grpc_schedule_on_exec_ctx)); - SPDLOG_DEBUG("Task[name={}] scheduled OK", timer_task->task_name); - return reinterpret_cast(timer_task); -} - -void Scheduler::cancel(std::uintptr_t task_id) { - { - absl::MutexLock lk(&tasks_mtx_); - if (!tasks_.contains(task_id)) { - SPDLOG_WARN("No matched schedule task found. Cancel ignored"); - return; - } - } - - auto timer_task = reinterpret_cast(task_id); - grpc_core::ExecCtx exec_ctx; - grpc_timer_cancel(&timer_task->g_timer); - SPDLOG_DEBUG("Scheduled task[name={}] will get cancelled", timer_task->task_name); - - clear(timer_task); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp new file mode 100644 index 000000000..8cb9d53fa --- /dev/null +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -0,0 +1,127 @@ +#include "SchedulerImpl.h" + +#include "LoggerImpl.h" +#include "absl/memory/memory.h" +#include "asio/error_code.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/steady_timer.hpp" +#include +#include +#include +#include +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +SchedulerImpl::SchedulerImpl() + : work_guard_( + absl::make_unique>(context_.get_executor())) { + spdlog::set_level(spdlog::level::debug); +} + +void SchedulerImpl::start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + + for (unsigned int i = 0; i < std::thread::hardware_concurrency(); i++) { + auto worker = std::thread([this]() { + { + State expect = State::STARTING; + if (state_.compare_exchange_strong(expect, State::STARTED, std::memory_order_relaxed)) { + absl::MutexLock lk(&start_mtx_); + start_cv_.SignalAll(); + } + } + context_.run(); + }); + threads_.emplace_back(std::move(worker)); + } + + { + absl::MutexLock lk(&start_mtx_); + if (State::STARTING == state_.load(std::memory_order_relaxed)) { + start_cv_.Wait(&start_mtx_); + SPDLOG_INFO("Scheduler threads start to loop"); + } + } + } +} + +void SchedulerImpl::shutdown() { + State expected = State::STARTED; + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + work_guard_->reset(); + { + absl::MutexLock lk(&tasks_mtx_); + tasks_.clear(); + } + context_.stop(); + + for (auto& worker : threads_) { + if (worker.joinable()) { + worker.join(); + } + } + + state_.store(State::STOPPED); + } +} + +std::uint32_t SchedulerImpl::schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) { + static std::uint32_t task_id = 0; + + auto task = std::make_shared(); + task->task_name = task_name; + task->callback = functor; + task->interval = interval; + + std::uint32_t id; + { + absl::MutexLock lk(&tasks_mtx_); + id = ++task_id; + tasks_.insert({id, task}); + } + asio::steady_timer* timer = new asio::steady_timer(context_, delay); + SPDLOG_DEBUG("Timer-task[name={}] to fire in {}ms", task_name, delay.count()); + timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer, std::weak_ptr(task))); + return id; +} + +void SchedulerImpl::cancel(std::uint32_t task_id) { + absl::MutexLock lk(&tasks_mtx_); + if (!tasks_.contains(task_id)) { + SPDLOG_ERROR("Scheduler does not have the task to delete. Task-ID specified is: {}", task_id); + return; + } + auto search = tasks_.find(task_id); + assert(search != tasks_.end()); + SPDLOG_INFO("Cancel task[task-id={}, name={}]", task_id, search->second->task_name); + tasks_.erase(search); +} + +void SchedulerImpl::execute(const asio::error_code& ec, asio::steady_timer* timer, std::weak_ptr task) { + std::shared_ptr timer_task = task.lock(); + if (!timer_task) { + delete timer; + return; + } + + SPDLOG_INFO("Execute task: {}. Use-count: {}", timer_task->task_name, timer_task.use_count()); + + // Execute the actual callback. + timer_task->callback(); + + if (timer_task->interval.count()) { + timer->expires_at(timer->expiry() + timer_task->interval); + timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer, task)); + SPDLOG_DEBUG("Repeated timer-task to fire in {}ms", timer_task->interval.count()); + } else { + delete timer; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index 492f13e2d..ab1a8f239 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -1,55 +1,26 @@ #pragma once -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_set.h" -#include "absl/synchronization/mutex.h" -#include "rocketmq/RocketMQ.h" -#include "src/core/lib/iomgr/timer.h" + +#include #include #include -#include -#include +#include -ROCKETMQ_NAMESPACE_BEGIN - -class Scheduler; +#include "rocketmq/RocketMQ.h" -struct TimerTask { - Scheduler* scheduler; - grpc_timer g_timer; - std::function callback; - grpc_millis interval; - std::string task_name; -}; +ROCKETMQ_NAMESPACE_BEGIN class Scheduler { public: - Scheduler(); - - ~Scheduler(); - - /** - * @functor Pointer to the functor. Lifecycle of this functor should be maintained by the caller. - * @task_name Name of the task. Task name would be very helpful at debug site. - * @delay The amount of time to wait before the first shot. - * @interval The interval between each fire-shot. If it is 0ms, callable will just fired once. - */ - std::uintptr_t schedule(const std::function& functor, const std::string& task_name, - std::chrono::milliseconds delay, std::chrono::milliseconds interval) - LOCKS_EXCLUDED(tasks_mtx_); + virtual ~Scheduler() = default; - /** - * Note: - * Periodic tasks should be explicitly cancelled once they are no longer needed. - */ - void cancel(std::uintptr_t task_id) LOCKS_EXCLUDED(tasks_mtx_); + virtual void start() = 0; -private: - static void onTrigger(void* arg, grpc_error_handle error); + virtual void shutdown() = 0; - void clear(TimerTask* timer_task) LOCKS_EXCLUDED(tasks_mtx_); + virtual std::uint32_t schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) = 0; - absl::flat_hash_set tasks_ GUARDED_BY(tasks_mtx_); - absl::Mutex tasks_mtx_; + virtual void cancel(std::uint32_t task_id) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h new file mode 100644 index 000000000..f5f65040e --- /dev/null +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -0,0 +1,66 @@ +#pragma once +#include "Scheduler.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "asio.hpp" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct TimerTask { + std::function callback; + std::chrono::milliseconds interval; + std::string task_name; +}; + +class SchedulerImpl : public Scheduler { +public: + SchedulerImpl(); + + ~SchedulerImpl() override = default; + + void start() override; + + void shutdown() override LOCKS_EXCLUDED(tasks_mtx_); + + /** + * @functor Pointer to the functor. Lifecycle of this functor should be maintained by the caller. + * @task_name Name of the task. Task name would be very helpful at debug site. + * @delay The amount of time to wait before the first shot. + * @interval The interval between each fire-shot. If it is 0ms, callable will just fired once. + */ + std::uint32_t schedule(const std::function& functor, const std::string& task_name, + std::chrono::milliseconds delay, std::chrono::milliseconds interval) override + LOCKS_EXCLUDED(tasks_mtx_); + + /** + * Note: + * Periodic tasks should be explicitly cancelled once they are no longer needed. + */ + void cancel(std::uint32_t task_id) override LOCKS_EXCLUDED(tasks_mtx_); + +private: + asio::io_context context_; + std::unique_ptr> work_guard_; + absl::Mutex start_mtx_; + absl::CondVar start_cv_; + std::vector threads_; + std::atomic state_{State::CREATED}; + + absl::flat_hash_map> tasks_ GUARDED_BY(tasks_mtx_); + absl::Mutex tasks_mtx_; + + static void execute(const asio::error_code& ec, asio::steady_timer* timer, std::weak_ptr task); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index fc9729a21..cd6703493 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -28,8 +28,9 @@ class RpcClientTest : public ::testing::Test { server_authorization_check_config_ = std::make_shared( std::make_shared()); std::vector pem_list; - grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, - .certificate_chain = TlsHelper::client_certificate_chain}; + grpc::experimental::IdentityKeyCertPair pair{}; + pair.private_key = TlsHelper::client_private_key; + pair.certificate_chain = TlsHelper::client_certificate_chain; pem_list.emplace_back(pair); certificate_provider_ = std::make_shared(TlsHelper::CA, pem_list); diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index 06156110f..80d0dee16 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -18,6 +18,19 @@ class TopAddressingTest : public testing::Test { } void TearDown() override { grpc_shutdown(); } + + void SetEnv(const char* key, const char* value) { + int overwrite = 1; + #ifdef _WIN32 + std::string env; + env.append(key); + env.push_back('='); + env.append(value); + _putenv(env.c_str()); + #else + setenv(key, value, overwrite); + #endif + } }; TEST_F(TopAddressingTest, testFetchNameServerAddresses) { @@ -51,9 +64,8 @@ TEST_F(TopAddressingTest, testFetchNameServerAddresses) { } TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { - int override = 1; - setenv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center", override); - setenv(HostInfo::ENV_LABEL_STAGE, "DAILY", override); + SetEnv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center"); + SetEnv(HostInfo::ENV_LABEL_STAGE, "DAILY"); std::vector list; absl::Mutex mtx; diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index ce3257417..488a22703 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -18,8 +18,9 @@ class TopicPublishInfoTest : public ::testing::Test { tls_channel_credential_option_.set_server_verification_option(GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); std::vector pem_list; - grpc::experimental::IdentityKeyCertPair pair{.private_key = TlsHelper::client_private_key, - .certificate_chain = TlsHelper::client_certificate_chain}; + grpc::experimental::IdentityKeyCertPair pair{}; + pair.private_key = TlsHelper::client_private_key; + pair.certificate_chain = TlsHelper::client_certificate_chain; pem_list.emplace_back(pair); certificate_provider_ = std::make_shared(TlsHelper::CA, pem_list); diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index 85a14da32..cecd4f72c 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -42,6 +42,7 @@ cc_test( "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", "@zlib//:zlib", + "@asio//:asio", ], ) @@ -131,7 +132,7 @@ cc_test( ], deps = [ "//api:rocketmq_interface", - "@com_github_progschj_ThreadPool//:ThreadPool", + "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp index a70aa47ef..a04133f83 100644 --- a/src/test/cpp/ut/base/HostInfoTest.cpp +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -17,14 +17,26 @@ class HostInfoTest : public testing::Test { std::string unit_{"unit"}; std::string app_{"app"}; std::string stage_{"stage"}; + + void SetEnv(const char* key, const char* value) { + int overwrite = 1; + #ifdef _WIN32 + std::string env; + env.append(key); + env.push_back('='); + env.append(value); + _putenv(env.c_str()); + #else + setenv(key, value, overwrite); + #endif + } }; TEST_F(HostInfoTest, testQueryString) { - int overwrite = 1; - setenv(HostInfo::ENV_LABEL_SITE, site_.c_str(), overwrite); - setenv(HostInfo::ENV_LABEL_UNIT, unit_.c_str(), overwrite); - setenv(HostInfo::ENV_LABEL_APP, app_.c_str(), overwrite); - setenv(HostInfo::ENV_LABEL_STAGE, stage_.c_str(), overwrite); + SetEnv(HostInfo::ENV_LABEL_SITE, site_.c_str()); + SetEnv(HostInfo::ENV_LABEL_UNIT, unit_.c_str()); + SetEnv(HostInfo::ENV_LABEL_APP, app_.c_str()); + SetEnv(HostInfo::ENV_LABEL_STAGE, stage_.c_str()); HostInfo host_info; std::string query_string = host_info.queryString(); diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp index 675a12792..ebbddae03 100644 --- a/src/test/cpp/ut/base/ThreadPoolTest.cpp +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -1,29 +1,58 @@ -#include "ThreadPool.h" -#include "gtest/gtest.h" -#include "rocketmq/RocketMQ.h" +#include "ThreadPoolImpl.h" #include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" +#include +#include +#include ROCKETMQ_NAMESPACE_BEGIN class ThreadPoolTest : public testing::Test { public: + ThreadPoolTest() = default; + void SetUp() override { - pool_ = absl::make_unique(2); + pool_ = absl::make_unique(2); + pool_->start(); + completed = false; } - + + void TearDown() override { pool_->shutdown(); } + protected: std::unique_ptr pool_; + absl::Mutex mtx; + absl::CondVar cv; + bool completed{false}; }; TEST_F(ThreadPoolTest, testBasics) { - auto task = [](int cnt) { + + auto task = [this](int cnt) { for (int i = 0; i < cnt; i++) { - std::cout << "It works" << std::endl; + std::cout << std::this_thread::get_id() << ": It works" << std::endl; + } + { + absl::MutexLock lk(&mtx); + if (!completed) { + completed = true; + cv.SignalAll(); + } } }; - pool_->enqueue(task, 3); -} -ROCKETMQ_NAMESPACE_END + for (int i = 0; i < 3; i++) { + pool_->submit(std::bind(task, 3)); + } + { + absl::MutexLock lk(&mtx); + if (!completed) { + cv.Wait(&mtx); + } + } +} +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index add0c2812..74da9c54b 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -1,5 +1,7 @@ #include "UtilAll.h" +#include "MixAll.h" #include "absl/strings/str_split.h" +#include "asio.hpp" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" @@ -56,4 +58,16 @@ TEST_F(UtilAllTest, split) { EXPECT_EQ(expected, segments); } +TEST_F(UtilAllTest, macAddrss) { + std::vector mac; + bool success = UtilAll::macAddress(mac); + ASSERT_TRUE(success); + std::cout << MixAll::hex(mac.data(), mac.size()) << std::endl; +} + +TEST_F(UtilAllTest, testAsioGetHostName) { + auto&& host_name = asio::ip::host_name(); + std::cout << host_name << std::endl; +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 8cfdd6bb7..a243de1b1 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -2,7 +2,7 @@ #include "ClientManagerFactory.h" #include "ClientManagerMock.h" #include "HttpClientMock.h" -#include "Scheduler.h" +#include "SchedulerImpl.h" #include "TopAddressing.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" @@ -24,19 +24,23 @@ class ClientImplTest : public testing::Test { public: void SetUp() override { grpc_init(); + scheduler_.start(); client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); client_ = std::make_shared(group_); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + scheduler_.shutdown(); + } protected: std::string resource_namespace_{"mq://test"}; std::string group_{"Group-0"}; std::shared_ptr> client_manager_; - Scheduler scheduler_; + SchedulerImpl scheduler_; std::shared_ptr client_; }; diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 33504a4c3..26600a7d6 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -26,8 +26,23 @@ TEST_F(CredentialsProviderTest, testEnvironmentVariable) { const char* access_key = "abc"; const char* access_secret = "def"; +#ifdef _WIN32 + std::string env_access_key; + env_access_key.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY); + env_access_key.push_back('='); + env_access_key.append(access_key); + + _putenv(env_access_key.c_str()); + + std::string env_access_secret; + env_access_secret.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET); + env_access_secret.push_back('='); + env_access_secret.append(access_secret); + _putenv(env_access_secret.c_str()); +#else setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY, access_key, 1); setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET, access_secret, 1); +#endif EnvironmentVariablesCredentialsProvider provider; const Credentials& credentials = provider.getCredentials(); diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index d9c76555b..23f7ae70f 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -1,6 +1,7 @@ #include "ProducerImpl.h" #include "ClientManagerFactory.h" #include "ClientManagerMock.h" +#include "SchedulerImpl.h" #include "TopicRouteData.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/MQMessage.h" @@ -63,14 +64,17 @@ class ProducerImplTest : public testing::Test { }; TEST_F(ProducerImplTest, testStartShutdown) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); producer_->start(); producer_->shutdown(); + scheduler.shutdown(); } TEST_F(ProducerImplTest, testSend) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -98,10 +102,12 @@ TEST_F(ProducerImplTest, testSend) { producer_->send(message); EXPECT_TRUE(cb_invoked); producer_->shutdown(); + scheduler.shutdown(); } TEST_F(ProducerImplTest, testSend_WithMessageGroup) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -129,6 +135,7 @@ TEST_F(ProducerImplTest, testSend_WithMessageGroup) { producer_->send(message, message_group_); EXPECT_TRUE(cb_invoked); producer_->shutdown(); + scheduler.shutdown(); } class TestMessageQueueSelector : public MessageQueueSelector { @@ -139,7 +146,8 @@ class TestMessageQueueSelector : public MessageQueueSelector { }; TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -169,6 +177,7 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { producer_->send(message, selector.get(), nullptr); EXPECT_TRUE(cb_invoked); producer_->shutdown(); + scheduler.shutdown(); } class TestSendCallback : public SendCallback { @@ -192,7 +201,8 @@ class TestSendCallback : public SendCallback { }; TEST_F(ProducerImplTest, testAsyncSend) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -232,6 +242,7 @@ TEST_F(ProducerImplTest, testAsyncSend) { } EXPECT_TRUE(cb_invoked); + scheduler.shutdown(); producer_->shutdown(); } diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 86cee143c..a6db0ca4c 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -18,6 +18,7 @@ class PullConsumerImplTest : public testing::Test { public: void SetUp() override { grpc_init(); + scheduler_.start(); client_manager_ = std::make_shared>(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); @@ -39,7 +40,10 @@ class PullConsumerImplTest : public testing::Test { } } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + scheduler_.shutdown(); + } protected: std::string resource_namespace_{"mq://test"}; @@ -49,7 +53,7 @@ class PullConsumerImplTest : public testing::Test { std::string tag_{"TagB"}; std::shared_ptr> client_manager_; std::shared_ptr pull_consumer_; - Scheduler scheduler_; + SchedulerImpl scheduler_; std::string broker_name_{"broker-a"}; int broker_id_{0}; std::string message_body_{"Message Body Content"}; diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 3b4e915d4..d98ef518d 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -51,7 +51,8 @@ class PushConsumerImplTest : public testing::Test { }; TEST_F(PushConsumerImplTest, testAck) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, @@ -88,10 +89,12 @@ TEST_F(PushConsumerImplTest, testAck) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); + scheduler.shutdown(); } TEST_F(PushConsumerImplTest, testNack) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, @@ -126,10 +129,12 @@ TEST_F(PushConsumerImplTest, testNack) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); + scheduler.shutdown(); } TEST_F(PushConsumerImplTest, testForward) { - Scheduler scheduler; + SchedulerImpl scheduler; + scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); InvocationContext invocation_context; @@ -173,6 +178,7 @@ TEST_F(PushConsumerImplTest, testForward) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); + scheduler.shutdown(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 0c14df83c..16fd55fab 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -4,61 +4,106 @@ #include #include -#include "Scheduler.h" -#include "grpc/grpc.h" +#include "SchedulerImpl.h" #include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN class SchedulerTest : public testing::Test { public: - void SetUp() override { - grpc_init(); - callback_fire_count = 0; - - callback = [this]() { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - }; - } - - void TearDown() override { grpc_shutdown(); } + void SetUp() override {} protected: - Scheduler scheduler; +}; + +TEST_F(SchedulerTest, testSingleShot) { + SchedulerImpl scheduler; + scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; - std::function callback; -}; - -TEST_F(SchedulerTest, testSingleShot) { scheduler.schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); // Wait till callback is executed. { absl::MutexLock lock(&mtx); - EXPECT_TRUE(!cv.WaitWithTimeout(&mtx, absl::Seconds(3))); + if (!callback_fire_count) { + cv.Wait(&mtx); + } } + + scheduler.shutdown(); } TEST_F(SchedulerTest, testCancel) { - std::uintptr_t task_id = - scheduler.schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); + SchedulerImpl scheduler; + scheduler.start(); + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + std::uint32_t task_id = scheduler.schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); scheduler.cancel(task_id); std::this_thread::sleep_for(std::chrono::seconds(2)); ASSERT_EQ(0, callback_fire_count); + scheduler.shutdown(); } TEST_F(SchedulerTest, testPeriodicShot) { + SchedulerImpl scheduler; + scheduler.start(); + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + std::uintptr_t task_id = scheduler.schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::seconds(1)); // Wait till callback is executed. std::this_thread::sleep_for(std::chrono::seconds(5)); ASSERT_TRUE(callback_fire_count >= 4); scheduler.cancel(task_id); + scheduler.shutdown(); +} + +TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { + SchedulerImpl scheduler; + scheduler.start(); + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + }; + + scheduler.schedule(callback, "single-shot-with-0-delay", std::chrono::milliseconds(0), std::chrono::milliseconds(0)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + if (!callback_fire_count) { + cv.Wait(&mtx); + } + } + + scheduler.shutdown(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/third_party/asio.BUILD b/third_party/asio.BUILD new file mode 100644 index 000000000..af74274ce --- /dev/null +++ b/third_party/asio.BUILD @@ -0,0 +1,15 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "asio", + hdrs = glob(["include/**/*.hpp", "include/**/*.ipp"]), + visibility = ["//visibility:public"], + strip_include_prefix = "include", + defines = [ + "ASIO_STANDALONE", + "ASIO_HAS_STD_ADDRESSOF", + "ASIO_HAS_STD_ARRAY", + "ASIO_HAS_CSTDINT", + "ASIO_HAS_STD_SHARED_PTR", + "ASIO_HAS_STD_TYPE_TRAITS", + ], +) \ No newline at end of file diff --git a/third_party/curl.BUILD b/third_party/curl.BUILD new file mode 100644 index 000000000..4b3965794 --- /dev/null +++ b/third_party/curl.BUILD @@ -0,0 +1,35 @@ +load("@org_apache_rocketmq//bazel:curl.bzl", "CURL_COPTS") + +package(features = ["no_copts_tokenization"]) + +config_setting( + name = "windows", + values = {"cpu": "x64_windows"}, + visibility = ["//visibility:private"], +) + +config_setting( + name = "osx", + values = {"cpu": "darwin"}, + visibility = ["//visibility:private"], +) + +cc_library( + name = "curl", + srcs = glob([ + "lib/**/*.c", + ]), + hdrs = glob([ + "include/curl/*.h", + "lib/**/*.h", + ]), + copts = CURL_COPTS + [ + "-DOS=\"os\"", + "-DCURL_EXTERN_SYMBOL=__attribute__((__visibility__(\"default\")))", + ], + includes = [ + "include/", + "lib/", + ], + visibility = ["//visibility:public"], +) From a7e76259bdf0211f9488c8d55e415c937056337e Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 16 Sep 2021 14:02:52 +0800 Subject: [PATCH 057/133] Add cpp_httplib-based http-client (#19) Use httplib based HTTP client --- bazel/rocketmq_deps.bzl | 9 +- src/main/cpp/base/BUILD.bazel | 52 +----- src/main/cpp/base/GHttpClient.cpp | 164 ------------------ src/main/cpp/base/HttpClientImpl.cpp | 76 ++++++++ src/main/cpp/base/TopAddressing.cpp | 9 +- src/main/cpp/base/include/GHttpClient.h | 67 ------- src/main/cpp/base/include/HttpClient.h | 14 +- src/main/cpp/base/include/HttpClientImpl.h | 33 ++++ .../cpp/base/mocks/include/HttpClientMock.h | 10 +- src/main/cpp/rocketmq/ClientImpl.cpp | 26 +-- src/main/cpp/rocketmq/CredentialsProvider.cpp | 26 ++- .../include/StsCredentialsProviderImpl.h | 14 +- src/test/cpp/it/BUILD.bazel | 1 - src/test/cpp/it/TopAddressingTest.cpp | 19 +- src/test/cpp/ut/base/BUILD.bazel | 7 +- src/test/cpp/ut/base/GHttpClientTest.cpp | 75 -------- src/test/cpp/ut/base/HttpClientTest.cpp | 50 ++++++ src/test/cpp/ut/base/TopAddressingTest.cpp | 16 +- src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 10 +- .../StsCredentialsProviderImplTest.cpp | 13 +- third_party/asio.BUILD | 4 +- third_party/cpp_httplib.BUILD | 17 ++ 22 files changed, 273 insertions(+), 439 deletions(-) delete mode 100644 src/main/cpp/base/GHttpClient.cpp create mode 100644 src/main/cpp/base/HttpClientImpl.cpp delete mode 100644 src/main/cpp/base/include/GHttpClient.h create mode 100644 src/main/cpp/base/include/HttpClientImpl.h delete mode 100644 src/test/cpp/ut/base/GHttpClientTest.cpp create mode 100644 src/test/cpp/ut/base/HttpClientTest.cpp create mode 100644 third_party/cpp_httplib.BUILD diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index a17955973..acee2ac11 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -119,11 +119,10 @@ def rocketmq_deps(): ], ) - # Curl library maybe( http_archive, - name = "com_github_curl", - build_file = "@org_apache_rocketmq//third_party:curl.BUILD", - strip_prefix = "curl-master", - urls = ["https://github.com/curl/curl/archive/master.zip"], + name = "com_github_yhirose_cpp_httplib", + build_file = "@org_apache_rocketmq//third_party:cpp_httplib.BUILD", + strip_prefix = "cpp-httplib-0.9.4", + urls = ["https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.9.4.tar.gz"], ) \ No newline at end of file diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 88aecd04d..1d99854ed 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -2,51 +2,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) -cc_library( - name = "http_client_interface", - hdrs = [ - "include/HttpClient.h", - ], - strip_include_prefix = "//src/main/cpp/base/include", - deps = [ - "//api:rocketmq_interface", - "@com_google_absl//absl/container:flat_hash_map", - ], -) - -cc_library( - name = "ghttp_client", - hdrs = [ - "include/GHttpClient.h", - ], - srcs = [ - "GHttpClient.cpp", - ], - deps = [ - ":http_client_interface", - "//src/main/cpp/log:log_library", - "@com_github_fmtlib_fmt//:fmtlib", - "@com_github_grpc_grpc//:grpc", - ], - strip_include_prefix = "//src/main/cpp/base/include", -) - -cc_library( - name = "top_addressing", - hdrs = [ - "include/TopAddressing.h", - "include/HostInfo.h" - ], - srcs = [ - "TopAddressing.cpp", - "HostInfo.cpp", - ], - strip_include_prefix = "//src/main/cpp/base/include", - deps = [ - ":ghttp_client", - ], -) - cc_library( name = "base_library", hdrs = glob(["include/*.h"]), @@ -54,16 +9,21 @@ cc_library( strip_include_prefix = "//src/main/cpp/base/include", deps = [ "//src/main/cpp/log:log_library", + "@com_github_fmtlib_fmt//:fmtlib", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/random", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_github_gabime_spdlog//:spdlog", "@com_github_grpc_grpc//:grpc", "@com_googlesource_code_re2//:re2", - "@zlib//:zlib", "@boringssl//:crypto", "@boringssl//:ssl", "//external:madler_zlib", + "@com_github_yhirose_cpp_httplib//:cpp_httplib", "@asio//:asio", ], ) \ No newline at end of file diff --git a/src/main/cpp/base/GHttpClient.cpp b/src/main/cpp/base/GHttpClient.cpp deleted file mode 100644 index e4e1c871c..000000000 --- a/src/main/cpp/base/GHttpClient.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "GHttpClient.h" -#include "absl/strings/str_join.h" -#include "fmt/format.h" -#include -#include -#include -#include -#include -#include -#include - -#include "LoggerImpl.h" - -ROCKETMQ_NAMESPACE_BEGIN - -GHttpClient::GHttpClient() : shutdown_(false) { - grpc_core::ExecCtx exec_ctx; - grpc_httpcli_context_init(&http_context_); - grpc_pollset* pollset = static_cast(gpr_zalloc(grpc_pollset_size())); - grpc_pollset_init(pollset, &http_mtx_); - http_polling_entity_ = grpc_polling_entity_create_from_pollset(pollset); - GRPC_CLOSURE_INIT(&destroy_, &GHttpClient::destroyPollingEntity, &http_polling_entity_, grpc_schedule_on_exec_ctx); -} - -GHttpClient::~GHttpClient() { - SPDLOG_INFO("GHttpClient::~GHttpClient() starts"); - shutdown(); - grpc_core::ExecCtx exec_ctx; - grpc_httpcli_context_destroy(&http_context_); - grpc_pollset_shutdown(grpc_polling_entity_pollset(&http_polling_entity_), &destroy_); - SPDLOG_INFO("GHttpClient::~GHttpClient() completed"); -} - -const int64_t GHttpClient::POLL_INTERVAL = 1000; - -const int GHttpClient::STATUS_OK = 200; - -void GHttpClient::poll() { - grpc_core::ExecCtx exec_ctx; - while (!shutdown_) { - gpr_mu_lock(http_mtx_); - grpc_error_handle error = grpc_pollset_work(grpc_polling_entity_pollset(&http_polling_entity_), &worker_, - grpc_core::ExecCtx::Get()->Now() + POLL_INTERVAL); - gpr_mu_unlock(http_mtx_); - if (error) { - SPDLOG_WARN("grpc_pollset_work failed"); - } else { - SPDLOG_TRACE("grpc_pollset_work returned. grpc_pollset_size: {}", grpc_pollset_size()); - } - submit0(); - } - SPDLOG_INFO("GHttpClient::poll completed"); -} - -void GHttpClient::start() { - SPDLOG_INFO("GHttpClient::start()"); - if (!shutdown_) { - loop_ = std::thread(std::bind(&GHttpClient::poll, this)); - SPDLOG_INFO("GHttpClient starts to poll"); - } -} - -void GHttpClient::shutdown() { - bool expected = false; - if (shutdown_.compare_exchange_strong(expected, true, std::memory_order_relaxed)) { - SPDLOG_INFO("GHttpClient::shutdown()"); - if (loop_.joinable()) { - loop_.join(); - SPDLOG_INFO("GHttpClient#loop thread quit OK"); - } - } -} - -void GHttpClient::get( - HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) { - auto http_invocation_context = new HttpInvocationContext(); - std::string http_host = fmt::format("{}:{}", host, port); - http_invocation_context->host = http_host; - http_invocation_context->path = path; - http_invocation_context->request.host = const_cast(http_invocation_context->host.c_str()); - http_invocation_context->request.http.path = const_cast(http_invocation_context->path.c_str()); - http_invocation_context->request.handshaker = &grpc_httpcli_plaintext; - http_invocation_context->callback = cb; - - { - absl::MutexLock lk(&pending_requests_mtx_); - pending_requests_.emplace_back(http_invocation_context); - SPDLOG_TRACE("Add HTTP request to pending list"); - } - - { - grpc_core::ExecCtx exec_ctx; - SPDLOG_TRACE("Prepare to grpc_pollset_kick"); - { - gpr_mu_lock(http_mtx_); - grpc_error_handle error = grpc_pollset_kick(grpc_polling_entity_pollset(&http_polling_entity_), nullptr); - gpr_mu_unlock(http_mtx_); - if (GRPC_ERROR_NONE != error) { - SPDLOG_WARN("grpc_pollset_kick failed"); - } else { - SPDLOG_TRACE("grpc_pollset_kick completed"); - } - } - } -} - -void GHttpClient::submit0() { - absl::MutexLock lk(&pending_requests_mtx_); - if (pending_requests_.empty()) { - SPDLOG_TRACE("No pending HTTP requests"); - return; - } - - SPDLOG_DEBUG("Add {} pending HTTP requests to pollset", pending_requests_.size()); - grpc_core::ExecCtx exec_ctx; - { - for (auto it = pending_requests_.begin(); it != pending_requests_.end();) { - auto http_invocation_context = *it; - SPDLOG_TRACE("Prepare to create quota"); - grpc_resource_quota* resource_quota = grpc_resource_quota_create("get"); - SPDLOG_TRACE("Quota created"); - SPDLOG_TRACE("grpc_httpcli_get starts"); - grpc_httpcli_get( - &http_context_, &http_polling_entity_, resource_quota, &http_invocation_context->request, - grpc_core::ExecCtx::Get()->Now() + absl::ToInt64Milliseconds(absl::Seconds(3)), - GRPC_CLOSURE_CREATE(&GHttpClient::onCompletion, http_invocation_context, grpc_schedule_on_exec_ctx), - &http_invocation_context->response); - SPDLOG_TRACE("grpc_httpcli_get completed"); - grpc_resource_quota_unref_internal(resource_quota); - SPDLOG_TRACE("Resource quota unref completed"); - it = pending_requests_.erase(it); - } - } -} - -void GHttpClient::destroyPollingEntity(void* arg, grpc_error_handle error) { - auto polling_entity = reinterpret_cast(arg); - grpc_pollset_destroy(grpc_polling_entity_pollset(polling_entity)); -} - -void GHttpClient::onCompletion(void* arg, grpc_error_handle error) { - auto context = reinterpret_cast(arg); - absl::flat_hash_map metadata; - - if (error) { - context->callback(500, metadata, std::string()); - SPDLOG_WARN("HTTP request failed"); - delete context; - return; - } - - for (size_t i = 0; i < context->response.hdr_count; i++) { - grpc_http_header* hdr = context->response.hdrs + i; - metadata.insert({std::string(hdr->key), std::string(hdr->value)}); - } - std::string body(context->response.body, context->response.body_length); - SPDLOG_DEBUG("HTTP response received. Code: {}, response-headers: {}, response-body: {}", context->response.status, - absl::StrJoin(metadata, ",", absl::PairFormatter("=")), body); - context->callback(context->response.status, metadata, body); - delete context; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/HttpClientImpl.cpp b/src/main/cpp/base/HttpClientImpl.cpp new file mode 100644 index 000000000..e4fbfa512 --- /dev/null +++ b/src/main/cpp/base/HttpClientImpl.cpp @@ -0,0 +1,76 @@ +#include "HttpClientImpl.h" + +#include +#include + +#include "fmt/format.h" +#include "spdlog/spdlog.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +HttpClientImpl::HttpClientImpl() {} + +HttpClientImpl::~HttpClientImpl() {} + +void HttpClientImpl::start() {} + +void HttpClientImpl::shutdown() {} + +/** + * @brief We current implement this function in sync mode since async http request in CURL is sort of unnecessarily + * complex. + * + * @param protocol + * @param host + * @param port + * @param path + * @param cb + */ +void HttpClientImpl::get( + HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) { + + std::string key; + switch (protocol) { + case HttpProtocol::HTTP: + key = fmt::format("http://{}:{}", host, port); + break; + case HttpProtocol::HTTPS: + key = fmt::format("https://{}:{}", host, port); + break; + } + + std::shared_ptr client; + { + absl::MutexLock lk(&clients_mtx_); + if (clients_.contains(key)) { + client = clients_[key]; + } + + if (!client || !client->is_valid()) { + client = std::make_shared(key); + clients_.insert_or_assign(key, client); + } + } + + if (!client || !client->is_valid()) { + int code = 400; + std::multimap headers; + std::string response; + cb(code, headers, response); + return; + } + + auto res = client->Get(path.c_str()); + + std::multimap headers; + for (auto& header : headers) { + headers.insert({header.first, header.second}); + } + + cb(res->status, headers, res->body); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index 17e36cee6..0c2196920 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -1,6 +1,6 @@ #include "TopAddressing.h" -#include "GHttpClient.h" +#include "HttpClientImpl.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_split.h" @@ -12,7 +12,7 @@ ROCKETMQ_NAMESPACE_BEGIN TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") {} TopAddressing::TopAddressing(std::string host, int port, std::string path) - : host_(std::move(host)), port_(port), path_(std::move(path)), http_client_(absl::make_unique()) { + : host_(std::move(host)), port_(port), path_(std::move(path)), http_client_(absl::make_unique()) { http_client_->start(); } @@ -36,10 +36,9 @@ void TopAddressing::fetchNameServerAddresses(const std::function& metadata, - const std::string& body) { + auto callback = [cb](int code, const std::multimap& metadata, const std::string& body) { SPDLOG_DEBUG("Receive HTTP response. Code: {}, body: {}", code, body); - if (GHttpClient::STATUS_OK == code) { + if (static_cast(HttpStatus::OK) == code) { cb(true, absl::StrSplit(body, ';')); } else { std::vector name_server_list; diff --git a/src/main/cpp/base/include/GHttpClient.h b/src/main/cpp/base/include/GHttpClient.h deleted file mode 100644 index 0cfcbc10a..000000000 --- a/src/main/cpp/base/include/GHttpClient.h +++ /dev/null @@ -1,67 +0,0 @@ -#include "rocketmq/RocketMQ.h" - -#include "HttpClient.h" -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/iomgr/iomgr.h" -#include -#include -#include -#include -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -struct HttpInvocationContext { - HttpInvocationContext() { memset(&request, 0, sizeof(request)); } - std::string host; - std::string path; - grpc_httpcli_request request; - grpc_http_response response; - std::function&, const std::string&)> callback; -}; - -class GHttpClient : public HttpClient { -public: - GHttpClient(); - - ~GHttpClient() override; - - void start() override; - - void shutdown() override; - - void get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) - override; - - static const int STATUS_OK; - -private: - static void onCompletion(void* arg, grpc_error_handle error); - - static void destroyPollingEntity(void* arg, grpc_error_handle error); - - void poll(); - - void submit0() LOCKS_EXCLUDED(pending_requests_mtx_); - - grpc_httpcli_context http_context_; - grpc_polling_entity http_polling_entity_; - gpr_mu* http_mtx_{nullptr}; - - std::thread loop_; - grpc_pollset_worker* worker_; - grpc_closure destroy_; - std::atomic_bool shutdown_; - - std::vector pending_requests_ GUARDED_BY(pending_requests_mtx_); - absl::Mutex pending_requests_mtx_; - - static const int64_t POLL_INTERVAL; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h index 84b35e747..4ab94761b 100644 --- a/src/main/cpp/base/include/HttpClient.h +++ b/src/main/cpp/base/include/HttpClient.h @@ -1,8 +1,11 @@ #pragma once -#include "absl/container/flat_hash_map.h" -#include "rocketmq/RocketMQ.h" + #include #include +#include +#include + +#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -11,6 +14,11 @@ enum class HttpProtocol : int8_t { HTTPS = 2, }; +enum class HttpStatus : int { + OK = 200, + INTERNAL = 500, +}; + class HttpClient { public: virtual ~HttpClient() = default; @@ -21,7 +29,7 @@ class HttpClient { virtual void get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) = 0; + const std::function&, const std::string&)>& cb) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h new file mode 100644 index 000000000..8e42a3315 --- /dev/null +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -0,0 +1,33 @@ +#pragma once + +#include "HttpClient.h" + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "httplib.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientImpl : public HttpClient { +public: + HttpClientImpl(); + + ~HttpClientImpl() override; + + void start() override; + + void shutdown() override; + + void + get(HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, + const std::function&, const std::string&)>& cb) override; + +private: + absl::flat_hash_map> clients_ GUARDED_BY(clients_mtx_); + absl::Mutex clients_mtx_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/mocks/include/HttpClientMock.h b/src/main/cpp/base/mocks/include/HttpClientMock.h index 392756ada..6b76cb429 100644 --- a/src/main/cpp/base/mocks/include/HttpClientMock.h +++ b/src/main/cpp/base/mocks/include/HttpClientMock.h @@ -1,19 +1,21 @@ #pragma once -#include "gmock/gmock.h" #include "HttpClient.h" #include "rocketmq/RocketMQ.h" +#include "gmock/gmock.h" ROCKETMQ_NAMESPACE_BEGIN class HttpClientMock : public HttpClient { public: ~HttpClientMock() override = default; - + MOCK_METHOD(void, start, (), (override)); MOCK_METHOD(void, shutdown, (), (override)); - MOCK_METHOD(void, get, (HttpProtocol, const std::string&, std::uint16_t, const std::string&, - const std::function&, const std::string&)>&), (override)); + MOCK_METHOD(void, get, + (HttpProtocol, const std::string&, std::uint16_t, const std::string&, + const std::function&, const std::string&)>&), + (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index d91250aed..4bbc90f15 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,14 +1,3 @@ -#include "ClientImpl.h" -#include "ClientManagerFactory.h" -#include "GHttpClient.h" -#include "InvocationContext.h" -#include "LoggerImpl.h" -#include "MessageAccessor.h" -#include "Signature.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "rocketmq/MQMessageExt.h" #include #include #include @@ -17,6 +6,19 @@ #include #include +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "apache/rocketmq/v1/definition.pb.h" + +#include "ClientImpl.h" +#include "ClientManagerFactory.h" +#include "HttpClientImpl.h" +#include "InvocationContext.h" +#include "LoggerImpl.h" +#include "MessageAccessor.h" +#include "Signature.h" +#include "rocketmq/MQMessageExt.h" + ROCKETMQ_NAMESPACE_BEGIN ClientImpl::ClientImpl(std::string group_name) : ClientConfigImpl(std::move(group_name)), state_(State::CREATED) {} @@ -180,7 +182,7 @@ void ClientImpl::renewNameServerList() { std::vector list; SPDLOG_DEBUG("Begin to renew name server list"); auto callback = [this](int code, const std::vector& name_server_list) { - if (GHttpClient::STATUS_OK != code) { + if (static_cast(HttpStatus::OK) != code) { SPDLOG_WARN("Failed to fetch name server list"); return; } diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index 942ecb361..3d7992671 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -1,18 +1,19 @@ -#include "GHttpClient.h" -#include "MixAll.h" -#include "StsCredentialsProviderImpl.h" +#include +#include +#include +#include + #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "fmt/format.h" #include "ghc/filesystem.hpp" #include "google/protobuf/struct.pb.h" #include "google/protobuf/util/json_util.h" -#include "rocketmq/Logger.h" #include "spdlog/spdlog.h" -#include -#include -#include -#include + +#include "MixAll.h" +#include "StsCredentialsProviderImpl.h" +#include "rocketmq/Logger.h" ROCKETMQ_NAMESPACE_BEGIN @@ -100,9 +101,7 @@ StsCredentialsProvider::StsCredentialsProvider(std::string ram_role_name) Credentials StsCredentialsProvider::getCredentials() { return impl_->getCredentials(); } StsCredentialsProviderImpl::StsCredentialsProviderImpl(std::string ram_role_name) - : ram_role_name_(std::move(ram_role_name)), http_client_(absl::make_unique()) { - http_client_->start(); -} + : ram_role_name_(std::move(ram_role_name)) {} StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { http_client_->shutdown(); } @@ -122,10 +121,9 @@ void StsCredentialsProviderImpl::refresh() { absl::Mutex sync_mtx; absl::CondVar sync_cv; bool completed = false; - auto callback = [&, this](int code, const absl::flat_hash_map& headers, - const std::string& body) { + auto callback = [&, this](int code, const std::multimap& headers, const std::string& body) { SPDLOG_DEBUG("Received STS response. Code: {}", code); - if (GHttpClient::STATUS_OK == code) { + if (static_cast(HttpStatus::OK) == code) { google::protobuf::Struct doc; google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(body, &doc); if (status.ok()) { diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h index d936195a1..9429355e5 100644 --- a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -2,8 +2,9 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" -#include "rocketmq/CredentialsProvider.h" + #include "HttpClient.h" +#include "rocketmq/CredentialsProvider.h" ROCKETMQ_NAMESPACE_BEGIN @@ -15,14 +16,7 @@ class StsCredentialsProviderImpl : public CredentialsProvider { Credentials getCredentials() override; - /** - * @brief Expose http_client for test purpose only. - * - * @param http_client - */ - void setHttpClient(std::unique_ptr http_client) { - http_client_ = std::move(http_client); - } + void withHttpClient(std::unique_ptr http_client) { http_client_ = std::move(http_client); } private: static const char* RAM_ROLE_HOST; @@ -32,7 +26,7 @@ class StsCredentialsProviderImpl : public CredentialsProvider { static const char* FIELD_SESSION_TOKEN; static const char* FIELD_EXPIRATION; static const char* EXPIRATION_DATE_TIME_FORMAT; - + std::string ram_role_name_; std::string access_key_ GUARDED_BY(mtx_); diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index 6bc10df54..c2e919089 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -5,7 +5,6 @@ cc_test( "TopAddressingTest.cpp", ], deps = [ - "//src/main/cpp/base:top_addressing", "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index 80d0dee16..91873d4ee 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -1,13 +1,16 @@ -#include "TopAddressing.h" -#include "RateLimiter.h" -#include "grpc/grpc.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" #include #include #include #include +#include "absl/synchronization/mutex.h" +#include "grpc/grpc.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + +#include "RateLimiter.h" +#include "TopAddressing.h" + ROCKETMQ_NAMESPACE_BEGIN class TopAddressingTest : public testing::Test { @@ -21,15 +24,15 @@ class TopAddressingTest : public testing::Test { void SetEnv(const char* key, const char* value) { int overwrite = 1; - #ifdef _WIN32 +#ifdef _WIN32 std::string env; env.append(key); env.push_back('='); env.append(value); _putenv(env.c_str()); - #else +#else setenv(key, value, overwrite); - #endif +#endif } }; diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index cecd4f72c..3e60c186f 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -47,12 +47,12 @@ cc_test( ) cc_test( - name = "ghttp_client_test", + name = "http_client_test", srcs = [ - "GHttpClientTest.cpp", + "HttpClientTest.cpp", ], deps = [ - "//src/main/cpp/base:ghttp_client", + "//src/main/cpp/base:base_library", "@com_google_googletest//:gtest_main", ], ) @@ -64,7 +64,6 @@ cc_test( ], deps = [ "//src/main/cpp/base:base_library", - "@com_google_absl//absl/container:flat_hash_set", "@com_google_googletest//:gtest_main", ], ) diff --git a/src/test/cpp/ut/base/GHttpClientTest.cpp b/src/test/cpp/ut/base/GHttpClientTest.cpp deleted file mode 100644 index eebbf26ba..000000000 --- a/src/test/cpp/ut/base/GHttpClientTest.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "GHttpClient.h" -#include "LoggerImpl.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include -#include -#include -ROCKETMQ_NAMESPACE_BEGIN - -class GHttpClientTest : public testing::Test { -public: - void SetUp() override { - SPDLOG_DEBUG("GHttpClient::SetUp() starts"); - grpc_init(); - SPDLOG_DEBUG("GHttpClient::SetUp() completed"); - } - - void TearDown() override { - SPDLOG_DEBUG("GHttpClientTest::TearDown() starts"); - grpc_shutdown(); - SPDLOG_DEBUG("GHttpClientTest::TearDown() completed"); - } -}; - -TEST_F(GHttpClientTest, testCtor) { - spdlog::set_level(spdlog::level::debug); - GHttpClient http_client; - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto callback = [&](int status, const absl::flat_hash_map& metadata, - const std::string& body) { - absl::MutexLock lk(&mtx); - completed = true; - SPDLOG_DEBUG("HTTP status-code: {}, response body: {}", status, body); - cv.SignalAll(); - }; - - std::string host("grpc.io"); - http_client.start(); - http_client.get(HttpProtocol::HTTP, host, 8080, "/", callback); - if (!completed) { - absl::MutexLock lk(&mtx); - cv.WaitWithTimeout(&mtx, absl::Seconds(5)); - } - ASSERT_TRUE(completed); - http_client.shutdown(); -} - -TEST_F(GHttpClientTest, testDestructWithoutShutdown) { - spdlog::set_level(spdlog::level::debug); - GHttpClient http_client; - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto callback = [&](int status, const absl::flat_hash_map& metadata, - const std::string& body) { - absl::MutexLock lk(&mtx); - completed = true; - SPDLOG_DEBUG("HTTP status-code: {}, response body: {}", status, body); - cv.SignalAll(); - }; - - std::string host("www.aliyun.com"); - http_client.start(); - http_client.get(HttpProtocol::HTTP, host, 8080, "/", callback); - if (!completed) { - absl::MutexLock lk(&mtx); - cv.WaitWithTimeout(&mtx, absl::Seconds(5)); - } - ASSERT_TRUE(completed); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp new file mode 100644 index 000000000..786bc13a3 --- /dev/null +++ b/src/test/cpp/ut/base/HttpClientTest.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "HttpClientImpl.h" +#include "LoggerImpl.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class HttpClientTest : public testing::Test { +public: + void SetUp() override { + SPDLOG_DEBUG("GHttpClient::SetUp() starts"); + http_client.start(); + SPDLOG_DEBUG("GHttpClient::SetUp() completed"); + } + + void TearDown() override { + SPDLOG_DEBUG("GHttpClientTest::TearDown() starts"); + http_client.shutdown(); + SPDLOG_DEBUG("GHttpClientTest::TearDown() completed"); + } + +protected: + HttpClientImpl http_client; +}; + +TEST_F(HttpClientTest, testBasics) {} + +TEST_F(HttpClientTest, testGet) { + auto cb = [](int code, const std::multimap& headers, const std::string& body) { + SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); + }; + + http_client.get(HttpProtocol::HTTP, "www.baidu.com", 80, "/", cb); +} + +TEST_F(HttpClientTest, DISABLED_testJMEnv) { + auto cb = [](int code, const std::multimap& headers, const std::string& body) { + SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); + }; + + http_client.get(HttpProtocol::HTTP, "jmenv.tbsite.net", 8080, "/rocketmq/nsaddr", cb); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp index 6e24e1bd0..836132f09 100644 --- a/src/test/cpp/ut/base/TopAddressingTest.cpp +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -1,12 +1,15 @@ -#include "TopAddressing.h" -#include "HttpClientMock.h" -#include "grpc/grpc.h" -#include "gtest/gtest.h" #include #include #include #include +#include "absl/synchronization/mutex.h" +#include "grpc/grpc.h" +#include "gtest/gtest.h" + +#include "HttpClientMock.h" +#include "TopAddressing.h" + ROCKETMQ_NAMESPACE_BEGIN class TopAddressingTest : public testing::Test { @@ -17,9 +20,8 @@ class TopAddressingTest : public testing::Test { auto http_client_ = absl::make_unique>(); auto mock_get = [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& - cb) { - absl::flat_hash_map headers; + const std::function&, const std::string&)>& cb) { + std::multimap headers; std::string body("10.0.0.1:9876"); cb(200, headers, body); }; diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index a243de1b1..ea071f184 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -52,7 +52,7 @@ TEST_F(ClientImplTest, testBasic) { std::string once{"10.0.0.1:9876"}; std::string then{"10.0.0.1:9876;10.0.0.2:9876"}; - absl::flat_hash_map header; + std::multimap header; bool completed = false; absl::Mutex mtx; @@ -61,12 +61,12 @@ TEST_F(ClientImplTest, testBasic) { int http_status = 200; auto once_cb = [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& - cb) { cb(http_status, header, once); }; + const std::function&, const std::string&)>& cb) { + cb(http_status, header, once); + }; auto then_cb = [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& - cb) { + const std::function&, const std::string&)>& cb) { cb(http_status, header, then); absl::MutexLock lk(&mtx); completed = true; diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp index f9b85fc99..19ccf8f60 100644 --- a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -1,12 +1,12 @@ #include "StsCredentialsProviderImpl.h" #include "HttpClientMock.h" #include "absl/memory/memory.h" +#include "grpc/grpc.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" #include #include #include -#include "grpc/grpc.h" ROCKETMQ_NAMESPACE_BEGIN @@ -18,9 +18,8 @@ class StsCredentialsProviderImplTest : public testing::Test { auto http_client_ = absl::make_unique>(); auto http_get_action = [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& - cb) { - absl::flat_hash_map header; + const std::function&, const std::string&)>& cb) { + std::multimap header; std::string body = R"( { "AccessKeyId": "key", @@ -35,12 +34,10 @@ class StsCredentialsProviderImplTest : public testing::Test { }; EXPECT_CALL(*http_client_, get).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(http_get_action)); - sts_credentials_provider->setHttpClient(std::move(http_client_)); + sts_credentials_provider->withHttpClient(std::move(http_client_)); } - void TearDown() override { - grpc_shutdown(); - } + void TearDown() override { grpc_shutdown(); } protected: std::shared_ptr sts_credentials_provider; diff --git a/third_party/asio.BUILD b/third_party/asio.BUILD index af74274ce..98f590dd0 100644 --- a/third_party/asio.BUILD +++ b/third_party/asio.BUILD @@ -3,7 +3,9 @@ cc_library( name = "asio", hdrs = glob(["include/**/*.hpp", "include/**/*.ipp"]), visibility = ["//visibility:public"], - strip_include_prefix = "include", + includes = [ + "include", + ], defines = [ "ASIO_STANDALONE", "ASIO_HAS_STD_ADDRESSOF", diff --git a/third_party/cpp_httplib.BUILD b/third_party/cpp_httplib.BUILD new file mode 100644 index 000000000..cf425f721 --- /dev/null +++ b/third_party/cpp_httplib.BUILD @@ -0,0 +1,17 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "cpp_httplib", + hdrs = [ + "httplib.h", + ], + visibility = [ + "//visibility:public", + ], + deps = [ + "//external:madler_zlib", + ], + defines = [ + "CPPHTTPLIB_ZLIB_SUPPORT", + ], +) \ No newline at end of file From 7d96cb1fc1c8f54ead6ad8394cafa517db98018e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 16 Sep 2021 16:34:56 +0800 Subject: [PATCH 058/133] Delete unused includes --- src/main/cpp/base/UtilAll.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index e4400d5c0..21938f0ad 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -1,7 +1,6 @@ #include "UtilAll.h" #include "LoggerImpl.h" -#include "src/core/lib/iomgr/gethostname.h" #include "zlib.h" #include From df89fc362c0d1b9ebdbe5f6ab54f7f6ecc8d15e6 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Fri, 17 Sep 2021 23:03:43 +0800 Subject: [PATCH 059/133] Name server resolver (#20) * Add StaticNameServerResolver and DynamicNameServerResolver --- api/rocketmq/DefaultMQProducer.h | 2 + api/rocketmq/DefaultMQPullConsumer.h | 2 + api/rocketmq/DefaultMQPushConsumer.h | 16 ++- src/main/cpp/client/ClientManagerImpl.cpp | 2 - src/main/cpp/client/include/ClientManager.h | 2 - .../cpp/client/include/ClientManagerImpl.h | 4 - .../client/mocks/include/ClientManagerMock.h | 2 - src/main/cpp/rocketmq/ClientImpl.cpp | 120 +++-------------- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 19 ++- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 20 ++- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 29 +++- .../rocketmq/DynamicNameServerResolver.cpp | 126 ++++++++++++++++++ .../cpp/rocketmq/StaticNameServerResolver.cpp | 24 ++++ src/main/cpp/rocketmq/include/ClientImpl.h | 37 +++-- .../include/DynamicNameServerResolver.h | 59 ++++++++ .../cpp/rocketmq/include/NameServerResolver.h | 25 ++++ .../include/StaticNameServerResolver.h | 33 +++++ .../mocks/include/NameServerResolverMock.h | 21 +++ src/test/cpp/ut/rocketmq/BUILD.bazel | 25 ++++ src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 18 ++- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 6 +- .../DynamicNameServerResolverTest.cpp | 61 +++++++++ src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 12 +- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 21 ++- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 17 ++- .../rocketmq/StaticNameServerResolverTest.cpp | 38 ++++++ .../cpp/ut/rocketmq/include/MQClientTest.h | 18 ++- 27 files changed, 572 insertions(+), 187 deletions(-) create mode 100644 src/main/cpp/rocketmq/DynamicNameServerResolver.cpp create mode 100644 src/main/cpp/rocketmq/StaticNameServerResolver.cpp create mode 100644 src/main/cpp/rocketmq/include/DynamicNameServerResolver.h create mode 100644 src/main/cpp/rocketmq/include/NameServerResolver.h create mode 100644 src/main/cpp/rocketmq/include/StaticNameServerResolver.h create mode 100644 src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h create mode 100644 src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp create mode 100644 src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index d3fca1b69..697b0b4d7 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -79,6 +79,8 @@ class DefaultMQProducer { void setNamesrvAddr(const std::string& name_server_address_list); + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + void setGroupName(const std::string& group_name); void setInstanceName(const std::string& instance_name); diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index e0fd33163..9b7d3ef2f 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -36,6 +36,8 @@ class DefaultMQPullConsumer { void setNamesrvAddr(const std::string& name_srv); + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + void setCredentialsProvider(std::shared_ptr credentials_provider); private: diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index 20af32172..176d90953 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -36,6 +36,8 @@ class DefaultMQPushConsumer { void setNamesrvAddr(const std::string& name_srv); + void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); + void setGroupName(const std::string& group_name); void setConsumeThreadCount(int thread_count); @@ -53,8 +55,8 @@ class DefaultMQPushConsumer { bool isTracingEnabled(); /** - * SDK of this version always uses asynchronous IO operation. As such, this function is no-op - * to keep backward compatibility. + * SDK of this version always uses asynchronous IO operation. As such, this + * function is no-op to keep backward compatibility. */ void setAsyncPull(bool); @@ -65,21 +67,23 @@ class DefaultMQPushConsumer { void setConsumeMessageBatchMaxSize(int batch_size); /** - * Lifecycle of executor is managed by external application. Passed-in executor should remain valid after consumer - * start and before stopping. + * Lifecycle of executor is managed by external application. Passed-in + * executor should remain valid after consumer start and before stopping. * @param executor Executor pool used to invoke consume callback. */ void setCustomExecutor(const Executor& executor); /** - * This function sets maximum number of message that may be consumed per second. + * This function sets maximum number of message that may be consumed per + * second. * @param topic Topic to control * @param threshold Threshold before throttling is enforced. */ void setThrottle(const std::string& topic, uint32_t threshold); /** - * Set abstract-resource-namespace, in which canonical name of topic, group remains unique. + * Set abstract-resource-namespace, in which canonical name of topic, group + * remains unique. * @param resource_namespace Abstract resource namespace. */ void setResourceNamespace(const char* resource_namespace); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 69f2c6d30..d542352c7 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -948,8 +948,6 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess Scheduler& ClientManagerImpl::getScheduler() { return scheduler_; } -TopAddressing& ClientManagerImpl::topAddressing() { return top_addressing_; } - void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb) { std::string target_host(target.data(), target.length()); diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 154ad9469..8ebb2eb02 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -24,8 +24,6 @@ class ClientManager { virtual Scheduler& getScheduler() = 0; - virtual TopAddressing& topAddressing() = 0; - virtual std::shared_ptr createChannel(const std::string& target_host) = 0; virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index f50ecc6ba..050ad9101 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -130,8 +130,6 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share Scheduler& getScheduler() override; - TopAddressing& topAddressing() override; - /** * Ack message asynchronously. * @param target_host Target broker host address. @@ -248,8 +246,6 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share grpc::ChannelArguments channel_arguments_; bool trace_{false}; - - TopAddressing top_addressing_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index c3c0d4c10..7abce6e73 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -14,8 +14,6 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(Scheduler&, getScheduler, (), (override)); - MOCK_METHOD(TopAddressing&, topAddressing, (), (override)); - MOCK_METHOD((std::shared_ptr), createChannel, (const std::string&), (override)); MOCK_METHOD(void, resolveRoute, diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 4bbc90f15..e7806edb7 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -30,39 +31,20 @@ void ClientImpl::start() { return; } + if (!name_server_resolver_) { + SPDLOG_ERROR("No name server resolver is configured."); + abort(); + } + name_server_resolver_->start(); + client_manager_ = ClientManagerFactory::getInstance().getClientManager(*this); client_manager_->start(); exporter_ = std::make_shared(client_manager_, this); exporter_->start(); - bool update_name_server_list = false; - { - absl::MutexLock lock(&name_server_list_mtx_); - if (name_server_list_.empty()) { - update_name_server_list = true; - } - } - std::weak_ptr ptr(self()); - if (update_name_server_list) { - // Acquire name server list immediately - renewNameServerList(); - - // Schedule to renew name server list periodically - SPDLOG_INFO("Name server list was empty. Schedule a task to fetch and renew periodically"); - auto name_server_update_functor = [ptr]() { - std::shared_ptr base = ptr.lock(); - if (base) { - base->renewNameServerList(); - } - }; - name_server_update_handle_ = - client_manager_->getScheduler().schedule(name_server_update_functor, UPDATE_NAME_SERVER_LIST_TASK_NAME, - std::chrono::milliseconds(0), std::chrono::seconds(30)); - } - auto route_update_functor = [ptr]() { std::shared_ptr base = ptr.lock(); if (base) { @@ -77,9 +59,8 @@ void ClientImpl::start() { void ClientImpl::shutdown() { state_.store(State::STOPPING, std::memory_order_relaxed); - if (name_server_update_handle_) { - client_manager_->getScheduler().cancel(name_server_update_handle_); - } + + name_server_resolver_->shutdown(); if (route_update_handle_) { client_manager_->getScheduler().cancel(route_update_handle_); @@ -91,7 +72,6 @@ void ClientImpl::shutdown() { } const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; -const char* ClientImpl::UPDATE_NAME_SERVER_LIST_TASK_NAME = "name_server_list_updater"; void ClientImpl::endpointsInUse(absl::flat_hash_set& endpoints) { absl::MutexLock lk(&topic_route_table_mtx_); @@ -150,81 +130,11 @@ void ClientImpl::getRouteFor(const std::string& topic, const std::function& list) { - std::string previous; - bool changed = false; - { - absl::MutexLock lock(&name_server_list_mtx_); - if (name_server_list_ != list) { - changed = true; - if (name_server_list_.empty()) { - previous.append("[]"); - } else { - previous = absl::StrJoin(name_server_list_.begin(), name_server_list_.end(), ";"); - } - } - } - std::string current = absl::StrJoin(list.begin(), list.end(), ";"); - if (changed) { - SPDLOG_INFO("Name server list changed. {} --> {}", previous, current); - } else { - SPDLOG_DEBUG("Name server list remains the same: {}", current); - } -} - -void ClientImpl::renewNameServerList() { - if (State::STARTED != state_.load(std::memory_order_relaxed) && - State::STARTING != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); - return; - } - - std::vector list; - SPDLOG_DEBUG("Begin to renew name server list"); - auto callback = [this](int code, const std::vector& name_server_list) { - if (static_cast(HttpStatus::OK) != code) { - SPDLOG_WARN("Failed to fetch name server list"); - return; - } - - if (name_server_list.empty()) { - SPDLOG_WARN("Yuck, got an empty name server list"); - return; - } - - debugNameServerChanges(name_server_list); - { - absl::MutexLock lock(&name_server_list_mtx_); - if (name_server_list_ != name_server_list) { - name_server_list_.clear(); - name_server_list_.insert(name_server_list_.begin(), name_server_list.begin(), name_server_list.end()); - } - } - }; - client_manager_->topAddressing().fetchNameServerAddresses(callback); -} - -bool ClientImpl::selectNameServer(std::string& selected, bool change) { - static uint32_t index = 0; - if (change) { - index++; - } - { - absl::MutexLock lock(&name_server_list_mtx_); - if (name_server_list_.empty()) { - return false; - } - uint32_t idx = index % name_server_list_.size(); - selected = name_server_list_[idx]; - } - return true; -} - void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { std::vector> pairs; { - absl::MutexLock lk(&name_server_list_mtx_); - for (const auto& name_server_item : name_server_list_) { + std::vector name_server_list = name_server_resolver_->resolve(); + for (const auto& name_server_item : name_server_list) { std::string::size_type pos = name_server_item.rfind(':'); if (std::string::npos == pos) { continue; @@ -254,8 +164,8 @@ void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { } void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { - std::string name_server; - if (!selectNameServer(name_server)) { + std::string name_server = name_server_resolver_->current(); + if (name_server.empty()) { SPDLOG_WARN("No name server available"); return; } @@ -263,8 +173,8 @@ void ClientImpl::fetchRouteFor(const std::string& topic, const std::functionnext(); + if (!name_server_changed.empty()) { SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); } cb(nullptr); diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index cdae0c06e..539cc6b41 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -1,9 +1,15 @@ #include "rocketmq/DefaultMQProducer.h" -#include "MixAll.h" -#include "ProducerImpl.h" + +#include +#include #include "absl/strings/str_split.h" +#include "DynamicNameServerResolver.h" +#include "MixAll.h" +#include "ProducerImpl.h" +#include "StaticNameServerResolver.h" + ROCKETMQ_NAMESPACE_BEGIN DefaultMQProducer::DefaultMQProducer(const std::string& group_name) @@ -26,8 +32,13 @@ void DefaultMQProducer::setSendMsgTimeout(std::chrono::milliseconds timeout) { } void DefaultMQProducer::setNamesrvAddr(const std::string& name_server_address_list) { - std::vector name_server_list = absl::StrSplit(name_server_address_list, ';'); - impl_->setNameServerList(name_server_list); + auto name_server_resolver = std::make_shared(name_server_address_list); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQProducer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); } void DefaultMQProducer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index 041066244..fd6f24454 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -1,7 +1,12 @@ #include "rocketmq/DefaultMQPullConsumer.h" + +#include "absl/strings/str_split.h" + #include "AwaitPullCallback.h" +#include "DynamicNameServerResolver.h" #include "PullConsumerImpl.h" -#include "absl/strings/str_split.h" +#include "StaticNameServerResolver.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -28,15 +33,22 @@ void DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullCallback* ca impl_->pull(query, callback); } -void DefaultMQPullConsumer::setResourceNamespace(const std::string& resource_namespace) { impl_->resourceNamespace(resource_namespace); } +void DefaultMQPullConsumer::setResourceNamespace(const std::string& resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} void DefaultMQPullConsumer::setCredentialsProvider(std::shared_ptr credentials_provider) { impl_->setCredentialsProvider(std::move(credentials_provider)); } void DefaultMQPullConsumer::setNamesrvAddr(const std::string& name_srv) { - std::vector name_server_list = absl::StrSplit(name_srv, absl::ByChar(';')); - impl_->setNameServerList(name_server_list); + auto name_server_resolver = std::make_shared(name_srv); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQPullConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index c6f7daa05..ab072fdce 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -1,8 +1,14 @@ -#include "rocketmq/DefaultMQPushConsumer.h" -#include "PushConsumerImpl.h" -#include "absl/strings/str_split.h" +#include +#include #include +#include "absl/strings/str_split.h" + +#include "DynamicNameServerResolver.h" +#include "PushConsumerImpl.h" +#include "StaticNameServerResolver.h" +#include "rocketmq/DefaultMQPushConsumer.h" + ROCKETMQ_NAMESPACE_BEGIN static std::set consumerTable{}; @@ -37,8 +43,17 @@ void DefaultMQPushConsumer::registerMessageListener(MessageListener* listener) { } void DefaultMQPushConsumer::setNamesrvAddr(const std::string& name_srv) { - std::vector name_server_list = absl::StrSplit(name_srv, ';'); - impl_->setNameServerList(name_server_list); + auto name_server_resolver = std::make_shared(name_srv); + impl_->withNameServerResolver(name_server_resolver); +} + +void DefaultMQPushConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { + if (discovery_endpoint.empty()) { + return; + } + + auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); + impl_->withNameServerResolver(name_server_resolver); } void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } @@ -67,7 +82,9 @@ void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t thres impl_->setThrottle(topic, threshold); } -void DefaultMQPushConsumer::setResourceNamespace(const char* resource_namespace) { impl_->resourceNamespace(resource_namespace); } +void DefaultMQPushConsumer::setResourceNamespace(const char* resource_namespace) { + impl_->resourceNamespace(resource_namespace); +} void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { impl_->setCredentialsProvider(std::move(credentials_provider)); diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp new file mode 100644 index 000000000..df1fb2b8f --- /dev/null +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -0,0 +1,126 @@ +#include "DynamicNameServerResolver.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/str_join.h" +#include "spdlog/spdlog.h" + +#include "SchedulerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +DynamicNameServerResolver::DynamicNameServerResolver(absl::string_view endpoint, + std::chrono::milliseconds refresh_interval) + : endpoint_(endpoint.data(), endpoint.length()), refresh_interval_(refresh_interval), + scheduler_(absl::make_unique()) { + absl::string_view remains; + if (absl::StartsWith(endpoint_, "https://")) { + ssl_ = true; + remains = absl::StripPrefix(endpoint_, "https://"); + } else { + remains = absl::StripPrefix(endpoint_, "http://"); + } + + std::int32_t port = 80; + if (ssl_) { + port = 443; + } + + absl::string_view host; + if (absl::StrContains(remains, ':')) { + std::vector segments = absl::StrSplit(remains, ':'); + host = segments[0]; + remains = absl::StripPrefix(remains, host); + remains = absl::StripPrefix(remains, ":"); + + segments = absl::StrSplit(remains, '/'); + if (!absl::SimpleAtoi(segments[0], &port)) { + SPDLOG_WARN("Failed to parse port of name-server-list discovery service endpoint"); + abort(); + } + remains = absl::StripPrefix(remains, segments[0]); + } else { + std::vector segments = absl::StrSplit(remains, '/'); + host = segments[0]; + remains = absl::StripPrefix(remains, host); + } + + top_addressing_ = absl::make_unique(std::string(host.data(), host.length()), port, + std::string(remains.data(), remains.length())); +} + +std::vector DynamicNameServerResolver::resolve() { + bool fetch_immediately = false; + { + absl::MutexLock lk(&name_server_list_mtx_); + if (name_server_list_.empty()) { + fetch_immediately = true; + } + } + + if (fetch_immediately) { + fetch(); + } + + { + absl::MutexLock lk(&name_server_list_mtx_); + return name_server_list_; + } +} + +void DynamicNameServerResolver::fetch() { + std::weak_ptr ptr(shared_from_this()); + auto callback = [ptr](bool success, const std::vector& name_server_list) { + if (success && !name_server_list.empty()) { + std::shared_ptr resolver = ptr.lock(); + if (resolver) { + resolver->onNameServerListFetched(name_server_list); + } + } + }; + top_addressing_->fetchNameServerAddresses(callback); +} + +void DynamicNameServerResolver::onNameServerListFetched(const std::vector& name_server_list) { + if (!name_server_list.empty()) { + absl::MutexLock lk(&name_server_list_mtx_); + if (name_server_list_ != name_server_list) { + SPDLOG_INFO("Name server list changed. {} --> {}", absl::StrJoin(name_server_list_, ";"), + absl::StrJoin(name_server_list, ";")); + name_server_list_ = name_server_list; + } + } +} + +void DynamicNameServerResolver::injectHttpClient(std::unique_ptr http_client) { + top_addressing_->injectHttpClient(std::move(http_client)); +} + +void DynamicNameServerResolver::start() { + scheduler_->start(); + scheduler_->schedule(std::bind(&DynamicNameServerResolver::fetch, this), "DynamicNameServerResolver", + std::chrono::milliseconds(0), refresh_interval_); +} + +void DynamicNameServerResolver::shutdown() { scheduler_->shutdown(); } + +std::string DynamicNameServerResolver::current() { + absl::MutexLock lk(&name_server_list_mtx_); + if (name_server_list_.empty()) { + return std::string(); + } + + std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size(); + return name_server_list_[index]; +} + +std::string DynamicNameServerResolver::next() { + index_.fetch_add(1, std::memory_order_relaxed); + return current(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp new file mode 100644 index 000000000..6e156be1c --- /dev/null +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -0,0 +1,24 @@ +#include "StaticNameServerResolver.h" + +#include "absl/strings/str_split.h" +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) + : name_server_list_(absl::StrSplit(name_server_list, ';')) {} + +std::string StaticNameServerResolver::current() { + std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size(); + return name_server_list_[index]; +} + +std::string StaticNameServerResolver::next() { + index_.fetch_add(1, std::memory_order_relaxed); + return current(); +} + +std::vector StaticNameServerResolver::resolve() { return name_server_list_; } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 4a2dc6e9c..a3046cf4a 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -1,13 +1,19 @@ +#pragma once + +#include +#include + +#include "apache/rocketmq/v1/definition.pb.h" + #include "Client.h" #include "ClientConfigImpl.h" #include "ClientManager.h" #include "ClientResourceBundle.h" #include "InvocationContext.h" +#include "NameServerResolver.h" #include "OtlpExporter.h" -#include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/State.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -31,11 +37,6 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { */ void endpointsInUse(absl::flat_hash_set& endpoints) override LOCKS_EXCLUDED(topic_route_table_mtx_); - void setNameServerList(std::vector name_server_list) { - absl::MutexLock lk(&name_server_list_mtx_); - name_server_list_ = std::move(name_server_list); - } - void heartbeat() override; bool active() override { @@ -48,6 +49,10 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { void schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) override; + void withNameServerResolver(std::shared_ptr name_server_resolver) { + name_server_resolver_ = std::move(name_server_resolver); + } + protected: ClientManagerPtr client_manager_; std::shared_ptr exporter_; @@ -60,14 +65,10 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { inflight_route_requests_ GUARDED_BY(inflight_route_requests_mtx_); absl::Mutex inflight_route_requests_mtx_ ACQUIRED_BEFORE(topic_route_table_mtx_); // Protects inflight_route_requests_ static const char* UPDATE_ROUTE_TASK_NAME; - std::uintptr_t route_update_handle_{0}; - - // Name server list management - std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); - absl::Mutex name_server_list_mtx_; // protects name_server_list_ + std::uint32_t route_update_handle_{0}; - static const char* UPDATE_NAME_SERVER_LIST_TASK_NAME; - std::uintptr_t name_server_update_handle_{0}; + // Set Name Server Resolver + std::shared_ptr name_server_resolver_; absl::flat_hash_map multiplexing_requests_; absl::Mutex multiplexing_requests_mtx_; @@ -75,12 +76,6 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { absl::flat_hash_set isolated_endpoints_ GUARDED_BY(isolated_endpoints_mtx_); absl::Mutex isolated_endpoints_mtx_; - void debugNameServerChanges(const std::vector& list) LOCKS_EXCLUDED(name_server_list_mtx_); - - void renewNameServerList() LOCKS_EXCLUDED(name_server_list_mtx_); - - bool selectNameServer(std::string& selected, bool change = false) LOCKS_EXCLUDED(name_server_list_mtx_); - void updateRouteInfo() LOCKS_EXCLUDED(topic_route_table_mtx_); /** @@ -104,7 +99,7 @@ class ClientImpl : virtual public Client, virtual public ClientConfigImpl { return resource_bundle; } - void setAccessPoint(rmq::Endpoints* endpoints) LOCKS_EXCLUDED(name_server_list_mtx_); + void setAccessPoint(rmq::Endpoints* endpoints); virtual void notifyClientTermination(); diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h new file mode 100644 index 000000000..7626bd14f --- /dev/null +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +#include "NameServerResolver.h" +#include "Scheduler.h" +#include "TopAddressing.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DynamicNameServerResolver : public NameServerResolver, + public std::enable_shared_from_this { +public: + DynamicNameServerResolver(absl::string_view endpoint, std::chrono::milliseconds refresh_interval); + + void start() override; + + void shutdown() override; + + std::string current() override LOCKS_EXCLUDED(name_server_list_mtx_); + + std::string next() override LOCKS_EXCLUDED(name_server_list_mtx_); + + std::vector resolve() override LOCKS_EXCLUDED(name_server_list_mtx_); + + void injectHttpClient(std::unique_ptr http_client); + +private: + std::string endpoint_; + + std::chrono::milliseconds refresh_interval_; + + void fetch(); + + void onNameServerListFetched(const std::vector& name_server_list) LOCKS_EXCLUDED(name_server_list_mtx_); + + std::vector name_server_list_ GUARDED_BY(name_server_list_mtx_); + absl::Mutex name_server_list_mtx_; + + std::atomic index_{0}; + + bool ssl_{false}; + std::unique_ptr top_addressing_; + + std::unique_ptr scheduler_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h new file mode 100644 index 000000000..829d2d21b --- /dev/null +++ b/src/main/cpp/rocketmq/include/NameServerResolver.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NameServerResolver { +public: + virtual ~NameServerResolver() = default; + + virtual void start() = 0; + + virtual void shutdown() = 0; + + virtual std::string next() = 0; + + virtual std::string current() = 0; + + virtual std::vector resolve() = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h new file mode 100644 index 000000000..1d62106a1 --- /dev/null +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include "NameServerResolver.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class StaticNameServerResolver : public NameServerResolver { +public: + StaticNameServerResolver(absl::string_view name_server_list); + + void start() override {} + + void shutdown() override {} + + std::string current() override; + + std::string next() override; + + std::vector resolve() override; + +private: + std::vector name_server_list_; + std::atomic index_{0}; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h new file mode 100644 index 000000000..89d7c7c8d --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h @@ -0,0 +1,21 @@ +#pragma once + +#include "NameServerResolver.h" +#include "gmock/gmock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NameServerResolverMock : public NameServerResolver { +public: + MOCK_METHOD(void, start, (), (override)); + + MOCK_METHOD(void, shutdown, (), (override)); + + MOCK_METHOD(std::string, next, (), (override)); + + MOCK_METHOD(std::string, current, (), (override)); + + MOCK_METHOD((std::vector), resolve, (), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index c70c146b7..47fa3d840 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -196,6 +196,31 @@ cc_test( "//src/main/cpp/base/mocks:base_mocks", "//src/main/cpp/client/mocks:client_mocks", "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "static_name_server_resolver_test", + srcs = [ + "StaticNameServerResolverTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], +) + + +cc_test( + name = "dynamic_name_server_resolver_test", + srcs = [ + "DynamicNameServerResolverTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "//src/main/cpp/base/mocks:base_mocks", "@com_google_googletest//:gtest_main", ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index ea071f184..424b4d6ae 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -1,13 +1,18 @@ +#include +#include +#include + #include "ClientImpl.h" #include "ClientManagerFactory.h" #include "ClientManagerMock.h" +#include "DynamicNameServerResolver.h" #include "HttpClientMock.h" +#include "NameServerResolverMock.h" #include "SchedulerImpl.h" #include "TopAddressing.h" #include "rocketmq/RocketMQ.h" + #include "gtest/gtest.h" -#include -#include ROCKETMQ_NAMESPACE_BEGIN @@ -24,11 +29,13 @@ class ClientImplTest : public testing::Test { public: void SetUp() override { grpc_init(); + name_server_resolver_ = std::make_shared(endpoint_, std::chrono::seconds(1)); scheduler_.start(); client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); client_ = std::make_shared(group_); + client_->withNameServerResolver(name_server_resolver_); } void TearDown() override { @@ -37,17 +44,17 @@ class ClientImplTest : public testing::Test { } protected: + std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; std::string resource_namespace_{"mq://test"}; std::string group_{"Group-0"}; std::shared_ptr> client_manager_; SchedulerImpl scheduler_; std::shared_ptr client_; + std::shared_ptr name_server_resolver_; }; TEST_F(ClientImplTest, testBasic) { - TopAddressing top_addressing_; - auto http_client = absl::make_unique(); std::string once{"10.0.0.1:9876"}; @@ -74,9 +81,8 @@ TEST_F(ClientImplTest, testBasic) { }; EXPECT_CALL(*http_client, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); - top_addressing_.injectHttpClient(std::move(http_client)); + name_server_resolver_->injectHttpClient(std::move(http_client)); - ON_CALL(*client_manager_, topAddressing).WillByDefault(testing::ReturnRef(top_addressing_)); client_->resourceNamespace(resource_namespace_); client_->start(); { diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index c458519c8..e827a1ede 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -132,7 +132,7 @@ class UnitTestSendCallback : public SendCallback { TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { auto producer = std::make_shared(group_name_); producer->resourceNamespace(resource_namespace_); - producer->setNameServerList(name_server_list_); + producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); MQMessage message; @@ -155,7 +155,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { TEST_F(DefaultMQProducerUnitTest, testSendMessage) { auto producer = std::make_shared(group_name_); producer->resourceNamespace(resource_namespace_); - producer->setNameServerList(name_server_list_); + producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); MQMessage message; @@ -167,7 +167,7 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { auto producer = std::make_shared(group_name_); producer->resourceNamespace(resource_namespace_); - producer->setNameServerList(name_server_list_); + producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp new file mode 100644 index 000000000..2435b8139 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -0,0 +1,61 @@ +#include "DynamicNameServerResolver.h" + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_join.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +#include "HttpClientMock.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DynamicNameServerResolverTest : public testing::Test { +public: + DynamicNameServerResolverTest() + : resolver_(std::make_shared(endpoint_, std::chrono::seconds(1))) {} + + void SetUp() override { + auto http_client = absl::make_unique>(); + + auto callback = + [this](HttpProtocol, const std::string&, std::uint16_t, const std::string&, + const std::function&, const std::string&)>& cb) { + int code = 200; + std::multimap headers; + cb(code, headers, name_server_list_); + }; + + ON_CALL(*http_client, get).WillByDefault(testing::Invoke(callback)); + + resolver_->injectHttpClient(std::move(http_client)); + + resolver_->start(); + } + + void TearDown() override { resolver_->shutdown(); } + +protected: + std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; + std::string name_server_list_{"10.0.0.0:9876;10.0.0.1:9876"}; + std::shared_ptr resolver_; +}; + +TEST_F(DynamicNameServerResolverTest, testResolve) { + auto name_server_list = resolver_->resolve(); + ASSERT_FALSE(name_server_list.empty()); + std::string resolved = absl::StrJoin(name_server_list, ";"); + ASSERT_EQ(name_server_list_, resolved); + + std::string first{"10.0.0.0:9876"}; + EXPECT_EQ(first, resolver_->current()); + + std::string second{"10.0.0.1:9876"}; + EXPECT_EQ(second, resolver_->next()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index 23f7ae70f..a9e253b5e 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -1,14 +1,16 @@ -#include "ProducerImpl.h" +#include + #include "ClientManagerFactory.h" #include "ClientManagerMock.h" +#include "ProducerImpl.h" #include "SchedulerImpl.h" +#include "StaticNameServerResolver.h" #include "TopicRouteData.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQSelector.h" #include "rocketmq/RocketMQ.h" #include "rocketmq/SendResult.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -19,11 +21,12 @@ class ProducerImplTest : public testing::Test { void SetUp() override { grpc_init(); + name_server_resolver_ = std::make_shared(name_server_list_); client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); producer_ = std::make_shared(group_); producer_->resourceNamespace(resource_namespace_); - producer_->setNameServerList(name_server_list_); + producer_->withNameServerResolver(name_server_resolver_); producer_->setCredentialsProvider(credentials_provider_); { @@ -44,7 +47,8 @@ class ProducerImplTest : public testing::Test { protected: std::shared_ptr> client_manager_; std::shared_ptr producer_; - std::vector name_server_list_{"10.0.0.1:9876"}; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; std::string resource_namespace_{"mq://test"}; std::string group_{"CID_test"}; std::string topic_{"Topic0"}; diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index a6db0ca4c..90f32d80b 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -1,16 +1,19 @@ -#include "PullConsumerImpl.h" +#include +#include +#include + #include "ClientManagerFactory.h" #include "ClientManagerMock.h" #include "InvocationContext.h" +#include "PullConsumerImpl.h" #include "Scheduler.h" +#include "StaticNameServerResolver.h" +#include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/ConsumeType.h" #include "rocketmq/RocketMQ.h" + #include "gtest/gtest.h" -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN @@ -18,13 +21,16 @@ class PullConsumerImplTest : public testing::Test { public: void SetUp() override { grpc_init(); + + name_server_resolver_ = std::make_shared(name_server_list_); + scheduler_.start(); client_manager_ = std::make_shared>(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); pull_consumer_ = std::make_shared(group_); - pull_consumer_->setNameServerList(name_server_list_); + pull_consumer_->withNameServerResolver(name_server_resolver_); pull_consumer_->resourceNamespace(resource_namespace_); { @@ -47,7 +53,8 @@ class PullConsumerImplTest : public testing::Test { protected: std::string resource_namespace_{"mq://test"}; - std::vector name_server_list_{"10.0.0.1:9876"}; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; std::string group_{"Group-0"}; std::string topic_{"Test"}; std::string tag_{"TagB"}; diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index d98ef518d..0789af4ac 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -1,14 +1,17 @@ -#include "PushConsumerImpl.h" +#include + +#include "gtest/gtest.h" + #include "ClientManagerFactory.h" #include "ClientManagerMock.h" #include "InvocationContext.h" #include "MessageAccessor.h" +#include "PushConsumerImpl.h" +#include "StaticNameServerResolver.h" #include "grpc/grpc.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/MessageListener.h" #include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -25,18 +28,22 @@ class PushConsumerImplTest : public testing::Test { void SetUp() override { grpc_init(); + + name_server_resolver_ = std::make_shared(name_server_list_); + client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); push_consumer_ = std::make_shared(group_); push_consumer_->resourceNamespace(resource_namespace_); - push_consumer_->setNameServerList(name_server_list_); + push_consumer_->withNameServerResolver(name_server_resolver_); push_consumer_->registerMessageListener(message_listener_.get()); } void TearDown() override { grpc_shutdown(); } protected: - std::vector name_server_list_{"10.0.0.1:9876"}; + std::string name_server_list_{"10.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; std::string resource_namespace_{"mq://test"}; std::string group_{"CID_test"}; std::string topic_{"Topic0"}; diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp new file mode 100644 index 000000000..dab7a0297 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -0,0 +1,38 @@ +#include "StaticNameServerResolver.h" + +#include "absl/strings/str_split.h" + +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class StaticNameServerResolverTest : public testing::Test { +public: + StaticNameServerResolverTest() : resolver_(name_server_list_) {} + + void SetUp() override { resolver_.start(); } + + void TearDown() override { resolver_.shutdown(); } + +protected: + std::string name_server_list_{"10.0.0.1:9876;10.0.0.2:9876"}; + StaticNameServerResolver resolver_; +}; + +TEST_F(StaticNameServerResolverTest, testResolve) { + std::vector segments = absl::StrSplit(name_server_list_, ';'); + ASSERT_EQ(segments, resolver_.resolve()); +} + +TEST_F(StaticNameServerResolverTest, testCurrentNext) { + std::string&& name_server_1 = resolver_.current(); + std::string expected = "10.0.0.1:9876"; + EXPECT_EQ(expected, name_server_1); + + expected = "10.0.0.2:9876"; + std::string&& name_server_2 = resolver_.next(); + EXPECT_EQ(expected, name_server_2); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 405aca3f5..6c13f20c9 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -1,19 +1,22 @@ #pragma once -#include "ClientManagerFactory.h" -#include "RpcClientMock.h" -#include "ClientManagerImpl.h" +#include +#include + #include "grpc/grpc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include -#include + +#include "ClientManagerFactory.h" +#include "ClientManagerImpl.h" +#include "RpcClientMock.h" +#include "StaticNameServerResolver.h" ROCKETMQ_NAMESPACE_BEGIN class MQClientTest : public testing::Test { public: - MQClientTest() = default; + MQClientTest() = default; void SetUp() override { grpc_init(); @@ -27,6 +30,8 @@ class MQClientTest : public testing::Test { std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_instance_); + + name_server_resolver_ = std::make_shared(name_server_address_); } void TearDown() override { @@ -43,6 +48,7 @@ class MQClientTest : public testing::Test { const int32_t partition_num_{24}; const int32_t avg_partition_per_host_{8}; std::string name_server_address_{"ipv4:127.0.0.1:9876"}; + std::shared_ptr name_server_resolver_; std::string group_name_{"CID_Test"}; std::string topic_{"Topic_Test"}; std::string resource_namespace_{"mq://test"}; From 64388fa68e02b5c60fd43b6dcbe3cb2e7d66e438 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 17 Sep 2021 23:10:40 +0800 Subject: [PATCH 060/133] Fix logging --- src/main/cpp/scheduler/SchedulerImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index 8cb9d53fa..f50079f3f 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -118,7 +118,7 @@ void SchedulerImpl::execute(const asio::error_code& ec, asio::steady_timer* time if (timer_task->interval.count()) { timer->expires_at(timer->expiry() + timer_task->interval); timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer, task)); - SPDLOG_DEBUG("Repeated timer-task to fire in {}ms", timer_task->interval.count()); + SPDLOG_DEBUG("Repeated timer-task {} to fire in {}ms", timer_task->task_name, timer_task->interval.count()); } else { delete timer; } From e87b7a2187e8369a9a6e87f0edfdeddea7a057a4 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 17 Sep 2021 23:14:04 +0800 Subject: [PATCH 061/133] Fix logging --- src/main/cpp/rocketmq/ClientImpl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index e7806edb7..d029f8367 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -250,6 +250,10 @@ void ClientImpl::heartbeat() { } void ClientImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) { + if (route) { + SPDLOG_DEBUG("Received route data for topic={}", topic); + } + updateRouteCache(topic, route); // Take all pending callbacks From 3222428b85d12cf0578fa66181112ea9ec8f5f3c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sat, 18 Sep 2021 10:03:21 +0800 Subject: [PATCH 062/133] Polish log --- src/main/cpp/client/ClientManagerImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index d542352c7..6ccac620c 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -49,12 +49,12 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) tls_channel_credential_options_.watch_root_certs(); tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - SPDLOG_INFO("ClientManager[ARN={}] created", resource_namespace_); + SPDLOG_INFO("ClientManager[ResourceNamespace={}] created", resource_namespace_); } ClientManagerImpl::~ClientManagerImpl() { shutdown(); - SPDLOG_INFO("ClientManager[ARN={}] destructed", resource_namespace_); + SPDLOG_INFO("ClientManager[ResourceNamespace={}] destructed", resource_namespace_); } void ClientManagerImpl::start() { From 74ca811c017549a933deca690fda2c3a50613e00 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Sun, 19 Sep 2021 21:22:21 +0800 Subject: [PATCH 063/133] Log error details to console --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8287300e9..dc3d39791 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,4 +38,4 @@ jobs: - name: Compile All Targets run: bazel build //... - name: Run Unit Tests - run: bazel test //src/test/cpp/ut/... \ No newline at end of file + run: bazel test --test_output=errors //src/test/cpp/ut/... \ No newline at end of file From c34e620fb9ad234b7671142042f31cad8ad6f8c7 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Sun, 19 Sep 2021 21:43:03 +0800 Subject: [PATCH 064/133] Use multiple runner --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dc3d39791..049cb2662 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: ubuntu-latest + runs-on: [ubuntu-20.04, ubuntu-18.04, windows-2016, windows-2019, windows-2022, macos-11, macos-10.15] # Steps represent a sequence of tasks that will be executed as part of the job steps: From 9f0e46da00fe43d6e2a33b414793c69b23d8afe3 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 19 Sep 2021 21:50:46 +0800 Subject: [PATCH 065/133] Test support of multiple runners --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 049cb2662..b54a664cf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: [ubuntu-20.04, ubuntu-18.04, windows-2016, windows-2019, windows-2022, macos-11, macos-10.15] + runs-on: [ubuntu-20.04, ubuntu-18.04] # Steps represent a sequence of tasks that will be executed as part of the job steps: From de37bcff3b0df8416e95f70552c7467869201ea1 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 19 Sep 2021 21:53:50 +0800 Subject: [PATCH 066/133] Revert runs-on --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b54a664cf..dc3d39791 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: [ubuntu-20.04, ubuntu-18.04] + runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: From c7a6cddc62a8573b0e240ba2f6071de25fed70a2 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Wed, 22 Sep 2021 16:33:25 +0800 Subject: [PATCH 067/133] Fix Transactional Message Bugs (#21) Expose transaction API --- api/rocketmq/DefaultMQProducer.h | 2 + api/rocketmq/MQMessage.h | 5 +- api/rocketmq/MessageType.h | 16 +++++++ api/rocketmq/Transaction.h | 6 ++- example/rocketmq/BUILD.bazel | 10 ++++ .../rocketmq/ExampleTransactionProducer.cpp | 34 ++++++++++++++ src/main/cpp/base/MQMessage.cpp | 6 +++ src/main/cpp/base/include/DigestType.h | 15 ++++++ src/main/cpp/base/include/Encoding.h | 15 ++++++ src/main/cpp/base/include/Protocol.h | 29 +++--------- src/main/cpp/client/ClientManagerImpl.cpp | 46 ++----------------- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 6 +++ src/main/cpp/rocketmq/ProducerImpl.cpp | 34 +++++++++----- src/main/cpp/rocketmq/SendCallbacks.cpp | 3 +- src/main/cpp/rocketmq/TransactionImpl.cpp | 4 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 4 +- src/main/cpp/rocketmq/include/SendCallbacks.h | 13 ++++-- 17 files changed, 161 insertions(+), 87 deletions(-) create mode 100644 api/rocketmq/MessageType.h create mode 100644 example/rocketmq/ExampleTransactionProducer.cpp create mode 100644 src/main/cpp/base/include/DigestType.h create mode 100644 src/main/cpp/base/include/Encoding.h diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 697b0b4d7..94532d110 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -114,6 +114,8 @@ class DefaultMQProducer { void setRegion(const std::string& region); + TransactionPtr prepare(MQMessage& message); + private: std::shared_ptr impl_; }; diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 7efa448e8..23f4169cd 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -7,7 +7,7 @@ #include #include -#include "RocketMQ.h" +#include "MessageType.h" ROCKETMQ_NAMESPACE_BEGIN @@ -69,6 +69,9 @@ class MQMessage { const std::map& getProperties() const; void setProperties(const std::map& properties); + void messageType(MessageType message_type); + MessageType messageType() const; + protected: MessageImpl* impl_; diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/MessageType.h new file mode 100644 index 000000000..01d01721c --- /dev/null +++ b/api/rocketmq/MessageType.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class MessageType : int8_t { + NORMAL = 0, + FIFO = 1, + DELAY = 2, + TRANSACTION = 3, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index 2265498ad..bc39c7940 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -1,7 +1,9 @@ #pragma once -#include "RocketMQ.h" #include +#include + +#include "RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -16,6 +18,8 @@ class Transaction { virtual bool rollback() = 0; }; +using TransactionPtr = std::unique_ptr; + enum class TransactionState : int8_t { COMMIT = 0, ROLLBACK = 1, diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel index 6e81e5088..a0655f86a 100644 --- a/example/rocketmq/BUILD.bazel +++ b/example/rocketmq/BUILD.bazel @@ -108,4 +108,14 @@ cc_binary( "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], +) + +cc_binary( + name = "example_transaction_producer", + srcs = [ + "ExampleTransactionProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], ) \ No newline at end of file diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/example/rocketmq/ExampleTransactionProducer.cpp new file mode 100644 index 000000000..04646cfed --- /dev/null +++ b/example/rocketmq/ExampleTransactionProducer.cpp @@ -0,0 +1,34 @@ +#include "rocketmq/DefaultMQProducer.h" +#include + +using namespace ROCKETMQ_NAMESPACE; + +int main(int argc, char* argv[]) { + DefaultMQProducer producer("TestGroup"); + + const char* topic = "cpp_sdk_standard"; + const char* name_server = "47.98.116.189:80"; + + producer.setNamesrvAddr(name_server); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + producer.setRegion("cn-hangzhou-pre"); + producer.setResourceNamespace(resource_namespace); + producer.setCredentialsProvider(std::make_shared()); + + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody("ABC"); + + producer.start(); + + auto transaction = producer.prepare(message); + + transaction->commit(); + + producer.shutdown(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index fdd51a6c9..1c50649dd 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -2,6 +2,7 @@ #include "MessageAccessor.h" #include "MessageImpl.h" #include "MixAll.h" +#include "Protocol.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" #include "rocketmq/MQMessageExt.h" @@ -111,4 +112,9 @@ void MQMessage::setProperties(const std::map& properti impl_->user_attribute_map_.insert({it.first, it.second}); } } + +void MQMessage::messageType(MessageType message_type) { impl_->system_attribute_.message_type = message_type; } + +MessageType MQMessage::messageType() const { return impl_->system_attribute_.message_type; } + ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/base/include/DigestType.h new file mode 100644 index 000000000..93b14cd94 --- /dev/null +++ b/src/main/cpp/base/include/DigestType.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class DigestType : int8_t { + CRC32 = 0, + MD5 = 1, + SHA1 = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/base/include/Encoding.h new file mode 100644 index 000000000..f3af8aab7 --- /dev/null +++ b/src/main/cpp/base/include/Encoding.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class Encoding : int8_t { + IDENTITY = 0, + GZIP = 1, + SNAPPY = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 5f4aa6d23..8ab32ab12 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -1,12 +1,16 @@ #pragma once -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "rocketmq/RocketMQ.h" #include #include #include +#include "absl/time/clock.h" +#include "absl/time/time.h" + +#include "DigestType.h" +#include "Encoding.h" +#include "rocketmq/MessageType.h" + ROCKETMQ_NAMESPACE_BEGIN class Protocol { @@ -14,31 +18,12 @@ class Protocol { static const char* PROTOCOL_VERSION; }; -enum class MessageType : int8_t { - NORMAL = 0, - FIFO = 1, - DELAY = 2, - TRANSACTION = 3, -}; - -enum class DigestType : int8_t { - CRC32 = 0, - MD5 = 1, - SHA1 = 2, -}; - struct Digest { DigestType digest_type{DigestType::MD5}; std::string checksum; Digest() = default; }; -enum class Encoding : int8_t { - IDENTITY = 0, - GZIP = 1, - SNAPPY = 2, -}; - struct Resource { /** * Abstract resource namespace diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 6ccac620c..a86ff5e01 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -320,24 +320,6 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met assert(cb); SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); RpcClientSharedPtr client = getRpcClient(target_host); - -#ifdef ENABLE_TRACING - nostd::shared_ptr span = nostd::shared_ptr(nullptr); - if (trace_) { - span = getTracer()->StartSpan("SendMessageAsync"); - - MQMessage message = context->getMessage(); - span->SetAttribute(TracingUtility::get().topic_, message.getTopic()); - span->SetAttribute(TracingUtility::get().tags_, message.getTags()); - span->SetAttribute(TracingUtility::get().msg_id_, context->getMessageId()); - - const std::string& serialized_span_context = TracingUtility::injectSpanContextToTraceParent(span->GetContext()); - request.mutable_message()->mutable_system_attribute()->set_trace_context(serialized_span_context); - } -#else - bool span = false; -#endif - // Invocation context will be deleted in its onComplete() method. auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; @@ -346,40 +328,20 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met } const std::string& topic = request.message().topic().name(); - auto completion_callback = [topic, cb, span, this](const InvocationContext* invocation_context) { + auto completion_callback = [topic, cb, this](const InvocationContext* invocation_context) { if (invocation_context->status.ok() && google::rpc::Code::OK == invocation_context->response.common().status().code()) { - -#ifdef ENABLE_TRACING - if (span) { - span->SetAttribute(TracingUtility::get().success_, true); - span->End(); - } -#else - (void)span; -#endif SendResult send_result; + send_result.setSendStatus(SendStatus::SEND_OK); send_result.setMsgId(invocation_context->response.message_id()); - send_result.setQueueOffset(-1); - MQMessageQueue message_queue; - message_queue.setQueueId(-1); - message_queue.setTopic(topic); - send_result.setMessageQueue(message_queue); + send_result.setTransactionId(invocation_context->response.transaction_id()); if (State::STARTED == state_.load(std::memory_order_relaxed)) { cb->onSuccess(send_result); } else { - SPDLOG_INFO("Client instance has stopped, state={}. Ignore send result {}", + SPDLOG_INFO("Client instance has stopped, state={}. Message[MessageId={}] ignored", state_.load(std::memory_order_relaxed), send_result.getMsgId()); } } else { - -#ifdef ENABLE_TRACING - if (span) { - span->SetAttribute(TracingUtility::get().success_, false); - span->End(); - } -#endif - if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", invocation_context->remote_address, invocation_context->status.error_code(), diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index 539cc6b41..f72e64fec 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -9,6 +9,7 @@ #include "MixAll.h" #include "ProducerImpl.h" #include "StaticNameServerResolver.h" +#include "rocketmq/Transaction.h" ROCKETMQ_NAMESPACE_BEGIN @@ -118,4 +119,9 @@ void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credential void DefaultMQProducer::setRegion(const std::string& region) { impl_->region(region); } +TransactionPtr DefaultMQProducer::prepare(MQMessage& message) { + auto transaction = impl_->prepare(message); + return transaction; +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index fe6bd3dc8..fb0a6d5c7 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -59,7 +59,7 @@ void ProducerImpl::ensureRunning() const { } } -void ProducerImpl::validate(const MQMessage& message) {} +bool ProducerImpl::validate(const MQMessage& message) { return MixAll::validate(message); } std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, const MQMessageQueue& message_queue) { @@ -79,15 +79,19 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM system_attribute->mutable_producer_group()->set_resource_namespace(resource_namespace_); system_attribute->mutable_producer_group()->set_name(group_name_); - // Set system flag if the message is transactional - const auto& properties = message.getProperties(); - auto search = properties.find(MixAll::PROPERTY_TRANSACTION_PREPARED_); - // TODO: set message type for normal/order/delay - if (search != properties.end()) { - const std::string& transactional_flag = search->second; - if ("true" == transactional_flag) { - system_attribute->set_message_type(rmq::MessageType::TRANSACTION); - } + switch (message.messageType()) { + case MessageType::NORMAL: + system_attribute->set_message_type(rmq::MessageType::NORMAL); + break; + case MessageType::DELAY: + system_attribute->set_message_type(rmq::MessageType::DELAY); + break; + case MessageType::TRANSACTION: + system_attribute->set_message_type(rmq::MessageType::TRANSACTION); + break; + case MessageType::FIFO: + system_attribute->set_message_type(rmq::MessageType::FIFO); + break; } if (message.bodyLength() >= compress_body_threshold_) { @@ -325,6 +329,13 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times) { assert(callback); + + if (!validate(message)) { + MQClientException e("Message is illegal", MESSAGE_ILLEGAL, __FILE__, __LINE__); + callback->onException(e); + return; + } + if (list.empty()) { MQClientException e("Topic route not found", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); callback->onException(e); @@ -472,8 +483,9 @@ void ProducerImpl::isolateEndpoint(const std::string& target) { isolated_endpoints_.insert(target); } -std::unique_ptr ProducerImpl::prepare(const MQMessage& message) { +std::unique_ptr ProducerImpl::prepare(MQMessage& message) { try { + message.messageType(MessageType::TRANSACTION); SendResult send_result = send(message); return std::unique_ptr(new TransactionImpl( send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index cddcb6898..8b067ca0d 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -2,8 +2,8 @@ #include "ProducerImpl.h" #include "TransactionImpl.h" -#include "opencensus/trace/span.h" #include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" #include "rocketmq/Logger.h" #include "rocketmq/MQMessageQueue.h" #include "spdlog/spdlog.h" @@ -52,6 +52,7 @@ void RetrySendCallback::onSuccess(SendResult& send_result) { span_.SetStatus(opencensus::trace::StatusCode::OK); span_.End(); } + send_result.setMessageQueue(messageQueue()); send_result.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); callback_->onSuccess(send_result); delete this; diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 7fc2e5b7e..d59518fbd 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -9,7 +9,7 @@ bool TransactionImpl::commit() { return false; } - return producer->commit(message_id_, transaction_id_, endpoint_, trace_context_); + return producer->commit(message_id_, transaction_id_, trace_context_, endpoint_); } bool TransactionImpl::rollback() { @@ -17,7 +17,7 @@ bool TransactionImpl::rollback() { if (!producer) { return false; } - return producer->rollback(message_id_, transaction_id_, endpoint_, trace_context_); + return producer->rollback(message_id_, transaction_id_, trace_context_, endpoint_); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index c1cd31fc5..043fdc4fb 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -50,7 +50,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); - std::unique_ptr prepare(const MQMessage& message); + std::unique_ptr prepare(MQMessage& message); bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); @@ -126,7 +126,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void ensureRunning() const; - void validate(const MQMessage& message); + bool validate(const MQMessage& message); void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index 658e0bb3a..bdcc92803 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -1,14 +1,16 @@ #pragma once -#include "TransactionImpl.h" +#include + #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "apache/rocketmq/v1/service.grpc.pb.h" #include "opencensus/trace/span.h" + +#include "TransactionImpl.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -78,11 +80,12 @@ class RetrySendCallback : public SendCallback { MQMessage message_; int attempt_times_{0}; int max_attempt_times_; - SendCallback* callback_; + SendCallback *callback_{nullptr}; /** - * @brief Once the first publish attempt failed, the following routable message queues are employed. - * + * @brief Once the first publish attempt failed, the following routable + * message queues are employed. + * */ std::vector candidates_; From 2c1969d0b2a7fcec814c36c2a8d9cbb81f6d060b Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 22 Sep 2021 16:42:35 +0800 Subject: [PATCH 068/133] Fix unit tests --- src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index e827a1ede..3d8b3d316 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -66,6 +66,7 @@ class DefaultMQProducerUnitTest : public MQClientTest { std::string access_key_{"access_key"}; std::string access_secret_{"access_secret"}; std::shared_ptr credentials_provider_; + std::string body_{"Test message body"}; }; TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { @@ -135,8 +136,10 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); + MQMessage message; message.setTopic(topic_); + message.setBody(body_); absl::Mutex mtx; absl::CondVar cv; @@ -158,7 +161,11 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); + MQMessage message; + message.setTopic(topic_); + message.setBody(body_); + SendResult send_result = producer->send(message); ASSERT_EQ(send_result.getMsgId(), message_id_); producer->shutdown(); From 6cebe5430ee819aa14aed4f97f05b227c0da5c4e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 22 Sep 2021 17:14:26 +0800 Subject: [PATCH 069/133] Expose message-id and transaction-id out of Transaction --- api/rocketmq/Transaction.h | 5 +++++ src/main/cpp/rocketmq/TransactionImpl.cpp | 5 +++++ src/main/cpp/rocketmq/include/TransactionImpl.h | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index bc39c7940..b519b2782 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -2,6 +2,7 @@ #include #include +#include #include "RocketMQ.h" @@ -16,6 +17,10 @@ class Transaction { virtual bool commit() = 0; virtual bool rollback() = 0; + + virtual std::string messageId() const = 0; + + virtual std::string transactionId() const = 0; }; using TransactionPtr = std::unique_ptr; diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index d59518fbd..4a4a65acc 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -1,5 +1,6 @@ #include "TransactionImpl.h" #include "ProducerImpl.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -20,4 +21,8 @@ bool TransactionImpl::rollback() { return producer->rollback(message_id_, transaction_id_, trace_context_, endpoint_); } +std::string TransactionImpl::messageId() const { return message_id_; } + +std::string TransactionImpl::transactionId() const { return transaction_id_; } + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 0074a54e8..7fac1353f 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -22,6 +22,10 @@ class TransactionImpl : public Transaction { bool rollback() override; + std::string messageId() const override; + + std::string transactionId() const override; + private: std::string message_id_; std::string transaction_id_; From 600cb66b1c6455c57f826c18344d1f7e617c55d8 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 23 Sep 2021 14:37:03 +0800 Subject: [PATCH 070/133] Fix group_name issue and virtual inheritance issue (#22) * Fix include order * Fix group-name issue * Fix virtual inheritance issue --- .bazelrc | 4 +- api/rocketmq/AsyncCallback.h | 3 + api/rocketmq/CommunicationMode.h | 14 ----- api/rocketmq/Credentials.h | 3 +- api/rocketmq/CredentialsProvider.h | 14 ++--- api/rocketmq/DefaultMQPushConsumer.h | 3 +- api/rocketmq/Executor.h | 3 +- api/rocketmq/ExpressionType.h | 1 + api/rocketmq/LocalTransactionStateChecker.h | 3 +- api/rocketmq/Logger.h | 1 + api/rocketmq/MQClientException.h | 12 ++-- api/rocketmq/MQMessageExt.h | 3 +- api/rocketmq/MQSelector.h | 8 ++- api/rocketmq/MessageModel.h | 1 + api/rocketmq/PullResult.h | 3 +- api/rocketmq/SendResult.h | 4 +- example/rocketmq/ExamplePushConsumer.cpp | 18 +++--- src/main/cpp/admin/include/ServerCall.h | 4 +- src/main/cpp/base/include/Histogram.h | 1 + src/main/cpp/base/include/HostInfo.h | 3 +- src/main/cpp/base/include/HttpClientImpl.h | 3 +- src/main/cpp/base/include/InvocationContext.h | 20 ++++--- src/main/cpp/base/include/MessageAccessor.h | 7 +-- src/main/cpp/base/include/MessageImpl.h | 2 + src/main/cpp/base/include/MetadataConstants.h | 1 + src/main/cpp/base/include/MixAll.h | 9 +-- src/main/cpp/base/include/RateLimiter.h | 3 +- src/main/cpp/base/include/ThreadPool.h | 3 +- src/main/cpp/base/include/ThreadPoolImpl.h | 12 ++-- src/main/cpp/base/include/UniqueIdGenerator.h | 9 +-- src/main/cpp/client/ClientConfigImpl.cpp | 11 ++-- src/main/cpp/client/include/Assignment.h | 16 +++-- src/main/cpp/client/include/Broker.h | 3 +- src/main/cpp/client/include/Client.h | 6 +- src/main/cpp/client/include/ClientConfig.h | 7 ++- .../cpp/client/include/ClientConfigImpl.h | 49 ++++++++------- src/main/cpp/client/include/ClientManager.h | 5 +- .../cpp/client/include/ClientManagerFactory.h | 5 +- .../cpp/client/include/ClientManagerImpl.h | 27 +++++---- .../cpp/client/include/ConsumeMessageType.h | 4 +- .../client/include/HeartbeatDataCallback.h | 1 - .../client/include/InterceptorContinuation.h | 1 + src/main/cpp/client/include/LogInterceptor.h | 1 + .../client/include/LogInterceptorFactory.h | 1 + .../include/OrphanTransactionCallback.h | 1 - .../cpp/client/include/ReceiveMessageResult.h | 8 +-- src/main/cpp/client/include/RpcClient.h | 18 +++--- src/main/cpp/client/include/RpcClientImpl.h | 5 +- .../cpp/client/include/SendMessageContext.h | 5 +- src/main/cpp/client/include/ServiceAddress.h | 6 +- src/main/cpp/client/include/Signature.h | 5 +- src/main/cpp/client/include/Topic.h | 3 +- .../cpp/client/include/TopicAssignmentInfo.h | 5 +- .../cpp/client/include/TopicPublishInfo.h | 5 +- .../client/include/TopicRouteChangeCallback.h | 5 +- src/main/cpp/client/include/TopicRouteData.h | 5 +- src/main/cpp/client/include/TracingUtility.h | 59 ------------------- .../cpp/concurrent/include/CountdownLatch.h | 4 +- src/main/cpp/rocketmq/ClientImpl.cpp | 2 +- .../ConsumeStandardMessageService.cpp | 21 +++---- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 3 +- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 20 ++----- src/main/cpp/rocketmq/ProducerImpl.cpp | 14 +++-- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 2 +- .../cpp/rocketmq/include/AwaitPullCallback.h | 1 + src/main/cpp/rocketmq/include/ClientImpl.h | 5 +- src/main/cpp/rocketmq/include/Consumer.h | 5 +- .../cpp/rocketmq/include/FilterExpression.h | 4 +- src/main/cpp/rocketmq/include/ProcessQueue.h | 3 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 10 +++- .../cpp/rocketmq/include/PullConsumerImpl.h | 4 +- src/main/cpp/rocketmq/include/PushConsumer.h | 5 +- .../cpp/rocketmq/include/PushConsumerImpl.h | 4 +- .../rocketmq/include/ReceiveMessageAction.h | 4 +- .../include/StaticNameServerResolver.h | 2 +- .../cpp/rocketmq/include/TransactionImpl.h | 4 +- .../cpp/scheduler/include/SchedulerImpl.h | 4 +- .../tracing/exporters/include/OtlpExporter.h | 18 +++--- src/test/cpp/it/RpcClientTest.cpp | 4 +- src/test/cpp/it/TopicPublishInfoTest.cpp | 8 +-- src/test/cpp/ut/rocketmq/BUILD.bazel | 11 ++++ .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 18 ++++++ 82 files changed, 324 insertions(+), 298 deletions(-) delete mode 100644 api/rocketmq/CommunicationMode.h delete mode 100644 src/main/cpp/client/include/TracingUtility.h create mode 100644 src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp diff --git a/.bazelrc b/.bazelrc index 5dc7579e3..e92ef362c 100644 --- a/.bazelrc +++ b/.bazelrc @@ -127,4 +127,6 @@ build:coverage --combined_report=lcov build:coverage --strategy=TestRunner=sandboxed,local build:coverage --strategy=CoverageReport=sandboxed,local build:coverage --collect_code_coverage -build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:],-//src/main/cpp/base/mocks[/:],-//src/main/cpp/client/mocks[/:],-//src/main/cpp/rocketmq/mocks[/:]" \ No newline at end of file +build:coverage --instrumentation_filter="//src/main/cpp[/:],//api[/:],-//src/main/cpp/base/mocks[/:],-//src/main/cpp/client/mocks[/:],-//src/main/cpp/rocketmq/mocks[/:]" + +test --test_output=errors \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index 44d89cdee..bb431a1cb 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -15,8 +15,11 @@ using sendCallbackType = SendCallbackType; class SendCallback : public AsyncCallback { public: virtual ~SendCallback() = default; + virtual void onSuccess(SendResult& send_result) = 0; + virtual void onException(const MQException& e) = 0; + virtual SendCallbackType getSendCallbackType() { return SendCallbackType::noAutoDeleteSendCallback; } }; diff --git a/api/rocketmq/CommunicationMode.h b/api/rocketmq/CommunicationMode.h deleted file mode 100644 index ad29a4906..000000000 --- a/api/rocketmq/CommunicationMode.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum CommunicationMode -{ - ComMode_SYNC, - ComMode_ASYNC, - ComMode_ONEWAY -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index 1b19a2f22..33374f637 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -1,10 +1,11 @@ #pragma once -#include "rocketmq/RocketMQ.h" #include #include #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN class Credentials { diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index 567220a16..93ba8a2a1 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -1,11 +1,11 @@ #pragma once -#include "Credentials.h" - -#include #include -#include #include +#include +#include + +#include "Credentials.h" ROCKETMQ_NAMESPACE_BEGIN @@ -44,6 +44,7 @@ class EnvironmentVariablesCredentialsProvider : public CredentialsProvider { static const char* ENVIRONMENT_ACCESS_KEY; static const char* ENVIRONMENT_ACCESS_SECRET; + private: std::string access_key_; std::string access_secret_; @@ -65,9 +66,8 @@ class ConfigFileCredentialsProvider : public CredentialsProvider { * For test purpose only. * @return */ - static const char* credentialFile() { - return CREDENTIAL_FILE_; - } + static const char* credentialFile() { return CREDENTIAL_FILE_; } + private: std::chrono::system_clock::duration refresh_interval_{std::chrono::seconds(10)}; std::string access_key_; diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index 176d90953..f20f9c26b 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -92,9 +92,10 @@ class DefaultMQPushConsumer { void setMessageModel(MessageModel message_model); + std::string groupName() const; + private: std::shared_ptr impl_; - std::string group_name_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Executor.h b/api/rocketmq/Executor.h index 1d78e8252..ccd4e0f41 100644 --- a/api/rocketmq/Executor.h +++ b/api/rocketmq/Executor.h @@ -1,8 +1,9 @@ #pragma once -#include "RocketMQ.h" #include +#include "RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN using Executor = std::function&)>; diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h index dcafc24e0..93855af3b 100644 --- a/api/rocketmq/ExpressionType.h +++ b/api/rocketmq/ExpressionType.h @@ -1,6 +1,7 @@ #pragma once #include + #include "RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/api/rocketmq/LocalTransactionStateChecker.h index 6d0bef618..0badebe59 100644 --- a/api/rocketmq/LocalTransactionStateChecker.h +++ b/api/rocketmq/LocalTransactionStateChecker.h @@ -1,8 +1,9 @@ #pragma once +#include + #include "MQMessageExt.h" #include "Transaction.h" -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h index 24b0b3484..dec8778f3 100644 --- a/api/rocketmq/Logger.h +++ b/api/rocketmq/Logger.h @@ -6,6 +6,7 @@ #include #include #include + #include "RocketMQ.h" #ifndef SPDLOG_ACTIVE_LEVEL diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h index 5c6c0f97f..3b1385438 100644 --- a/api/rocketmq/MQClientException.h +++ b/api/rocketmq/MQClientException.h @@ -11,7 +11,7 @@ ROCKETMQ_NAMESPACE_BEGIN class MQException : public std::exception { public: - MQException(const std::string& msg, int error, const char* file, int line) throw() + MQException(const std::string& msg, int error, const char* file, int line) noexcept : m_error(error), m_line(line), m_file(file) { try { std::stringstream ss; @@ -21,7 +21,7 @@ class MQException : public std::exception { } } - MQException(const std::string& msg, int error, const char* file, const char* type, int line) throw() + MQException(const std::string& msg, int error, const char* file, const char* type, int line) noexcept : m_error(error), m_line(line), m_file(file), m_type(type) { try { std::stringstream ss; @@ -31,13 +31,13 @@ class MQException : public std::exception { } } - virtual ~MQException() throw() {} + ~MQException() noexcept override = default; - const char* what() const throw() { return m_msg.c_str(); } + const char* what() const noexcept override { return m_msg.c_str(); } - int GetError() const throw() { return m_error; } + int GetError() const noexcept { return m_error; } - virtual const char* GetType() const throw() { return m_type.c_str(); } + virtual const char* GetType() const noexcept { return m_type.c_str(); } protected: int m_error; diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h index c814c8ed3..ad5942e55 100644 --- a/api/rocketmq/MQMessageExt.h +++ b/api/rocketmq/MQMessageExt.h @@ -1,8 +1,9 @@ #pragma once -#include "MQMessage.h" #include +#include "MQMessage.h" + ROCKETMQ_NAMESPACE_BEGIN class MessageAccessor; diff --git a/api/rocketmq/MQSelector.h b/api/rocketmq/MQSelector.h index 1131691e1..029c6f89a 100644 --- a/api/rocketmq/MQSelector.h +++ b/api/rocketmq/MQSelector.h @@ -1,15 +1,17 @@ #pragma once #include -#include "MQMessageQueue.h" + #include "MQMessage.h" +#include "MQMessageQueue.h" ROCKETMQ_NAMESPACE_BEGIN class MessageQueueSelector { public: - virtual ~MessageQueueSelector() {} - virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; + virtual ~MessageQueueSelector() = default; + + virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h index 6f9f5a7e8..12fb37f70 100644 --- a/api/rocketmq/MessageModel.h +++ b/api/rocketmq/MessageModel.h @@ -1,4 +1,5 @@ #pragma once + #include "RocketMQ.h" #include diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h index 473cae3fe..acd611eaf 100644 --- a/api/rocketmq/PullResult.h +++ b/api/rocketmq/PullResult.h @@ -1,9 +1,10 @@ #pragma once -#include "MQMessageExt.h" #include #include +#include "MQMessageExt.h" + ROCKETMQ_NAMESPACE_BEGIN class PullResult { diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 9eba74699..961d229c5 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -83,10 +83,10 @@ class SendResult { } private: - SendStatus send_status_; + SendStatus send_status_{SendStatus::SEND_OK}; std::string message_id_; mutable MQMessageQueue message_queue_; - long long queue_offset_; + long long queue_offset_{0}; std::string transaction_id_; std::string region_id_; std::string trace_context_; diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 40c1c14f1..a319345cc 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -1,11 +1,14 @@ -#include "rocketmq/DefaultMQPushConsumer.h" -#include "rocketmq/Logger.h" -#include "spdlog/spdlog.h" #include #include #include #include +#include "rocketmq/Logger.h" + +#include "spdlog/spdlog.h" + +#include "rocketmq/DefaultMQPushConsumer.h" + using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { @@ -13,6 +16,7 @@ class SampleMQMessageListener : public StandardMessageListener { ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); + std::cout << "Consume Message[MsgId=" << msg.getMsgId() << "] OK" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } return ConsumeMessageResult::SUCCESS; @@ -25,20 +29,20 @@ int main(int argc, char* argv[]) { logger.setLevel(Level::Debug); logger.init(); - const char* cid = "GID_cpp_sdk_standard"; + const char* group_id = "GID_cpp_sdk_standard"; const char* topic = "cpp_sdk_standard"; const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; - DefaultMQPushConsumer push_consumer(cid); + DefaultMQPushConsumer push_consumer(group_id); push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); push_consumer.setNamesrvAddr("47.98.116.189:80"); MessageListener* listener = new SampleMQMessageListener; - push_consumer.setGroupName(cid); push_consumer.setInstanceName("instance_0"); + // push_consumer.setGroupName(group_id); push_consumer.subscribe(topic, "*"); push_consumer.registerMessageListener(listener); - push_consumer.setConsumeThreadCount(2); + push_consumer.setConsumeThreadCount(4); push_consumer.start(); std::this_thread::sleep_for(std::chrono::seconds(300)); diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h index 213cf3c17..bcb3ccc13 100644 --- a/src/main/cpp/admin/include/ServerCall.h +++ b/src/main/cpp/admin/include/ServerCall.h @@ -1,8 +1,10 @@ #pragma once +#include + #include "apache/rocketmq/v1/admin.grpc.pb.h" + #include "rocketmq/RocketMQ.h" -#include namespace rmq = apache::rocketmq::v1; diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h index 573bf7c88..e62d5548a 100644 --- a/src/main/cpp/base/include/Histogram.h +++ b/src/main/cpp/base/include/Histogram.h @@ -5,6 +5,7 @@ #include #include #include + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h index 68b8bfa91..301970ff4 100644 --- a/src/main/cpp/base/include/HostInfo.h +++ b/src/main/cpp/base/include/HostInfo.h @@ -1,8 +1,9 @@ #pragma once -#include "rocketmq/RocketMQ.h" #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN struct HostInfo { std::string site_; diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h index 8e42a3315..e8f37aef5 100644 --- a/src/main/cpp/base/include/HttpClientImpl.h +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -1,12 +1,11 @@ #pragma once -#include "HttpClient.h" - #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "httplib.h" +#include "HttpClient.h" #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 32ef5a7c2..276bb645c 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -1,18 +1,20 @@ #pragma once +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "grpcpp/client_context.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/impl/codegen/async_stream.h" +#include "grpcpp/impl/codegen/async_unary_call.h" + #include "LoggerImpl.h" #include "MetadataConstants.h" #include "UniqueIdGenerator.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/time.h" #include "rocketmq/RocketMQ.h" -#include -#include -#include -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h index 9f6ca757e..9e1b3b8ea 100644 --- a/src/main/cpp/base/include/MessageAccessor.h +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -1,7 +1,6 @@ #pragma once #include "rocketmq/MQMessageExt.h" -#include "rocketmq/RocketMQ.h" #include "Protocol.h" @@ -28,9 +27,9 @@ class MessageAccessor { static void setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times); - static void setDecodedTimestamp(MQMessageExt &message, absl::Time decode_timestamp); - static absl::Time decodedTimestamp(const MQMessageExt &message); - + static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); + static absl::Time decodedTimestamp(const MQMessageExt& message); + static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); static void setReceiptHandle(MQMessageExt& message, std::string receipt_handle); diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h index 36d3d74ed..8572a0793 100644 --- a/src/main/cpp/base/include/MessageImpl.h +++ b/src/main/cpp/base/include/MessageImpl.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "Protocol.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index bb5c2c947..a9dde76c4 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -1,4 +1,5 @@ #pragma once + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 11aac599d..0eb5e72ab 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -1,13 +1,14 @@ #pragma once -#include "absl/strings/string_view.h" -#include "re2/re2.h" -#include "rocketmq/MQMessage.h" - #include #include #include +#include "absl/strings/string_view.h" +#include "re2/re2.h" + +#include "rocketmq/MQMessage.h" + ROCKETMQ_NAMESPACE_BEGIN class MixAll { diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h index 805939891..060bbd023 100644 --- a/src/main/cpp/base/include/RateLimiter.h +++ b/src/main/cpp/base/include/RateLimiter.h @@ -1,6 +1,5 @@ #pragma once -#include "rocketmq/RocketMQ.h" #include #include #include @@ -10,6 +9,8 @@ #include #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN class Tick { diff --git a/src/main/cpp/base/include/ThreadPool.h b/src/main/cpp/base/include/ThreadPool.h index 4e6044ec3..967ad26a2 100644 --- a/src/main/cpp/base/include/ThreadPool.h +++ b/src/main/cpp/base/include/ThreadPool.h @@ -1,8 +1,9 @@ #pragma once -#include "rocketmq/RocketMQ.h" #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN class ThreadPool { diff --git a/src/main/cpp/base/include/ThreadPoolImpl.h b/src/main/cpp/base/include/ThreadPoolImpl.h index 43090065e..fad920408 100644 --- a/src/main/cpp/base/include/ThreadPoolImpl.h +++ b/src/main/cpp/base/include/ThreadPoolImpl.h @@ -1,10 +1,5 @@ #pragma once -#include "ThreadPool.h" -#include "absl/synchronization/mutex.h" -#include "asio/io_context.hpp" -#include "rocketmq/RocketMQ.h" -#include "rocketmq/State.h" #include #include #include @@ -12,13 +7,18 @@ #include #include +#include "absl/synchronization/mutex.h" #include "asio.hpp" +#include "asio/io_context.hpp" + +#include "ThreadPool.h" +#include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN class ThreadPoolImpl : public ThreadPool { public: - ThreadPoolImpl(std::uint16_t workers); + explicit ThreadPoolImpl(std::uint16_t workers); ~ThreadPoolImpl() override = default; diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h index e72cb7e99..ec6cf50e6 100644 --- a/src/main/cpp/base/include/UniqueIdGenerator.h +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -1,13 +1,14 @@ #pragma once -#include "rocketmq/RocketMQ.h" -#include "absl/base/thread_annotations.h" -#include "absl/synchronization/mutex.h" - #include #include #include +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" + +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN class UniqueIdGenerator { diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index 650305e58..ff711566e 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -1,11 +1,13 @@ #include "ClientConfigImpl.h" -#include "UtilAll.h" + #include #ifndef _WIN32 #include #endif +#include "UtilAll.h" + ROCKETMQ_NAMESPACE_BEGIN #ifndef CLIENT_VERSION_MAJOR @@ -22,10 +24,9 @@ ROCKETMQ_NAMESPACE_BEGIN const char* ClientConfigImpl::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; -ClientConfigImpl::ClientConfigImpl() : ClientConfigImpl(std::string()) {} - -ClientConfigImpl::ClientConfigImpl(std::string group_name) - : group_name_(std::move(group_name)), io_timeout_(absl::Seconds(3)), long_polling_timeout_(absl::Seconds(30)) { +ClientConfigImpl::ClientConfigImpl(absl::string_view group_name) + : group_name_(group_name.data(), group_name.length()), io_timeout_(absl::Seconds(3)), + long_polling_timeout_(absl::Seconds(30)) { instance_name_ = "DEFAULT"; } diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index 17cfdb3fa..220c6ad17 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -1,18 +1,18 @@ #pragma once -#include "ConsumeMessageType.h" -#include "rocketmq/MQMessageQueue.h" #include #include +#include "ConsumeMessageType.h" +#include "rocketmq/MQMessageQueue.h" + ROCKETMQ_NAMESPACE_BEGIN class Assignment { public: - Assignment(MQMessageQueue message_queue) - : message_queue_(std::move(message_queue)) {} + Assignment(MQMessageQueue message_queue) : message_queue_(std::move(message_queue)) {} - bool operator==(const Assignment &rhs) const { + bool operator==(const Assignment& rhs) const { if (this == &rhs) { return true; } @@ -20,11 +20,9 @@ class Assignment { return message_queue_ == rhs.message_queue_; } - bool operator<(const Assignment &rhs) const { - return message_queue_ < rhs.message_queue_; - } + bool operator<(const Assignment& rhs) const { return message_queue_ < rhs.message_queue_; } - const MQMessageQueue &messageQueue() const { return message_queue_; } + const MQMessageQueue& messageQueue() const { return message_queue_; } private: MQMessageQueue message_queue_; diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h index 5c40c480a..1fc17e111 100644 --- a/src/main/cpp/client/include/Broker.h +++ b/src/main/cpp/client/include/Broker.h @@ -1,9 +1,10 @@ #pragma once -#include "ServiceAddress.h" #include #include +#include "ServiceAddress.h" + ROCKETMQ_NAMESPACE_BEGIN class Broker { diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index 847dac9e9..d029598f2 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -1,9 +1,11 @@ #pragma once -#include "ClientConfig.h" -#include "absl/container/flat_hash_set.h" #include +#include "absl/container/flat_hash_set.h" + +#include "ClientConfig.h" + ROCKETMQ_NAMESPACE_BEGIN class Client : virtual public ClientConfig { diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index b5ed3d15a..ec3f529f4 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -1,10 +1,11 @@ #pragma once -#include "rocketmq/CredentialsProvider.h" -#include "absl/time/time.h" -#include "rocketmq/RocketMQ.h" #include +#include "absl/time/time.h" + +#include "rocketmq/CredentialsProvider.h" + ROCKETMQ_NAMESPACE_BEGIN class ClientConfig { diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index f106d5805..d45392d3b 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -1,49 +1,55 @@ #pragma once -#include "ClientConfig.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "rocketmq/RocketMQ.h" #include #include #include #include #include +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +#include "ClientConfig.h" + ROCKETMQ_NAMESPACE_BEGIN class ClientConfigImpl : virtual public ClientConfig { public: - ClientConfigImpl(); - - explicit ClientConfigImpl(std::string group_name); + explicit ClientConfigImpl(absl::string_view group_name); ~ClientConfigImpl() override = default; - const std::string& resourceNamespace() const override { return resource_namespace_; } + const std::string &resourceNamespace() const override { + return resource_namespace_; + } void resourceNamespace(absl::string_view resource_namespace) { - resource_namespace_ = std::string(resource_namespace.data(), resource_namespace.length()); + resource_namespace_ = + std::string(resource_namespace.data(), resource_namespace.length()); } std::string clientId() const override; - const std::string& getInstanceName() const; + const std::string &getInstanceName() const; void setInstanceName(std::string instance_name); - const std::string& getGroupName() const override; + const std::string &getGroupName() const override; void setGroupName(std::string group_name); - const std::string& getUnitName() const { return unit_name_; } + const std::string &getUnitName() const { return unit_name_; } void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } absl::Duration getIoTimeout() const override; void setIoTimeout(absl::Duration timeout); - absl::Duration getLongPollingTimeout() const override { return long_polling_timeout_; } + absl::Duration getLongPollingTimeout() const override { + return long_polling_timeout_; + } - void setLongPollingTimeout(absl::Duration timeout) { long_polling_timeout_ = timeout; } + void setLongPollingTimeout(absl::Duration timeout) { + long_polling_timeout_ = timeout; + } bool isTracingEnabled() { return enable_tracing_.load(); } void enableTracing(bool enabled) { enable_tracing_.store(enabled); } @@ -51,16 +57,18 @@ class ClientConfigImpl : virtual public ClientConfig { CredentialsProviderPtr credentialsProvider() override; void setCredentialsProvider(CredentialsProviderPtr credentials_provider); - void serviceName(std::string service_name) { service_name_ = std::move(service_name); } - const std::string& serviceName() const override { return service_name_; } + void serviceName(std::string service_name) { + service_name_ = std::move(service_name); + } + const std::string &serviceName() const override { return service_name_; } void region(std::string region) { region_ = std::move(region); } - const std::string& region() const override { return region_; } + const std::string ®ion() const override { return region_; } void tenantId(std::string tenant_id) { tenant_id_ = std::move(tenant_id); } - const std::string& tenantId() const override { return tenant_id_; } + const std::string &tenantId() const override { return tenant_id_; } - static const char* CLIENT_VERSION; + static const char *CLIENT_VERSION; protected: /** @@ -74,7 +82,8 @@ class ClientConfigImpl : virtual public ClientConfig { std::string region_; /** - * RocketMQ instance namespace, in which topic, consumer group and any other abstract resources remain unique. + * RocketMQ instance namespace, in which topic, consumer group and any other + * abstract resources remain unique. */ std::string resource_namespace_; diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 8ebb2eb02..b98a6d3cc 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "Client.h" #include "ReceiveMessageCallback.h" #include "RpcClient.h" @@ -7,8 +10,6 @@ #include "TopAddressing.h" #include "TopicRouteData.h" #include "rocketmq/MQMessageExt.h" -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h index 98405f32f..d1baf8548 100644 --- a/src/main/cpp/client/include/ClientManagerFactory.h +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -3,11 +3,12 @@ #include #include -#include "ClientConfig.h" -#include "ClientManager.h" #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" + +#include "ClientConfig.h" +#include "ClientManager.h" #include "rocketmq/AdminServer.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 050ad9101..ba56c479a 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -1,5 +1,19 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + #include "Client.h" #include "ClientManager.h" #include "HeartbeatDataCallback.h" @@ -15,21 +29,8 @@ #include "TopAddressing.h" #include "TopicRouteChangeCallback.h" #include "TopicRouteData.h" -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" #include "rocketmq/AsyncCallback.h" -#include "rocketmq/CommunicationMode.h" #include "rocketmq/State.h" -#include -#include -#include -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h index 3f649ad37..e57b5b34e 100644 --- a/src/main/cpp/client/include/ConsumeMessageType.h +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -1,9 +1,9 @@ #pragma once -#include "rocketmq/RocketMQ.h" - #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN enum class ConsumeMessageType : int8_t { diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h index 9be195d8a..f105c7066 100644 --- a/src/main/cpp/client/include/HeartbeatDataCallback.h +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -1,7 +1,6 @@ #pragma once #include "RpcClient.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h index 399038685..05a73d23b 100644 --- a/src/main/cpp/client/include/InterceptorContinuation.h +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -1,6 +1,7 @@ #pragma once #include "grpcpp/impl/codegen/interceptor.h" + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h index f270a64a0..a14bbf00f 100644 --- a/src/main/cpp/client/include/LogInterceptor.h +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -1,4 +1,5 @@ #include "grpcpp/impl/codegen/client_interceptor.h" + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/LogInterceptorFactory.h b/src/main/cpp/client/include/LogInterceptorFactory.h index c3560909e..85c07191d 100644 --- a/src/main/cpp/client/include/LogInterceptorFactory.h +++ b/src/main/cpp/client/include/LogInterceptorFactory.h @@ -1,6 +1,7 @@ #pragma once #include "grpcpp/impl/codegen/client_interceptor.h" + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/src/main/cpp/client/include/OrphanTransactionCallback.h index 356fd6426..b320650f8 100644 --- a/src/main/cpp/client/include/OrphanTransactionCallback.h +++ b/src/main/cpp/client/include/OrphanTransactionCallback.h @@ -1,7 +1,6 @@ #pragma once #include "rocketmq/MQMessage.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index 026f5fd3d..badf8e10a 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -1,13 +1,13 @@ #pragma once -#include "MixAll.h" -#include "absl/time/time.h" -#include "rocketmq/MQMessageExt.h" - #include #include #include +#include "MixAll.h" +#include "absl/time/time.h" +#include "rocketmq/MQMessageExt.h" + ROCKETMQ_NAMESPACE_BEGIN enum class ReceiveMessageStatus : int32_t { diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index e9b9d71f9..416001e94 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -1,19 +1,19 @@ #pragma once -#include "apache/rocketmq/v1/service.pb.h" -#include "grpcpp/grpcpp.h" - -#include "InvocationContext.h" -#include "OrphanTransactionCallback.h" +#include +#include +#include +#include #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "apache/rocketmq/v1/definition.grpc.pb.h" #include "apache/rocketmq/v1/service.grpc.pb.h" -#include -#include -#include -#include +#include "apache/rocketmq/v1/service.pb.h" +#include "grpcpp/grpcpp.h" + +#include "InvocationContext.h" +#include "OrphanTransactionCallback.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index cc0439d69..fbdbe40be 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -1,9 +1,10 @@ #pragma once -#include "RpcClient.h" +#include #include "absl/container/flat_hash_map.h" -#include + +#include "RpcClient.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index f3a19dcb7..a54bb8e5d 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -1,8 +1,9 @@ #pragma once + +#include + #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" -#include "rocketmq/RocketMQ.h" -#include ROCKETMQ_NAMESPACE_BEGIN class SendMessageContext { diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h index 7c28d674b..067e73494 100644 --- a/src/main/cpp/client/include/ServiceAddress.h +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -1,11 +1,13 @@ #pragma once -#include "absl/strings/string_view.h" -#include "rocketmq/RocketMQ.h" #include #include #include +#include "absl/strings/string_view.h" + +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN enum AddressScheme : int8_t { diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h index f617eccd8..7d9c0c410 100644 --- a/src/main/cpp/client/include/Signature.h +++ b/src/main/cpp/client/include/Signature.h @@ -1,6 +1,9 @@ -#include "ClientConfig.h" +#pragma once + #include "absl/container/flat_hash_map.h" +#include "ClientConfig.h" + ROCKETMQ_NAMESPACE_BEGIN class Signature { diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h index 8778d9263..360f794ad 100644 --- a/src/main/cpp/client/include/Topic.h +++ b/src/main/cpp/client/include/Topic.h @@ -1,9 +1,10 @@ #pragma once -#include "rocketmq/RocketMQ.h" #include #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN class Topic { diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h index 9581afb8c..41ec33664 100644 --- a/src/main/cpp/client/include/TopicAssignmentInfo.h +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -1,10 +1,11 @@ #pragma once -#include "Assignment.h" -#include "RpcClient.h" #include #include +#include "Assignment.h" +#include "RpcClient.h" + ROCKETMQ_NAMESPACE_BEGIN class TopicAssignment { diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h index d07e49551..d4b18ddb9 100644 --- a/src/main/cpp/client/include/TopicPublishInfo.h +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -2,11 +2,12 @@ #include -#include "TopicRouteData.h" -#include "absl/container/flat_hash_set.h" #include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" + +#include "TopicRouteData.h" #include "rocketmq/MQMessageQueue.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/TopicRouteChangeCallback.h b/src/main/cpp/client/include/TopicRouteChangeCallback.h index 1415586df..0a8829e3d 100644 --- a/src/main/cpp/client/include/TopicRouteChangeCallback.h +++ b/src/main/cpp/client/include/TopicRouteChangeCallback.h @@ -1,11 +1,10 @@ #pragma once -#include "TopicRouteData.h" #include "absl/strings/string_view.h" -ROCKETMQ_NAMESPACE_BEGIN - +#include "TopicRouteData.h" +ROCKETMQ_NAMESPACE_BEGIN class TopicRouteChangeCallback { public: diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h index 4719ef4ef..2de0c8386 100644 --- a/src/main/cpp/client/include/TopicRouteData.h +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -1,10 +1,11 @@ #pragma once -#include "Partition.h" -#include "RpcClient.h" #include #include +#include "Partition.h" +#include "RpcClient.h" + ROCKETMQ_NAMESPACE_BEGIN namespace rmq = apache::rocketmq::v1; diff --git a/src/main/cpp/client/include/TracingUtility.h b/src/main/cpp/client/include/TracingUtility.h deleted file mode 100644 index c69a98f77..000000000 --- a/src/main/cpp/client/include/TracingUtility.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#ifdef ENABLE_TRACING -#include "opentelemetry/trace/span_context.h" -#include "rocketmq/RocketMQ.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -namespace trace = opentelemetry::trace; - -class TracingUtility { -public: - static TracingUtility& get(); - - static const int kTraceDelimiterBytes = 3; - // 0: version, 1: trace id, 2: span id, 3: trace flags - constexpr static const int kHeaderElementLengths[4] = {2, 32, 16, 2}; - static const int kHeaderSize = kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + - kHeaderElementLengths[3] + kTraceDelimiterBytes; - - static const int kTraceStateMaxMembers = 32; - static const int kVersionBytes = 2; - static const int kTraceIdBytes = 32; - static const int kSpanIdBytes = 16; - static const int kTraceFlagBytes = 2; - - static const std::string INVALID_TRACE_ID; - static const std::string INVALID_SPAN_ID; - - const std::string topic_ = "topic"; - const std::string consumer_group_ = "consumer_group"; - const std::string msg_id_ = "msg_id"; - const std::string tags_ = "tags"; - const std::string store_host_ = "store_host"; - const std::string success_ = "success"; - const std::string max_attempt_times_ = "retry_time"; - const std::string expired_ = "expired"; - - static std::string injectSpanContextToTraceParent(const trace::SpanContext& span_context); - - static trace::SpanContext extractContextFromTraceParent(const std::string& trace_parent); - -private: - static void generateHexFromString(const std::string& string, int bytes, uint8_t* buf); - - static uint8_t hexToInt(char c); - - static trace::TraceId generateTraceIdFromString(const std::string& trace_id); - - static bool isValidHex(const std::string& str); - - static trace::SpanId generateSpanIdFromString(const std::string& span_id); - - static trace::TraceFlags generateTraceFlagsFromString(std::string trace_flags); -}; - -ROCKETMQ_NAMESPACE_END -#endif \ No newline at end of file diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h index d873375af..6ef2183a0 100644 --- a/src/main/cpp/concurrent/include/CountdownLatch.h +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -1,10 +1,12 @@ #pragma once +#include + #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" + #include "rocketmq/RocketMQ.h" -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index d029f8367..df1b5ae25 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -22,7 +22,7 @@ ROCKETMQ_NAMESPACE_BEGIN -ClientImpl::ClientImpl(std::string group_name) : ClientConfigImpl(std::move(group_name)), state_(State::CREATED) {} +ClientImpl::ClientImpl(absl::string_view group_name) : ClientConfigImpl(group_name), state_(State::CREATED) {} void ClientImpl::start() { State expected = CREATED; diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index c08eac8e5..d74274f1d 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -1,3 +1,14 @@ +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_join.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + #include "ConsumeMessageService.h" #include "LoggerImpl.h" #include "MessageAccessor.h" @@ -5,20 +16,10 @@ #include "OtlpExporter.h" #include "Protocol.h" #include "PushConsumer.h" -#include "TracingUtility.h" #include "UtilAll.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_join.h" -#include "absl/time/time.h" -#include "absl/types/span.h" -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" #include "rocketmq/ConsumeType.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MessageListener.h" -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index fd6f24454..f46d02ab1 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -1,12 +1,13 @@ #include "rocketmq/DefaultMQPullConsumer.h" +#include + #include "absl/strings/str_split.h" #include "AwaitPullCallback.h" #include "DynamicNameServerResolver.h" #include "PullConsumerImpl.h" #include "StaticNameServerResolver.h" -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index ab072fdce..a5148a379 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -1,8 +1,5 @@ #include #include -#include - -#include "absl/strings/str_split.h" #include "DynamicNameServerResolver.h" #include "PushConsumerImpl.h" @@ -11,24 +8,15 @@ ROCKETMQ_NAMESPACE_BEGIN -static std::set consumerTable{}; -DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) : group_name_(group_name) { - if (consumerTable.count(group_name)) { - SPDLOG_ERROR("create consumer with same group name in a process, group name : {}", group_name); - std::string err_msg = "create consumer with same group name in a process, group name :" + group_name; - THROW_MQ_EXCEPTION(MQClientException, err_msg, -1); - } else { - impl_ = std::make_shared(group_name); - consumerTable.insert(group_name); - } +DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) { + impl_ = std::make_shared(group_name); } void DefaultMQPushConsumer::start() { impl_->start(); } void DefaultMQPushConsumer::shutdown() { impl_->shutdown(); - consumerTable.erase(group_name_); - SPDLOG_DEBUG("DefaultMQPushConsumerImpl shared_ptr use_count : {}", impl_.use_count()); + SPDLOG_DEBUG("PushConsumerImpl shared_ptr use_count={}", impl_.use_count()); } void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::string& expression, @@ -92,4 +80,6 @@ void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr creden void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { impl_->setMessageModel(message_model); } +std::string DefaultMQPushConsumer::groupName() const { return impl_->getGroupName(); } + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index fb0a6d5c7..9f174fd1d 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -1,5 +1,11 @@ #include "ProducerImpl.h" +#include + +#include "absl/strings/str_join.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + #include "MessageAccessor.h" #include "MessageGroupQueueSelector.h" #include "MetadataConstants.h" @@ -13,20 +19,16 @@ #include "TransactionImpl.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" -#include "absl/strings/str_join.h" -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" #include "rocketmq/Transaction.h" -#include ROCKETMQ_NAMESPACE_BEGIN -ProducerImpl::ProducerImpl(std::string group_name) - : ClientImpl(std::move(group_name)), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { +ProducerImpl::ProducerImpl(absl::string_view group_name) + : ClientImpl(group_name), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { // TODO: initialize client_config_ and fault_strategy_ } diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 09fdb35d9..99550b124 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -16,7 +16,7 @@ ROCKETMQ_NAMESPACE_BEGIN -PushConsumerImpl::PushConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} +PushConsumerImpl::PushConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) {} PushConsumerImpl::~PushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index 718be51a4..95d957238 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -1,4 +1,5 @@ #include "absl/synchronization/mutex.h" + #include "rocketmq/AsyncCallback.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index a3046cf4a..40a9756a8 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -3,6 +3,7 @@ #include #include +#include "absl/strings/string_view.h" #include "apache/rocketmq/v1/definition.pb.h" #include "Client.h" @@ -17,9 +18,9 @@ ROCKETMQ_NAMESPACE_BEGIN -class ClientImpl : virtual public Client, virtual public ClientConfigImpl { +class ClientImpl : public ClientConfigImpl, virtual public Client { public: - explicit ClientImpl(std::string group_name); + explicit ClientImpl(absl::string_view group_name); ~ClientImpl() override = default; diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index 860a34248..20751b8bf 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -1,10 +1,11 @@ #pragma once +#include "absl/container/flat_hash_map.h" +#include "absl/types/optional.h" + #include "Client.h" #include "FilterExpression.h" #include "ReceiveMessageAction.h" -#include "absl/container/flat_hash_map.h" -#include "absl/types/optional.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h index c71fe9ed0..3c4e3cca3 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -1,7 +1,9 @@ #pragma once + +#include + #include "rocketmq/MQMessageExt.h" #include "rocketmq/ExpressionType.h" -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index 61ee74d5b..a73b67ead 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -1,10 +1,11 @@ #pragma once +#include + #include "ConsumeMessageType.h" #include "FilterExpression.h" #include "ReceiveMessageCallback.h" #include "rocketmq/MQMessageExt.h" -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index 043fdc4fb..43459c2c0 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -5,6 +5,8 @@ #include #include +#include "absl/strings/string_view.h" + #include "ClientImpl.h" #include "ClientManagerImpl.h" #include "MixAll.h" @@ -23,7 +25,7 @@ ROCKETMQ_NAMESPACE_BEGIN class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_this { public: - explicit ProducerImpl(std::string group_name); + explicit ProducerImpl(absl::string_view group_name); ~ProducerImpl() override; @@ -52,9 +54,11 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t std::unique_ptr prepare(MQMessage& message); - bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); + bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, + const std::string& target); - bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); + bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, + const std::string& target); /** * Check if the RPC client for the target host is isolated or not diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index 2b8cc533c..614534392 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -3,6 +3,8 @@ #include #include +#include "absl/strings/string_view.h" + #include "ClientConfig.h" #include "ClientImpl.h" #include "ClientManagerImpl.h" @@ -14,7 +16,7 @@ ROCKETMQ_NAMESPACE_BEGIN class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this { public: - explicit PullConsumerImpl(std::string group_name) : ClientImpl(std::move(group_name)) {} + explicit PullConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) {} void start() override; diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index 183c0bd06..c71bd1e44 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -1,13 +1,14 @@ #pragma once +#include +#include + #include "ConsumeMessageService.h" #include "Consumer.h" #include "ProcessQueue.h" #include "rocketmq/Executor.h" #include "rocketmq/MessageListener.h" #include "rocketmq/MessageModel.h" -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 64d2f5f73..c03653bfb 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -4,6 +4,8 @@ #include #include +#include "absl/strings/string_view.h" + #include "ClientConfigImpl.h" #include "ClientImpl.h" #include "ClientManagerImpl.h" @@ -30,7 +32,7 @@ class PushConsumerImpl : virtual public ClientImpl, virtual public PushConsumer, public std::enable_shared_from_this { public: - explicit PushConsumerImpl(std::string group_name); + explicit PushConsumerImpl(absl::string_view group_name); ~PushConsumerImpl() override; diff --git a/src/main/cpp/rocketmq/include/ReceiveMessageAction.h b/src/main/cpp/rocketmq/include/ReceiveMessageAction.h index 7805e61a0..6dea6a915 100644 --- a/src/main/cpp/rocketmq/include/ReceiveMessageAction.h +++ b/src/main/cpp/rocketmq/include/ReceiveMessageAction.h @@ -1,9 +1,9 @@ #pragma once -#include "rocketmq/RocketMQ.h" - #include +#include "rocketmq/RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN enum class ReceiveMessageAction : std::int8_t { diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h index 1d62106a1..ed798b427 100644 --- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -13,7 +13,7 @@ ROCKETMQ_NAMESPACE_BEGIN class StaticNameServerResolver : public NameServerResolver { public: - StaticNameServerResolver(absl::string_view name_server_list); + explicit StaticNameServerResolver(absl::string_view name_server_list); void start() override {} diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 7fac1353f..be33cee3f 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -1,10 +1,10 @@ #pragma once -#include "rocketmq/Transaction.h" - #include #include +#include "rocketmq/Transaction.h" + ROCKETMQ_NAMESPACE_BEGIN class ProducerImpl; diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h index f5f65040e..fdbe98f04 100644 --- a/src/main/cpp/scheduler/include/SchedulerImpl.h +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -1,6 +1,4 @@ #pragma once -#include "Scheduler.h" - #include #include #include @@ -13,6 +11,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" #include "asio.hpp" + +#include "Scheduler.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index e8a820449..536f8d213 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -1,8 +1,11 @@ #pragma once -#include "ClientConfig.h" -#include "ClientManager.h" -#include "InvocationContext.h" +#include +#include +#include +#include +#include + #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "absl/synchronization/mutex.h" @@ -10,12 +13,11 @@ #include "opencensus/trace/exporter/span_exporter.h" #include "opencensus/trace/sampler.h" #include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h" + +#include "ClientConfig.h" +#include "ClientManager.h" +#include "InvocationContext.h" #include "rocketmq/RocketMQ.h" -#include -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index cd6703493..c9dddabef 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -13,9 +13,9 @@ #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" #include "gtest/gtest.h" +#include #include #include -#include using namespace testing; @@ -174,7 +174,7 @@ class RpcClientTest : public ::testing::Test { std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; absl::flat_hash_map metadata_; - ClientConfigImpl client_config_; + ClientConfigImpl client_config_{group_}; CredentialsProviderPtr credentials_provider_; std::shared_ptr certificate_provider_; grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index 488a22703..a7674538f 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -1,12 +1,12 @@ #include "TopicPublishInfo.h" +#include "ClientConfigImpl.h" #include "LogInterceptorFactory.h" #include "RpcClientImpl.h" #include "Signature.h" #include "TlsHelper.h" +#include "grpcpp/security/tls_credentials_options.h" #include "rocketmq/MQMessageQueue.h" -#include "ClientConfigImpl.h" #include "gtest/gtest.h" -#include "grpcpp/security/tls_credentials_options.h" ROCKETMQ_NAMESPACE_BEGIN @@ -62,7 +62,7 @@ class TopicPublishInfoTest : public ::testing::Test { std::string region_id_{"cn-hangzhou"}; std::string service_name_{"MQ"}; std::string target_{"dns:grpc.dev:9876"}; - ClientConfigImpl client_config_; + ClientConfigImpl client_config_{group_}; absl::flat_hash_map metadata_; std::shared_ptr completion_queue_; std::shared_ptr client_; @@ -81,7 +81,7 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { request.mutable_topic()->set_name(topic_); auto invocation_context = new InvocationContext(); invocation_context->context.set_deadline(std::chrono::system_clock::now() + - absl::ToChronoMilliseconds(client_config_.getIoTimeout())); + absl::ToChronoMilliseconds(client_config_.getIoTimeout())); for (const auto& item : metadata_) { invocation_context->context.AddMetadata(item.first, item.second); } diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 47fa3d840..b1fce27cf 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -223,4 +223,15 @@ cc_test( "//src/main/cpp/base/mocks:base_mocks", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "default_mq_push_consumer_test", + srcs = [ + "DefaultMQPushConsumerTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp new file mode 100644 index 000000000..845966f3b --- /dev/null +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -0,0 +1,18 @@ +#include "rocketmq/DefaultMQPushConsumer.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class DefaultMQPushConsumerTest : public testing::Test { +public: + DefaultMQPushConsumerTest() : group_name_("group-0"), consumer_(group_name_) {} + +protected: + std::string group_name_; + DefaultMQPushConsumer consumer_; +}; + +TEST_F(DefaultMQPushConsumerTest, testGroupName) { EXPECT_EQ(group_name_, consumer_.groupName()); } + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From cf6c1415d81f53ec22c02a38fc9b0342cdd117c3 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 23 Sep 2021 16:14:52 +0800 Subject: [PATCH 071/133] Fix include order --- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index 900af1f81..f5c455e55 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -1,13 +1,15 @@ #include "ProcessQueueImpl.h" + +#include +#include +#include +#include + #include "ClientManagerImpl.h" #include "MetadataConstants.h" #include "Protocol.h" #include "PushConsumer.h" #include "Signature.h" -#include -#include -#include -#include using namespace std::chrono; From 9540691b5c914de6d1bdbfb72034290ecf3d9dee Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 23 Sep 2021 20:54:35 +0800 Subject: [PATCH 072/133] Unify API --- api/rocketmq/DefaultMQPushConsumer.h | 2 +- src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index f20f9c26b..28a479866 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -86,7 +86,7 @@ class DefaultMQPushConsumer { * remains unique. * @param resource_namespace Abstract resource namespace. */ - void setResourceNamespace(const char* resource_namespace); + void setResourceNamespace(const std::string& resource_namespace); void setCredentialsProvider(CredentialsProviderPtr credentials_provider); diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index a5148a379..744a57865 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -70,7 +70,7 @@ void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t thres impl_->setThrottle(topic, threshold); } -void DefaultMQPushConsumer::setResourceNamespace(const char* resource_namespace) { +void DefaultMQPushConsumer::setResourceNamespace(const std::string& resource_namespace) { impl_->resourceNamespace(resource_namespace); } From c99d515f8eabceee293f69a8565595121c884214 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Sun, 26 Sep 2021 11:28:15 +0800 Subject: [PATCH 073/133] Improve Error Handling (#23) Improve error handling --- .clang-format | 6 + api/BUILD.bazel | 3 + api/rocketmq/AsyncCallback.h | 25 +- api/rocketmq/DefaultMQProducer.h | 33 +- api/rocketmq/ErrorCategory.h | 22 + api/rocketmq/ErrorCode.h | 148 ++- api/rocketmq/MQMessage.h | 71 +- api/rocketmq/MQMessageQueue.h | 50 +- api/rocketmq/SendResult.h | 2 + example/rocketmq/ExampleAsyncProducer.cpp | 9 +- src/main/cpp/base/ErrorCategory.cpp | 89 ++ src/main/cpp/base/ErrorCode.cpp | 10 + src/main/cpp/client/ClientManagerImpl.cpp | 923 ++++++++++++------ src/main/cpp/client/LogInterceptor.cpp | 8 +- src/main/cpp/client/include/Client.h | 2 + src/main/cpp/client/include/ClientManager.h | 25 +- .../cpp/client/include/ClientManagerImpl.h | 32 +- .../client/include/ReceiveMessageCallback.h | 12 +- .../client/mocks/include/ClientManagerMock.h | 19 +- .../cpp/client/mocks/include/ClientMock.h | 2 + .../include/ReceiveMessageCallbackMock.h | 8 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 5 +- src/main/cpp/rocketmq/AwaitPullCallback.cpp | 9 +- src/main/cpp/rocketmq/ClientImpl.cpp | 107 +- .../rocketmq/ConsumeFifoMessageService.cpp | 36 +- .../ConsumeStandardMessageService.cpp | 46 +- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 125 ++- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 126 ++- src/main/cpp/rocketmq/ProducerImpl.cpp | 320 +++--- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 99 +- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 181 ++-- src/main/cpp/rocketmq/SendCallbacks.cpp | 24 +- .../include/AsyncReceiveMessageCallback.h | 3 +- .../cpp/rocketmq/include/AwaitPullCallback.h | 14 +- src/main/cpp/rocketmq/include/ClientImpl.h | 33 +- .../rocketmq/include/ConsumeMessageService.h | 3 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 28 +- src/main/cpp/rocketmq/include/PushConsumer.h | 5 +- .../cpp/rocketmq/include/PushConsumerImpl.h | 8 +- src/main/cpp/rocketmq/include/SendCallbacks.h | 44 +- .../rocketmq/mocks/include/PushConsumerMock.h | 5 +- src/test/cpp/ut/api/BUILD.bazel | 12 + src/test/cpp/ut/api/ErrorCodeTest.cpp | 14 + src/test/cpp/ut/client/ClientManagerTest.cpp | 25 +- .../cpp/ut/rocketmq/AwaitPullCallbackTest.cpp | 9 +- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 20 +- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 83 +- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 104 +- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 23 +- 49 files changed, 1933 insertions(+), 1077 deletions(-) create mode 100644 api/rocketmq/ErrorCategory.h create mode 100644 src/main/cpp/base/ErrorCategory.cpp create mode 100644 src/main/cpp/base/ErrorCode.cpp create mode 100644 src/test/cpp/ut/api/BUILD.bazel create mode 100644 src/test/cpp/ut/api/ErrorCodeTest.cpp diff --git a/.clang-format b/.clang-format index bb3e5d7da..6345fe113 100644 --- a/.clang-format +++ b/.clang-format @@ -7,6 +7,12 @@ PointerAlignment: Left SortIncludes: true IndentWidth: 2 NamespaceIndentation: Inner +AlwaysBreakTemplateDeclarations: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +IndentCaseBlocks: false +IndentCaseLabels: true ... --- diff --git a/api/BUILD.bazel b/api/BUILD.bazel index d971e6d5e..86934279c 100644 --- a/api/BUILD.bazel +++ b/api/BUILD.bazel @@ -5,4 +5,7 @@ cc_library( hdrs = glob(["rocketmq/*.h"]), strip_include_prefix = "//api", visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/strings", + ], ) \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index bb431a1cb..f7d5ae72d 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -1,35 +1,34 @@ #pragma once +#include + #include "MQClientException.h" #include "PullResult.h" #include "SendResult.h" ROCKETMQ_NAMESPACE_BEGIN -struct AsyncCallback {}; - -enum class SendCallbackType : int8_t { noAutoDeleteSendCallback = 0, autoDeleteSendCallback = 1 }; - -using sendCallbackType = SendCallbackType; +class AsyncCallback { +public: + virtual ~AsyncCallback() = default; +}; class SendCallback : public AsyncCallback { public: - virtual ~SendCallback() = default; - - virtual void onSuccess(SendResult& send_result) = 0; + ~SendCallback() override = default; - virtual void onException(const MQException& e) = 0; + virtual void onSuccess(SendResult &send_result) noexcept = 0; - virtual SendCallbackType getSendCallbackType() { return SendCallbackType::noAutoDeleteSendCallback; } + virtual void onFailure(const std::error_code &ec) noexcept = 0; }; class PullCallback : public AsyncCallback { public: - virtual ~PullCallback() = default; + ~PullCallback() override = default; - virtual void onSuccess(const PullResult& pull_result) = 0; + virtual void onSuccess(const PullResult &pull_result) noexcept = 0; - virtual void onException(const MQException& e) = 0; + virtual void onFailure(const std::error_code &ec) noexcept = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 94532d110..4110fd20f 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -2,10 +2,12 @@ #include #include +#include #include #include "AsyncCallback.h" #include "CredentialsProvider.h" +#include "ErrorCode.h" #include "LocalTransactionStateChecker.h" #include "Logger.h" #include "MQMessage.h" @@ -42,18 +44,31 @@ class DefaultMQProducer { */ void setSendMsgTimeout(std::chrono::milliseconds timeout); - SendResult send(const MQMessage& message, const std::string& message_group); + SendResult send(MQMessage& message, const std::string& message_group); /** * Send message in synchronous manner. * @param message Message to send. * @param filter_active_broker Do NOT rely on this parameter. it has been deprecated. */ - SendResult send(const MQMessage& message, bool filter_active_broker = false); - SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); - SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); + SendResult send(const MQMessage& message, bool filter_active_broker = true); - SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, + SendResult send(const MQMessage& message, std::error_code& ec) noexcept; + + SendResult send(MQMessage& message, const MQMessageQueue& message_queue); + SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg); + + /** + * @brief This function is deprecated. + * + * @param message + * @param selector + * @param arg + * @param retry_times retry_times is ignored with respect to member retry_times setting. + * @param select_active_broker + * @return SendResult + */ + SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, bool select_active_broker = false); /** @@ -63,8 +78,8 @@ class DefaultMQProducer { * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. */ void send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker = false); - void send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback); - void send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); + void send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback); + void send(MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); /** * send message in Oneway(The implementation is simply ignore the result of send message in synchronous). @@ -72,8 +87,8 @@ class DefaultMQProducer { * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. */ void sendOneway(const MQMessage& message, bool select_active_broker = false); - void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); - void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + void sendOneway(MQMessage& message, const MQMessageQueue& message_queue); + void sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg); void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); diff --git a/api/rocketmq/ErrorCategory.h b/api/rocketmq/ErrorCategory.h new file mode 100644 index 000000000..316db57e4 --- /dev/null +++ b/api/rocketmq/ErrorCategory.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ErrorCode.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ErrorCategory : public std::error_category { +public: + static const ErrorCategory& instance() { + static ErrorCategory instance; + return instance; + } + + const char* name() const noexcept override { return "RocketMQ"; } + + std::string message(int code) const override; + +private: + ErrorCategory() = default; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index fa6cdf0d7..0e09dd674 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -2,26 +2,152 @@ #include "RocketMQ.h" +#include +#include +#include + ROCKETMQ_NAMESPACE_BEGIN -constexpr int NO_TOPIC_ROUTE_INFO = -1; +enum class ErrorCode : int { + Success = 0, + + /** + * @brief Client state not as expected. Call Producer#start() first. + * + */ + IllegalState = 1, + + /** + * @brief Bad configuration. For example, negative max-attempt-times. + * + */ + BadConfiguration = 300, + + /** + * @brief The server cannot process the request due to apprent client-side + * error. For example, topic contains invalid character or is excessively + * long. + * + */ + BadRequest = 400, + + /** + * @brief Authentication failed. Possibly caused by invalid credentials. + * + */ + Unauthorized = 401, + + /** + * @brief Credentials are understood by server but authenticated user does not + * have privilege to perform the requested action. + * + */ + Forbidden = 403, + + /** + * @brief Topic not found, which should be created through console or + * administration API before hand. + * + */ + NotFound = 404, + + /** + * @brief Timeout when connecting, reading from or writing to brokers. + * + */ + RequestTimeout = 408, + + /** + * @brief Message body is too large. + * + */ + PayloadTooLarge = 413, + + /** + * @brief When trying to perform an action whose dependent procedure state is + * not right, this code will be used. + * 1. Acknowledge a message that is not previously received; + * 2. Commit/Rollback a transactional message that does not exist; + * 3. Commit an offset which is greater than maximum of partition; + */ + PreconditionRequired = 428, + + /** + * @brief Quota exchausted. The user has sent too many requests in a given + * amount of time. + * + */ + TooManyRequest = 429, + + /** + * @brief The server is unwilling to process the request because either an + * individual header field, or all the header fields collectively, are too + * large + * + */ + HeaderFieldsTooLarge = 431, + + /** + * @brief A server operator has received a legal demand to deny access to a + * resource or to a set of resources that includes the requested resource. + * + */ + UnavailableForLegalReasons = 451, -constexpr int FAILED_TO_SELECT_MESSAGE_QUEUE = -2; + /** + * @brief Server side interval error + * + */ + InternalServerError = 500, -constexpr int FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE = -3; + /** + * @brief The server either does not recognize the request method, or it lacks + * the ability to fulfil the request. + * + */ + NotImplemented = 501, -constexpr int FAILED_TO_SEND_MESSAGE = -4; + /** + * @brief The server was acting as a gateway or proxy and received an invalid + * response from the upstream server. + * + */ + BadGateway = 502, -constexpr int FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY = -5; + /** + * @brief The server cannot handle the request (because it is overloaded or + * down for maintenance). Generally, this is a temporary state. + * + */ + ServiceUnavailable = 503, -constexpr int ILLEGAL_STATE = -6; + /** + * @brief The server was acting as a gateway or proxy and did not receive a + * timely response from the upstream server. + * + */ + GatewayTimeout = 504, -constexpr int MESSAGE_ILLEGAL = -7; + /** + * @brief The server does not support the protocol version used in the + * request. + * + */ + ProtocolVersionNotSupported = 505, -constexpr int BAD_CONFIGURATION = -8; + /** + * @brief The server is unable to store the representation needed to complete + * the request. + * + */ + InsufficientStorage = 507, +}; -constexpr int MESSAGE_QUEUE_ILLEGAL = -9; +std::error_code make_error_code(ErrorCode code); -constexpr int ERR_INVALID_MAX_ATTEMPT_TIME = -10; +ROCKETMQ_NAMESPACE_END -ROCKETMQ_NAMESPACE_END \ No newline at end of file +namespace std { +template <> +struct is_error_code_enum : true_type {}; +} // namespace std \ No newline at end of file diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 23f4169cd..30dac5913 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -7,6 +7,9 @@ #include #include +#include "absl/strings/string_view.h" + +#include "MQMessageQueue.h" #include "MessageType.h" ROCKETMQ_NAMESPACE_BEGIN @@ -18,64 +21,82 @@ class MessageAccessor; class MQMessage { public: MQMessage(); - MQMessage(const std::string& topic, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); + MQMessage(const std::string &topic, const std::string &body); + MQMessage(const std::string &topic, const std::string &tags, + const std::string &body); + MQMessage(const std::string &topic, const std::string &tags, + const std::string &keys, const std::string &body); virtual ~MQMessage(); - MQMessage(const MQMessage& other); - MQMessage& operator=(const MQMessage& other); + MQMessage(const MQMessage &other); + MQMessage &operator=(const MQMessage &other); - const std::string& getMsgId() const; + const std::string &getMsgId() const; - void setProperty(const std::string& name, const std::string& value); - std::string getProperty(const std::string& name) const; + void setProperty(const std::string &name, const std::string &value); + std::string getProperty(const std::string &name) const; - const std::string& getTopic() const; - void setTopic(const std::string& topic); - void setTopic(const char* data, int len); + const std::string &getTopic() const; + void setTopic(const std::string &topic); + void setTopic(const char *data, int len); std::string getTags() const; - void setTags(const std::string& tags); + void setTags(const std::string &tags); - const std::vector& getKeys() const; + const std::vector &getKeys() const; /** * @brief Add a unique key for the message - * TODO: a message may be associated with multiple keys. setKey, actually mean attach the given key to the message. - * Better rename it. + * TODO: a message may be associated with multiple keys. setKey, actually mean + * attach the given key to the message. Better rename it. * @param key Unique key in perspective of bussiness logic. */ - void setKey(const std::string& key); - void setKeys(const std::vector& keys); + void setKey(const std::string &key); + void setKeys(const std::vector &keys); int getDelayTimeLevel() const; void setDelayTimeLevel(int level); - const std::string& traceContext() const; - void traceContext(const std::string& trace_context); + const std::string &traceContext() const; + void traceContext(const std::string &trace_context); std::string getBornHost() const; std::chrono::system_clock::time_point deliveryTimestamp() const; - const std::string& getBody() const; - void setBody(const char* data, int len); - void setBody(const std::string& body); + const std::string &getBody() const; + void setBody(const char *data, int len); + void setBody(const std::string &body); uint32_t bodyLength() const; - const std::map& getProperties() const; - void setProperties(const std::map& properties); + const std::map &getProperties() const; + void setProperties(const std::map &properties); void messageType(MessageType message_type); MessageType messageType() const; + void bindMessageGroup(absl::string_view message_group) { + message_group_ = std::string(message_group.data(), message_group.length()); + } + + void bindMessageQueue(const MQMessageQueue &message_queue) { + message_queue_ = message_queue; + } + + const std::string &messageGroup() const { return message_group_; } + + const MQMessageQueue &messageQueue() const { return message_queue_; } + protected: - MessageImpl* impl_; + MessageImpl *impl_; friend class MessageAccessor; + +private: + std::string message_group_; + MQMessageQueue message_queue_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h index d7d907070..4beb3476e 100644 --- a/api/rocketmq/MQMessageQueue.h +++ b/api/rocketmq/MQMessageQueue.h @@ -13,18 +13,19 @@ class MQMessageQueue { MQMessageQueue() = default; MQMessageQueue(std::string topic, std::string broker_name, int queue_id) - : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) {} + : topic_(std::move(topic)), broker_name_(std::move(broker_name)), + queue_id_(queue_id) {} - MQMessageQueue(const MQMessageQueue& other) { this->operator=(other); } + MQMessageQueue(const MQMessageQueue &other) { this->operator=(other); } - MQMessageQueue(MQMessageQueue&& other) noexcept { + MQMessageQueue(MQMessageQueue &&other) noexcept { topic_ = std::move(other.topic_); broker_name_ = std::move(other.broker_name_); queue_id_ = other.queue_id_; service_address_ = other.service_address_; } - MQMessageQueue& operator=(const MQMessageQueue& other) { + MQMessageQueue &operator=(const MQMessageQueue &other) { if (this == &other) { return *this; } @@ -36,27 +37,31 @@ class MQMessageQueue { return *this; } - const std::string& getTopic() const { return topic_; } + const std::string &getTopic() const { return topic_; } - void setTopic(const std::string& topic) { topic_ = topic; } + void setTopic(const std::string &topic) { topic_ = topic; } - const std::string& getBrokerName() const { return broker_name_; } + const std::string &getBrokerName() const { return broker_name_; } - void setBrokerName(const std::string& broker_name) { broker_name_ = broker_name; } + void setBrokerName(const std::string &broker_name) { + broker_name_ = broker_name; + } int getQueueId() const { return queue_id_; } void setQueueId(int queue_id) { queue_id_ = queue_id; } - bool operator!=(const MQMessageQueue& rhs) const { - return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || queue_id_ != rhs.queue_id_; + bool operator!=(const MQMessageQueue &rhs) const { + return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || + queue_id_ != rhs.queue_id_; } - bool operator==(const MQMessageQueue& mq) const { - return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && queue_id_ == mq.queue_id_; + bool operator==(const MQMessageQueue &mq) const { + return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && + queue_id_ == mq.queue_id_; } - bool operator<(const MQMessageQueue& mq) const { + bool operator<(const MQMessageQueue &mq) const { if (topic_ != mq.topic_) { return topic_ < mq.topic_; } @@ -68,7 +73,11 @@ class MQMessageQueue { return queue_id_ < mq.queue_id_; } - int compareTo(const MQMessageQueue& mq) const { + operator bool() const { + return !topic_.empty() && !broker_name_.empty() && queue_id_ >= 0; + } + + int compareTo(const MQMessageQueue &mq) const { if (this == &mq) { return 0; } @@ -84,21 +93,22 @@ class MQMessageQueue { return 0; } - std::string simpleName() const { return topic_ + "_" + broker_name_ + "_" + std::to_string(queue_id_); } + std::string simpleName() const { + return topic_ + "_" + broker_name_ + "_" + std::to_string(queue_id_); + } std::string toString() const { std::stringstream ss; - ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ << ", queueId=" << queue_id_ << "]"; + ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ + << ", queueId=" << queue_id_ << "]"; return ss.str(); } - template friend H AbslHashValue(H h, const MQMessageQueue& mq) { + template friend H AbslHashValue(H h, const MQMessageQueue &mq) { return H::combine(std::move(h), mq.topic_, mq.broker_name_, mq.queue_id_); } - const std::string& serviceAddress() const { - return service_address_; - } + const std::string &serviceAddress() const { return service_address_; } void serviceAddress(std::string service_address) { service_address_ = std::move(service_address); diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 961d229c5..554827c1a 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -82,6 +82,8 @@ class SendResult { trace_context_ = std::move(trace_context); } + operator bool() { return !message_id_.empty(); } + private: SendStatus send_status_{SendStatus::SEND_OK}; std::string message_id_; diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index a40dd02ac..78e23e5f6 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -1,4 +1,5 @@ #include "rocketmq/DefaultMQProducer.h" +#include "rocketmq/ErrorCode.h" #include #include #include @@ -6,6 +7,7 @@ #include #include #include +#include using namespace rocketmq; @@ -41,7 +43,8 @@ std::string randomString(std::string::size_type len) { return result; } -template class RateLimiter { +template +class RateLimiter { public: explicit RateLimiter(int permit) : permits_{0}, interval_(1000 / PARTITION), stopped_(false) { int avg = permit / PARTITION; @@ -137,9 +140,9 @@ class SampleSendCallback : public rocketmq::SendCallback { public: SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) {} - void onSuccess(SendResult& send_result) override { counter_.fetch_add(1, std::memory_order_relaxed); } + void onSuccess(SendResult& send_result) noexcept override { counter_.fetch_add(1, std::memory_order_relaxed); } - void onException(const MQException& e) override { error_.fetch_add(1, std::memory_order_relaxed); } + void onFailure(const std::error_code& ec) noexcept override { error_.fetch_add(1, std::memory_order_relaxed); } private: std::atomic_int& counter_; diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp new file mode 100644 index 000000000..9d4072522 --- /dev/null +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -0,0 +1,89 @@ +#include "rocketmq/ErrorCategory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::string ErrorCategory::message(int code) const { + ErrorCode ec = static_cast(code); + switch (ec) { + case ErrorCode::Success: + return "Success"; + + case ErrorCode::IllegalState: + return "Client state illegal. Forgot to call start()?"; + + case ErrorCode::BadConfiguration: + return "Bad configuration."; + + case ErrorCode::BadRequest: + return "Message is ill-formed. Check validity of your topic, tag, " + "etc"; + + case ErrorCode::Unauthorized: + return "Authentication failed. Possibly caused by invalid credentials."; + + case ErrorCode::Forbidden: + return "Authenticated user does not have privilege to perform the " + "requested action"; + + case ErrorCode::NotFound: + return "Topic not found, which should be created through console or " + "administration API before hand."; + + case ErrorCode::RequestTimeout: + return "Timeout when connecting, reading from or writing to brokers."; + + case ErrorCode::PayloadTooLarge: + return "Message body is too large."; + + case ErrorCode::PreconditionRequired: + return "State of dependent procedure is not right"; + + case ErrorCode::TooManyRequest: + return "Quota exchausted. The user has sent too many requests in a given " + "amount of time."; + + case ErrorCode::UnavailableForLegalReasons: + return "A server operator has received a legal demand to deny access to " + "a resource or to a set of resources that " + "includes the requested resource."; + + case ErrorCode::HeaderFieldsTooLarge: + return "The server is unwilling to process the request because either an " + "individual header field, or all the header fields collectively, " + "are too large"; + + case ErrorCode::InternalServerError: + return "Server side interval error"; + + case ErrorCode::NotImplemented: + return "The server either does not recognize the request method, or it " + "lacks the ability to fulfil the request."; + + case ErrorCode::BadGateway: + return "The server was acting as a gateway or proxy and received an " + "invalid response from the upstream server."; + + case ErrorCode::ServiceUnavailable: + return "The server cannot handle the request (because it is overloaded " + "or down for maintenance). Generally, this " + "is a temporary state."; + + case ErrorCode::GatewayTimeout: + return "The server was acting as a gateway or proxy and did not receive " + "a timely response from the upstream " + "server."; + + case ErrorCode::ProtocolVersionNotSupported: + return "The server does not support the protocol version used in the " + "request."; + + case ErrorCode::InsufficientStorage: + return "The server is unable to store the representation needed to " + "complete the request."; + + default: + return "Not-Implemented"; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/ErrorCode.cpp b/src/main/cpp/base/ErrorCode.cpp new file mode 100644 index 000000000..b6e44dc2d --- /dev/null +++ b/src/main/cpp/base/ErrorCode.cpp @@ -0,0 +1,10 @@ +#include "rocketmq/ErrorCategory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::error_code make_error_code(ErrorCode code) { + const ErrorCategory& instance = ErrorCategory::instance(); + return {static_cast(code), instance}; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index a86ff5e01..679ce3f39 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1,5 +1,14 @@ #include "ClientManagerImpl.h" +#include +#include +#include +#include +#include +#include + +#include "google/rpc/code.pb.h" + #include "InvocationContext.h" #include "LogInterceptor.h" #include "LogInterceptorFactory.h" @@ -16,11 +25,6 @@ #include "grpcpp/create_channel.h" #include "rocketmq/ErrorCode.h" #include "rocketmq/MQMessageExt.h" -#include -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN @@ -155,18 +159,14 @@ void ClientManagerImpl::assignLabels(Histogram& histogram) { void ClientManagerImpl::healthCheck( const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - { - absl::MutexLock lk(&rpc_clients_mtx_); - if (!rpc_clients_.contains(target_host)) { - SPDLOG_WARN("Try to perform health check for {}, which is unknown to client manager", target_host); - cb(target_host, nullptr); - return; - } - } - + const std::function*)>& cb) { + std::error_code ec; auto client = getRpcClient(target_host); - assert(client); + if (!client) { + ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); + return; + } auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; @@ -176,7 +176,41 @@ void ClientManagerImpl::healthCheck( invocation_context->context.AddMetadata(entry.first, entry.second); } - auto callback = [cb](const InvocationContext* ctx) { cb(ctx->remote_address, ctx); }; + auto callback = [cb](const InvocationContext* ctx) { + std::error_code ec; + if (!ctx->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec, ctx); + return; + } + + const auto& common = ctx->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + cb(ec, ctx); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + cb(ec, ctx); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + cb(ec, ctx); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + cb(ec, ctx); + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release"); + ec = ErrorCode::NotImplemented; + cb(ec, ctx); + } break; + } + }; invocation_context->callback = callback; client->asyncHealthCheck(request, invocation_context); @@ -190,7 +224,7 @@ void ClientManagerImpl::doHealthCheck() { return; } - cleanOfflineRpcClients(); + auto&& rpc_clients_removed = cleanOfflineRpcClients(); std::vector> clients; { @@ -203,13 +237,19 @@ void ClientManagerImpl::doHealthCheck() { } } + if (!rpc_clients_removed.empty()) { + for (auto& client : clients) { + client->onRemoteEndpointRemoval(rpc_clients_removed); + } + } + for (auto& client : clients) { client->healthCheck(); } SPDLOG_DEBUG("Health check completed"); } -void ClientManagerImpl::cleanOfflineRpcClients() { +std::vector ClientManagerImpl::cleanOfflineRpcClients() { absl::flat_hash_set hosts; { absl::MutexLock lk(&clients_mtx_); @@ -222,23 +262,27 @@ void ClientManagerImpl::cleanOfflineRpcClients() { } } + std::vector removed; { absl::MutexLock lk(&rpc_clients_mtx_); for (auto it = rpc_clients_.begin(); it != rpc_clients_.end();) { std::string host = it->first; if (it->second->needHeartbeat() && !hosts.contains(host)) { SPDLOG_INFO("Removed RPC client whose peer is offline. RemoteHost={}", host); + removed.push_back(host); rpc_clients_.erase(it++); } else { it++; } } } + + return removed; } void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { + const std::function& cb) { auto client = getRpcClient(target_host, true); if (!client) { return; @@ -251,22 +295,47 @@ void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata } auto callback = [cb](const InvocationContext* invocation_context) { - if (invocation_context->status.ok()) { - if (google::rpc::Code::OK == invocation_context->response.common().status().code()) { - SPDLOG_DEBUG("Send heartbeat to target_host={}, gRPC status OK", invocation_context->remote_address); - cb(true, invocation_context->response); - } else { - SPDLOG_WARN("Server[{}] failed to process heartbeat. Reason: {}", invocation_context->remote_address, - invocation_context->response.common().DebugString()); - cb(false, invocation_context->response); - } - } else { - SPDLOG_WARN("Failed to send heartbeat to target_host={}. GRPC code: {}, message : {}", + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send heartbeat to target_host={}. gRPC code: {}, message: {}", invocation_context->remote_address, invocation_context->status.error_code(), invocation_context->status.error_message()); - cb(false, invocation_context->response); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); + return; + } + + const auto& common = invocation_context->response.common(); + std::error_code ec; + switch (common.status().code()) { + case google::rpc::Code::OK: { + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + ec = ErrorCode::BadRequest; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + cb(ec, invocation_context->response); + } break; + default: { + SPDLOG_WARN("NotImplemented: Please upgrade SDK to latest release"); + } break; } }; + invocation_context->callback = callback; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); client->asyncHeartbeat(request, invocation_context); @@ -296,7 +365,6 @@ void ClientManagerImpl::doHeartbeat() { } void ClientManagerImpl::pollCompletionQueue() { - while (State::STARTED == state_.load(std::memory_order_relaxed) || State::STARTING == state_.load(std::memory_order_relaxed)) { bool ok = false; @@ -328,40 +396,65 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met } const std::string& topic = request.message().topic().name(); - auto completion_callback = [topic, cb, this](const InvocationContext* invocation_context) { - if (invocation_context->status.ok() && - google::rpc::Code::OK == invocation_context->response.common().status().code()) { - SendResult send_result; - send_result.setSendStatus(SendStatus::SEND_OK); - send_result.setMsgId(invocation_context->response.message_id()); - send_result.setTransactionId(invocation_context->response.transaction_id()); - if (State::STARTED == state_.load(std::memory_order_relaxed)) { - cb->onSuccess(send_result); - } else { - SPDLOG_INFO("Client instance has stopped, state={}. Message[MessageId={}] ignored", - state_.load(std::memory_order_relaxed), send_result.getMsgId()); - } - } else { - if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", - invocation_context->remote_address, invocation_context->status.error_code(), - invocation_context->status.error_message()); - } - std::string msg; - msg.append("gRPC code: ") - .append(std::to_string(invocation_context->status.error_code())) - .append(", gRPC message: ") - .append(invocation_context->status.error_message()) - .append(", code: ") - .append(std::to_string(invocation_context->response.common().status().code())) - .append(", remark: ") - .append(invocation_context->response.common().DebugString()); - MQException e(msg, FAILED_TO_SEND_MESSAGE, __FILE__, __LINE__); - if (State::STARTED == state_.load(std::memory_order_relaxed)) { - cb->onException(e); - } else { - SPDLOG_WARN("Client instance has stopped, state={}. Ignore exception raised while sending message: {}", - state_.load(std::memory_order_relaxed), e.what()); + std::weak_ptr client_manager(shared_from_this()); + auto completion_callback = [topic, cb, + client_manager](const InvocationContext* invocation_context) { + ClientManagerPtr client_manager_ptr = client_manager.lock(); + if (!client_manager_ptr) { + return; + } + + if (State::STARTED != client_manager_ptr->state()) { + // TODO: Would this leak some memroy? + return; + } + + const auto& common = invocation_context->response.common(); + + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + cb->onFailure(ec); + return; + } + + if (invocation_context->status.ok()) { + switch (invocation_context->response.common().status().code()) { + case google::rpc::Code::OK: { + SendResult send_result; + send_result.setSendStatus(SendStatus::SEND_OK); + send_result.setMsgId(invocation_context->response.message_id()); + send_result.setTransactionId(invocation_context->response.transaction_id()); + cb->onSuccess(send_result); + } break; + + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + std::error_code ec = ErrorCode::BadRequest; + cb->onFailure(ec); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + std::error_code ec = ErrorCode::Unauthorized; + cb->onFailure(ec); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + std::error_code ec = ErrorCode::Forbidden; + cb->onFailure(ec); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + std::error_code ec = ErrorCode::InternalServerError; + cb->onFailure(ec); + } break; + default: { + SPDLOG_WARN("Unsupported status code. Check and upgrade SDK to the latest"); + std::error_code ec = ErrorCode::NotImplemented; + cb->onFailure(ec); + } break; } } }; @@ -447,12 +540,13 @@ void ClientManagerImpl::addClientObserver(std::weak_ptr client) { void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { + const std::function& cb) { RpcClientSharedPtr client = getRpcClient(target_host, false); if (!client) { SPDLOG_WARN("Failed to create RPC client for name server[host={}]", target_host); - cb(false, nullptr); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); return; } @@ -467,94 +561,144 @@ void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metad if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to send query route request to server[host={}]. Reason: {}", invocation_context->remote_address, invocation_context->status.error_message()); - cb(false, nullptr); - return; - } - - if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query route request. Reason: {}", - invocation_context->remote_address, invocation_context->response.common().DebugString()); - cb(false, nullptr); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, nullptr); return; } - auto& partitions = invocation_context->response.partitions(); - - std::vector topic_partitions; - for (const auto& partition : partitions) { - Topic t(partition.topic().resource_namespace(), partition.topic().name()); - - auto& broker = partition.broker(); - AddressScheme scheme = AddressScheme::IPv4; - switch (broker.endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - scheme = AddressScheme::IPv4; - break; - case rmq::AddressScheme::IPv6: - scheme = AddressScheme::IPv6; - break; - case rmq::AddressScheme::DOMAIN_NAME: - scheme = AddressScheme::DOMAIN_NAME; - break; - default: - break; - } - - std::vector
addresses; - for (const auto& address : broker.endpoints().addresses()) { - addresses.emplace_back(Address{address.host(), address.port()}); - } - ServiceAddress service_address(scheme, addresses); - Broker b(partition.broker().name(), partition.broker().id(), service_address); - - Permission permission = Permission::READ_WRITE; - switch (partition.permission()) { - case rmq::Permission::READ: - permission = Permission::READ; - break; - - case rmq::Permission::WRITE: - permission = Permission::WRITE; - break; - case rmq::Permission::READ_WRITE: - permission = Permission::READ_WRITE; - break; - default: - break; - } - Partition topic_partition(t, partition.id(), permission, std::move(b)); - topic_partitions.emplace_back(std::move(topic_partition)); + std::error_code ec; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + auto& partitions = invocation_context->response.partitions(); + std::vector topic_partitions; + for (const auto& partition : partitions) { + Topic t(partition.topic().resource_namespace(), partition.topic().name()); + + auto& broker = partition.broker(); + AddressScheme scheme = AddressScheme::IPv4; + switch (broker.endpoints().scheme()) { + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + break; + } + + std::vector
addresses; + for (const auto& address : broker.endpoints().addresses()) { + addresses.emplace_back(Address{address.host(), address.port()}); + } + ServiceAddress service_address(scheme, addresses); + Broker b(partition.broker().name(), partition.broker().id(), service_address); + + Permission permission = Permission::READ_WRITE; + switch (partition.permission()) { + case rmq::Permission::READ: + permission = Permission::READ; + break; + + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + break; + } + Partition topic_partition(t, partition.id(), permission, std::move(b)); + topic_partitions.emplace_back(std::move(topic_partition)); + } + auto ptr = + std::make_shared(std::move(topic_partitions), invocation_context->response.DebugString()); + cb(ec, ptr); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + cb(ec, nullptr); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + cb(ec, nullptr); + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + ec = ErrorCode::BadRequest; + cb(ec, nullptr); + } break; + case google::rpc::Code::NOT_FOUND: { + SPDLOG_WARN("NotFound: {}", common.status().message()); + ec = ErrorCode::NotFound; + cb(ec, nullptr); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + cb(ec, nullptr); + } break; + default: { + SPDLOG_WARN("NotImplement: Please upgrade to latest SDK release"); + ec = ErrorCode::NotImplemented; + cb(ec, nullptr); + } break; } - - auto ptr = - std::make_shared(std::move(topic_partitions), invocation_context->response.DebugString()); - cb(true, ptr); }; invocation_context->callback = callback; client->asyncQueryRoute(request, invocation_context); } -void ClientManagerImpl::queryAssignment(const std::string& target, const Metadata& metadata, - const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::queryAssignment( + const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { SPDLOG_DEBUG("Prepare to send query assignment request to broker[address={}]", target); std::shared_ptr client = getRpcClient(target); auto callback = [&, cb](const InvocationContext* invocation_context) { if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to query assignment. Reason: {}", invocation_context->status.error_message()); - cb(false, invocation_context->response); + std::error_code ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); return; } - if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query assignment request. Reason: {}", - invocation_context->remote_address, invocation_context->response.common().DebugString()); - cb(false, invocation_context->response); - return; + const auto& common = invocation_context->response.common(); + std::error_code ec; + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Query assignment OK"); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release"); + ec = ErrorCode::NotImplemented; + } break; } - - cb(true, invocation_context->response); + cb(ec, invocation_context->response); }; auto invocation_context = new InvocationContext(); @@ -585,17 +729,57 @@ void ClientManagerImpl::receiveMessage(const std::string& target_host, const Met auto callback = [this, cb](const InvocationContext* invocation_context) { if (invocation_context->status.ok()) { SPDLOG_DEBUG("Received pop response through gRPC from brokerAddress={}", invocation_context->remote_address); - ReceiveMessageResult receive_result; - this->processPopResult(invocation_context->context, invocation_context->response, receive_result, - invocation_context->remote_address); - cb->onSuccess(receive_result); + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + ReceiveMessageResult receive_result; + this->processPopResult(invocation_context->context, invocation_context->response, receive_result, + invocation_context->remote_address); + cb->onSuccess(receive_result); + } break; + + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + std::error_code ec = ErrorCode::Unauthorized; + cb->onFailure(ec); + } break; + + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + std::error_code ec = ErrorCode::Forbidden; + cb->onFailure(ec); + } break; + + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + std::error_code ec = ErrorCode::BadRequest; + cb->onFailure(ec); + } break; + + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}", common.status().message()); + std::error_code ec = ErrorCode::GatewayTimeout; + cb->onFailure(ec); + } break; + + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("IntervalServerError: {}", common.status().message()); + std::error_code ec = ErrorCode::InternalServerError; + cb->onFailure(ec); + } break; + default: { + SPDLOG_WARN("Unsupported code. Please upgrade to use the latest release"); + std::error_code ec = ErrorCode::NotImplemented; + cb->onFailure(ec); + } break; + } + } else { SPDLOG_WARN("Failed to pop messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", invocation_context->remote_address, invocation_context->status.error_code(), invocation_context->status.error_message()); - MQException e(invocation_context->status.error_message(), FAILED_TO_POP_MESSAGE_ASYNCHRONOUSLY, __FILE__, - __LINE__); - cb->onException(e); + std::error_code ec = ErrorCode::RequestTimeout; + cb->onFailure(ec); } }; invocation_context->callback = callback; @@ -608,25 +792,31 @@ void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_conte // process response to result ReceiveMessageStatus status; switch (response.common().status().code()) { - case google::rpc::Code::OK: - status = ReceiveMessageStatus::OK; - break; - case google::rpc::Code::RESOURCE_EXHAUSTED: - status = ReceiveMessageStatus::RESOURCE_EXHAUSTED; - SPDLOG_WARN("Too many pop requests in broker. Long polling is full in the broker side. BrokerAddress={}", - target_host); - break; - case google::rpc::Code::DEADLINE_EXCEEDED: - status = ReceiveMessageStatus::DEADLINE_EXCEEDED; - break; - case google::rpc::Code::NOT_FOUND: - status = ReceiveMessageStatus::NOT_FOUND; - break; - default: - SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", target_host, - response.common().DebugString()); - status = ReceiveMessageStatus::INTERNAL; - break; + case google::rpc::Code::OK: { + status = ReceiveMessageStatus::OK; + break; + } + case google::rpc::Code::RESOURCE_EXHAUSTED: { + status = ReceiveMessageStatus::RESOURCE_EXHAUSTED; + SPDLOG_WARN("Too many pop requests in broker. Long polling is full in the broker side. BrokerAddress={}", + target_host); + break; + } + + case google::rpc::Code::DEADLINE_EXCEEDED: { + status = ReceiveMessageStatus::DEADLINE_EXCEEDED; + break; + } + case google::rpc::Code::NOT_FOUND: { + status = ReceiveMessageStatus::NOT_FOUND; + break; + } + default: { + SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", target_host, + response.common().DebugString()); + status = ReceiveMessageStatus::INTERNAL; + break; + } } result.sourceHost(target_host); @@ -667,34 +857,42 @@ void ClientManagerImpl::processPullResult(const grpc::ClientContext& client_cont } result.sourceHost(target_host); switch (response.common().status().code()) { - case google::rpc::Code::OK: { - assert(!response.messages().empty()); - result.status_ = ReceiveMessageStatus::OK; - for (const auto& item : response.messages()) { - MQMessageExt message; - if (!wrapMessage(item, message)) { - result.status_ = ReceiveMessageStatus::DATA_CORRUPTED; - return; + case google::rpc::Code::OK: { + assert(!response.messages().empty()); + result.status_ = ReceiveMessageStatus::OK; + for (const auto& item : response.messages()) { + MQMessageExt message; + if (!wrapMessage(item, message)) { + result.status_ = ReceiveMessageStatus::DATA_CORRUPTED; + return; + } + result.messages_.emplace_back(message); } - result.messages_.emplace_back(message); + break; } - } break; - case google::rpc::Code::DEADLINE_EXCEEDED: - result.status_ = ReceiveMessageStatus::DEADLINE_EXCEEDED; - break; + case google::rpc::Code::DEADLINE_EXCEEDED: { + result.status_ = ReceiveMessageStatus::DEADLINE_EXCEEDED; + break; + } - case google::rpc::Code::RESOURCE_EXHAUSTED: - result.status_ = ReceiveMessageStatus::RESOURCE_EXHAUSTED; - break; + case google::rpc::Code::RESOURCE_EXHAUSTED: { + result.status_ = ReceiveMessageStatus::RESOURCE_EXHAUSTED; + break; + } - case google::rpc::Code::OUT_OF_RANGE: - result.status_ = ReceiveMessageStatus::OUT_OF_RANGE; - result.next_offset_ = response.next_offset(); - break; + case google::rpc::Code::OUT_OF_RANGE: { + result.status_ = ReceiveMessageStatus::OUT_OF_RANGE; + result.next_offset_ = response.next_offset(); + break; + } } } +State ClientManagerImpl::state() const { + return state_.load(std::memory_order_relaxed); +} + bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { assert(item.topic().resource_namespace() == resource_namespace_); @@ -728,57 +926,57 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess body_digest_match = true; } else { switch (digest.type()) { - case rmq::DigestType::CRC32: { - std::string checksum; - bool success = MixAll::crc32(item.body(), checksum); - if (success) { - body_digest_match = (digest.checksum() == checksum); - if (body_digest_match) { - SPDLOG_DEBUG("Message body CRC32 checksum validation passed."); + case rmq::DigestType::CRC32: { + std::string checksum; + bool success = MixAll::crc32(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("Message body CRC32 checksum validation passed."); + } else { + SPDLOG_WARN("Body CRC32 checksum validation failed. Actual: {}, expect: {}", checksum, digest.checksum()); + } } else { - SPDLOG_WARN("Body CRC32 checksum validation failed. Actual: {}, expect: {}", checksum, digest.checksum()); + SPDLOG_WARN("Failed to calculate CRC32 checksum. Skip."); } - } else { - SPDLOG_WARN("Failed to calculate CRC32 checksum. Skip."); + break; } - break; - } - case rmq::DigestType::MD5: { - std::string checksum; - bool success = MixAll::md5(item.body(), checksum); - if (success) { - body_digest_match = (digest.checksum() == checksum); - if (body_digest_match) { - SPDLOG_DEBUG("MD5 checksum validation passed."); + case rmq::DigestType::MD5: { + std::string checksum; + bool success = MixAll::md5(item.body(), checksum); + if (success) { + body_digest_match = (digest.checksum() == checksum); + if (body_digest_match) { + SPDLOG_DEBUG("MD5 checksum validation passed."); + } else { + SPDLOG_WARN("Body MD5 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + } } else { - SPDLOG_WARN("Body MD5 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); + body_digest_match = true; } - } else { - SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); - body_digest_match = true; + break; } - break; - } - case rmq::DigestType::SHA1: { - std::string checksum; - bool success = MixAll::sha1(item.body(), checksum); - if (success) { - body_digest_match = (checksum == digest.checksum()); - if (body_digest_match) { - SPDLOG_DEBUG("SHA1 checksum validation passed"); + case rmq::DigestType::SHA1: { + std::string checksum; + bool success = MixAll::sha1(item.body(), checksum); + if (success) { + body_digest_match = (checksum == digest.checksum()); + if (body_digest_match) { + SPDLOG_DEBUG("SHA1 checksum validation passed"); + } else { + SPDLOG_WARN("Body SHA1 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + } } else { - SPDLOG_WARN("Body SHA1 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + SPDLOG_WARN("Failed to calculate SHA1 digest. Skip."); } - } else { - SPDLOG_WARN("Failed to calculate SHA1 digest. Skip."); + break; + } + default: { + SPDLOG_WARN("Unsupported message body digest algorithm"); + body_digest_match = true; + break; } - break; - } - default: { - SPDLOG_WARN("Unsupported message body digest algorithm"); - body_digest_match = true; - break; - } } } @@ -790,20 +988,20 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess // Body encoding switch (system_attributes.body_encoding()) { - case rmq::Encoding::GZIP: { - std::string uncompressed; - UtilAll::uncompress(item.body(), uncompressed); - message_ext.setBody(uncompressed); - break; - } - case rmq::Encoding::IDENTITY: { - message_ext.setBody(item.body()); - break; - } - default: { - SPDLOG_WARN("Unsupported encoding algorithm"); - break; - } + case rmq::Encoding::GZIP: { + std::string uncompressed; + UtilAll::uncompress(item.body(), uncompressed); + message_ext.setBody(uncompressed); + break; + } + case rmq::Encoding::IDENTITY: { + message_ext.setBody(item.body()); + break; + } + default: { + SPDLOG_WARN("Unsupported encoding algorithm"); + break; + } } timeval tv{}; @@ -811,22 +1009,22 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess // Message-type MessageType message_type; switch (system_attributes.message_type()) { - case rmq::MessageType::NORMAL: - message_type = MessageType::NORMAL; - break; - case rmq::MessageType::FIFO: - message_type = MessageType::FIFO; - break; - case rmq::MessageType::DELAY: - message_type = MessageType::DELAY; - break; - case rmq::MessageType::TRANSACTION: - message_type = MessageType::TRANSACTION; - break; - default: - SPDLOG_WARN("Unknown message type. Treat it as normal message"); - message_type = MessageType::NORMAL; - break; + case rmq::MessageType::NORMAL: + message_type = MessageType::NORMAL; + break; + case rmq::MessageType::FIFO: + message_type = MessageType::FIFO; + break; + case rmq::MessageType::DELAY: + message_type = MessageType::DELAY; + break; + case rmq::MessageType::TRANSACTION: + message_type = MessageType::TRANSACTION; + break; + default: + SPDLOG_WARN("Unknown message type. Treat it as normal message"); + message_type = MessageType::NORMAL; + break; } MessageAccessor::setMessageType(message_ext, message_type); @@ -853,20 +1051,20 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess // Process one-of: delivery-timestamp and delay-level. switch (system_attributes.timed_delivery_case()) { - case rmq::SystemAttribute::TimedDeliveryCase::kDelayLevel: { - message_ext.setDelayTimeLevel(system_attributes.delay_level()); - break; - } + case rmq::SystemAttribute::TimedDeliveryCase::kDelayLevel: { + message_ext.setDelayTimeLevel(system_attributes.delay_level()); + break; + } - case rmq::SystemAttribute::TimedDeliveryCase::kDeliveryTimestamp: { - tv.tv_sec = system_attributes.delivery_timestamp().seconds(); - tv.tv_usec = system_attributes.delivery_timestamp().nanos(); - MessageAccessor::setDeliveryTimestamp(message_ext, absl::TimeFromTimeval(tv)); - break; - } + case rmq::SystemAttribute::TimedDeliveryCase::kDeliveryTimestamp: { + tv.tv_sec = system_attributes.delivery_timestamp().seconds(); + tv.tv_usec = system_attributes.delivery_timestamp().nanos(); + MessageAccessor::setDeliveryTimestamp(message_ext, absl::TimeFromTimeval(tv)); + break; + } - default: - break; + default: + break; } // Partition-id @@ -908,10 +1106,12 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess return true; } -Scheduler& ClientManagerImpl::getScheduler() { return scheduler_; } +Scheduler& ClientManagerImpl::getScheduler() { + return scheduler_; +} void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, const AckMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) { + std::chrono::milliseconds timeout, const std::function& cb) { std::string target_host(target.data(), target.length()); SPDLOG_DEBUG("Prepare to ack message against {} asynchronously. AckMessageRequest: {}", target_host, request.DebugString()); @@ -927,12 +1127,40 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, // TODO: Use capture by move and pass-by-value paradigm when C++ 14 is available. auto callback = [request, cb](const InvocationContext* invocation_context) { - if (invocation_context->status.ok() && - google::rpc::Code::OK == invocation_context->response.common().status().code()) { - cb(true); - } else { - cb(false); + std::error_code ec; + if (!invocation_context->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec); + return; + } + + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Ack OK. host={}", invocation_context->remote_address); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplement: please upgrade SDK to latest release. host={}", invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; } + cb(ec); }; invocation_context->callback = callback; client->asyncAck(request, invocation_context); @@ -940,7 +1168,7 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, void ClientManagerImpl::nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& completion_callback) { + const std::function& completion_callback) { RpcClientSharedPtr client = getRpcClient(target_host); assert(client); auto invocation_context = new InvocationContext(); @@ -952,25 +1180,58 @@ void ClientManagerImpl::nack(const std::string& target_host, const Metadata& met } auto callback = [completion_callback](const InvocationContext* invocation_context) { - if (invocation_context->status.ok() && - google::rpc::Code::OK == invocation_context->response.common().status().code()) { - completion_callback(true); - } else { - completion_callback(false); + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to write Nack request to wire. gRPC-code: {}, gRPC-message: {}", + invocation_context->status.error_code(), invocation_context->status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + completion_callback(ec); + return; + } + + std::error_code ec; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Nack to {} OK", invocation_context->remote_address); + break; + }; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + break; + } + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + break; + } + default: { + SPDLOG_WARN("NotImplemented: Please upgrade to latest SDK, host={}", invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + break; + } } + completion_callback(ec); }; invocation_context->callback = callback; client->asyncNack(request, invocation_context); } -void ClientManagerImpl::endTransaction(const std::string& target_host, const Metadata& metadata, - const EndTransactionRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { +void ClientManagerImpl::endTransaction( + const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { RpcClientSharedPtr client = getRpcClient(target_host); if (!client) { SPDLOG_WARN("No RPC client for {}", target_host); EndTransactionResponse response; - cb(false, response); + std::error_code ec = ErrorCode::BadRequest; + cb(ec, response); return; } @@ -987,17 +1248,43 @@ void ClientManagerImpl::endTransaction(const std::string& target_host, const Met invocation_context->context.set_deadline(deadline); auto callback = [target_host, cb](const InvocationContext* invocation_context) { - if (!invocation_context->status.ok() || - google::rpc::Code::OK != invocation_context->response.common().status().code()) { - SPDLOG_WARN("Failed to endTransaction. TargetHost={}, gRPC statusCode={}, errorMessage={}", target_host.data(), - invocation_context->status.error_message(), invocation_context->status.error_message()); - cb(false, invocation_context->response); + std::error_code ec; + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to write EndTransaction to wire. gRPC-code: {}, gRPC-message: {}, host={}", + invocation_context->status.error_code(), invocation_context->status.error_message(), + invocation_context->remote_address); + ec = ErrorCode::BadRequest; + cb(ec, invocation_context->response); return; } - SPDLOG_DEBUG("endTransaction completed OK. Response: {}", invocation_context->response.DebugString()); - cb(true, invocation_context->response); + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("endTransaction completed OK. Response: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. {}, host={}", common.status().message(), + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } + } + cb(ec, invocation_context->response); }; + invocation_context->callback = callback; client->asyncEndTransaction(request, invocation_context); } @@ -1042,10 +1329,14 @@ void ClientManagerImpl::multiplexingCall( void ClientManagerImpl::queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { + const std::function& cb) { auto client = getRpcClient(target_host); + std::error_code ec; if (!client) { SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); + ec = ErrorCode::RequestTimeout; + QueryOffsetResponse response; + cb(ec, response); return; } @@ -1053,21 +1344,45 @@ void ClientManagerImpl::queryOffset(const std::string& target_host, const Metada invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); auto callback = [cb](const InvocationContext* invocation_context) { + std::error_code ec; + if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to send query offset request to {}. Reason: {}", invocation_context->remote_address, - invocation_context->status.error_message()); - cb(false, invocation_context->response); + SPDLOG_WARN("Failed to write QueryOffset request to wire. gRPC-code: {}, gRPC-message: {}, host={}", + invocation_context->status.error_code(), invocation_context->status.error_message(), + invocation_context->remote_address); + ec = ErrorCode::RequestTimeout; + cb(ec, invocation_context->response); return; } - if (google::rpc::Code::OK != invocation_context->response.common().status().code()) { - SPDLOG_WARN("Server[host={}] failed to process query offset request. Reason: {}", - invocation_context->remote_address, invocation_context->response.common().DebugString()); - cb(false, invocation_context->response); + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + cb(ec, invocation_context->response); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + cb(ec, invocation_context->response); + } break; + default: { + SPDLOG_WARN("NotImplemented: please upgrade SDK to the latest release. host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + cb(ec, invocation_context->response); + } } - - SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); - cb(true, invocation_context->response); }; invocation_context->callback = callback; client->asyncQueryOffset(request, invocation_context); @@ -1141,6 +1456,8 @@ bool ClientManagerImpl::notifyClientTermination(const std::string& target_host, NotifyClientTerminationResponse response; grpc::Status status = client->notifyClientTermination(&context, request, &response); if (!status.ok()) { + SPDLOG_WARN("Failed to write NotifyClientTermination request to wire. gRPC-code: {}, gRPC-message: {}, host={}", + status.error_code(), status.error_message(), target_host); return false; } diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 3497dddb6..869fc9508 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -34,8 +34,12 @@ void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* meth response_headers.insert({absl::string_view(it.first.data(), it.first.length()), absl::string_view(it.second.data(), it.second.length())}); } - SPDLOG_DEBUG("[Inbound]Response Headers of {}:\n{}", client_rpc_info_->method(), - absl::StrJoin(response_headers, "\n", absl::PairFormatter(" --> "))); + if (!response_headers.empty()) { + SPDLOG_DEBUG("[Inbound]Response Headers of {}:\n{}", client_rpc_info_->method(), + absl::StrJoin(response_headers, "\n", absl::PairFormatter(" --> "))); + } else { + SPDLOG_DEBUG("[Inbound]Response metadata of {} is empty", client_rpc_info_->method()); + } } } diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index d029598f2..9a14e8cbc 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -18,6 +18,8 @@ class Client : virtual public ClientConfig { virtual bool active() = 0; + virtual void onRemoteEndpointRemoval(const std::vector&) = 0; + /** * For endpoints that are marked as inactive due to one or multiple business operation failure, this function is to * initiate health-check RPCs; Once the health-check passes, they are conceptually add back to serve further business diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index b98a6d3cc..28d0ef16f 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -2,6 +2,7 @@ #include #include +#include #include "Client.h" #include "ReceiveMessageCallback.h" @@ -10,6 +11,7 @@ #include "TopAddressing.h" #include "TopicRouteData.h" #include "rocketmq/MQMessageExt.h" +#include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN @@ -29,11 +31,11 @@ class ClientManager { virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) = 0; + const std::function& cb) = 0; virtual void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) = 0; + const std::function& cb) = 0; virtual void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, std::chrono::milliseconds timeout, @@ -42,10 +44,10 @@ class ClientManager { virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) = 0; + std::chrono::milliseconds timeout, const std::function& cb) = 0; virtual void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& callback) = 0; + std::chrono::milliseconds timeout, const std::function& callback) = 0; virtual void forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, @@ -54,22 +56,23 @@ class ClientManager { virtual void endTransaction(const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) = 0; + const std::function& cb) = 0; virtual void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) = 0; + const std::function& cb) = 0; virtual void healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; + const std::function*)>& cb) = 0; virtual void addClientObserver(std::weak_ptr client) = 0; - virtual void queryAssignment(const std::string& target, const Metadata& metadata, - const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) = 0; + virtual void + queryAssignment(const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) = 0; virtual void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, const std::shared_ptr& cb) = 0; @@ -87,6 +90,8 @@ class ClientManager { virtual bool notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) = 0; + + virtual State state() const = 0; }; using ClientManagerPtr = std::shared_ptr; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index ba56c479a..2cb179b1d 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "absl/base/thread_annotations.h" @@ -65,7 +66,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share */ void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) override + const std::function& cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); void doHealthCheck() LOCKS_EXCLUDED(clients_mtx_); @@ -74,16 +75,15 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share * If inactive RPC clients refer to remote hosts that are absent from topic_route_table_, we need to purge them * immediately. */ - void cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_); + std::vector cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_); /** * Execute health-check on behalf of the client. */ - void - healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) override - LOCKS_EXCLUDED(rpc_clients_mtx_); + void healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) + override LOCKS_EXCLUDED(rpc_clients_mtx_); bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, SendCallback* cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); @@ -114,7 +114,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void queryAssignment(const std::string& target, const Metadata& metadata, const QueryAssignmentRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) override; + const std::function& cb) override; void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, const std::shared_ptr& cb) override @@ -137,10 +137,10 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share * @param request Ack message request. */ void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) override; + std::chrono::milliseconds timeout, const std::function& cb) override; void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& callback) override; + std::chrono::milliseconds timeout, const std::function& callback) override; void forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, @@ -163,7 +163,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share */ void endTransaction(const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) override; + const std::function& cb) override; void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, std::chrono::milliseconds timeout, @@ -171,7 +171,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) override; + const std::function& cb) override; void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, @@ -181,15 +181,19 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) override; - void trace(bool trace) { trace_ = trace; } + void trace(bool trace) { + trace_ = trace; + } void heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) override; + const std::function& cb) override; void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, ReceiveMessageResult& result, const std::string& target_host) override; + State state() const override; + private: void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, ReceiveMessageResult& result, const std::string& target_host); diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h index 738b8a717..e4088a6f8 100644 --- a/src/main/cpp/client/include/ReceiveMessageCallback.h +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -2,12 +2,18 @@ #include "ReceiveMessageResult.h" #include "rocketmq/AsyncCallback.h" +#include "rocketmq/ErrorCode.h" +#include ROCKETMQ_NAMESPACE_BEGIN + class ReceiveMessageCallback : public AsyncCallback { public: - virtual ~ReceiveMessageCallback() = default; - virtual void onSuccess(ReceiveMessageResult& result) = 0; - virtual void onException(MQException& e) = 0; + ~ReceiveMessageCallback() override = default; + + virtual void onSuccess(ReceiveMessageResult &result) = 0; + + virtual void onFailure(const std::error_code &ec) = 0; }; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 7abce6e73..f5e5bc2d6 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -3,6 +3,7 @@ #include "ClientManager.h" #include "gmock/gmock.h" #include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -18,12 +19,12 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, resolveRoute, (const std::string&, const Metadata&, const QueryRouteRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, heartbeat, (const std::string&, const Metadata&, const HeartbeatRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, multiplexingCall, @@ -35,12 +36,12 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, ack, (const std::string&, const Metadata&, const AckMessageRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, nack, (const std::string&, const Metadata&, const NackMessageRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, forwardMessageToDeadLetterQueue, @@ -51,24 +52,24 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, endTransaction, (const std::string&, const Metadata&, const EndTransactionRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, queryOffset, (const std::string&, const Metadata&, const QueryOffsetRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, healthCheck, (const std::string&, const Metadata&, const HealthCheckRequest&, std::chrono::milliseconds, - (const std::function*)>&)), + (const std::function*)>&)), (override)); MOCK_METHOD(void, addClientObserver, (std::weak_ptr), (override)); MOCK_METHOD(void, queryAssignment, (const std::string& target, const Metadata&, const QueryAssignmentRequest&, std::chrono::milliseconds, - (const std::function&)), + (const std::function&)), (override)); MOCK_METHOD(void, receiveMessage, @@ -90,6 +91,8 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(bool, notifyClientTermination, (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), (override)); + + MOCK_METHOD(State, state, (), (const override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h index 7ad69b0ae..dfd7a6615 100644 --- a/src/main/cpp/client/mocks/include/ClientMock.h +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -13,6 +13,8 @@ class ClientMock : virtual public Client, virtual public ClientConfigMock { MOCK_METHOD(bool, active, (), (override)); + MOCK_METHOD(void, onRemoteEndpointRemoval, (const std::vector&), (override)); + MOCK_METHOD(void, healthCheck, (), (override)); MOCK_METHOD(void, schedule, (const std::string&, const std::function&, std::chrono::milliseconds), diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h index cb7016cd6..7f4090382 100644 --- a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -1,14 +1,16 @@ #pragma once -#include "ReceiveMessageCallback.h" + #include "gmock/gmock.h" +#include "ReceiveMessageCallback.h" + ROCKETMQ_NAMESPACE_BEGIN class ReceiveMessageCallbackMock : public ReceiveMessageCallback { public: - MOCK_METHOD(void, onSuccess, (ReceiveMessageResult&), (override)); + MOCK_METHOD(void, onSuccess, (ReceiveMessageResult &), (override)); - MOCK_METHOD(void, onException, (MQException&), (override)); + MOCK_METHOD(void, onFailure, (const std::error_code &), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index 654b4e665..9bd1d5233 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -3,6 +3,7 @@ #include "ConsumeMessageType.h" #include "LoggerImpl.h" #include "PushConsumer.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -98,10 +99,10 @@ void AsyncReceiveMessageCallback::checkThrottleThenReceive() { } } -void AsyncReceiveMessageCallback::onException(MQException& e) { +void AsyncReceiveMessageCallback::onFailure(const std::error_code& ec) { auto process_queue_ptr = process_queue_.lock(); if (process_queue_ptr) { - SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", e.what(), process_queue_ptr->simpleName()); + SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", ec.message(), process_queue_ptr->simpleName()); // pop message later receiveMessageLater(); } diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/rocketmq/AwaitPullCallback.cpp index 99ca912c4..6c942a89c 100644 --- a/src/main/cpp/rocketmq/AwaitPullCallback.cpp +++ b/src/main/cpp/rocketmq/AwaitPullCallback.cpp @@ -2,19 +2,18 @@ ROCKETMQ_NAMESPACE_BEGIN -void AwaitPullCallback::onSuccess(const PullResult& pull_result) { +void AwaitPullCallback::onSuccess(const PullResult& pull_result) noexcept { absl::MutexLock lk(&mtx_); - has_failure_ = false; completed_ = true; // TODO: optimize out messages copy here. pull_result_ = pull_result; cv_.SignalAll(); } -void AwaitPullCallback::onException(const MQException& e) { +void AwaitPullCallback::onFailure(const std::error_code& ec) noexcept { absl::MutexLock lk(&mtx_); - has_failure_ = true; completed_ = true; + ec_ = ec; cv_.SignalAll(); } @@ -24,7 +23,7 @@ bool AwaitPullCallback::await() { while (!completed_) { cv_.Wait(&mtx_); } - return !has_failure_; + return !hasFailure(); } } diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index df1b5ae25..60a404987 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -2,9 +2,11 @@ #include #include #include +#include #include #include #include +#include #include #include "absl/strings/str_join.h" @@ -85,7 +87,8 @@ void ClientImpl::endpointsInUse(absl::flat_hash_set& endpoints) { } } -void ClientImpl::getRouteFor(const std::string& topic, const std::function& cb) { +void ClientImpl::getRouteFor(const std::string& topic, + const std::function& cb) { TopicRouteDataPtr route = nullptr; { absl::MutexLock lock(&topic_route_table_mtx_); @@ -95,7 +98,8 @@ void ClientImpl::getRouteFor(const std::string& topic, const std::function> inflight{cb}; + std::vector> inflight{cb}; inflight_route_requests_.insert({topic, inflight}); SPDLOG_INFO("Create inflight route query cache for topic={}", topic); } @@ -124,9 +128,11 @@ void ClientImpl::getRouteFor(const std::string& topic, const std::function& cb) { +void ClientImpl::fetchRouteFor(const std::string& topic, + const std::function& cb) { std::string name_server = name_server_resolver_->current(); if (name_server.empty()) { SPDLOG_WARN("No name server available"); return; } - auto callback = [this, topic, name_server, cb](bool ok, const TopicRouteDataPtr& route) { - if (!ok || !route) { + auto callback = [this, topic, name_server, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); std::string name_server_changed = name_server_resolver_->next(); if (!name_server_changed.empty()) { SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); } - cb(nullptr); + cb(ec, nullptr); return; } SPDLOG_DEBUG("Apply callback of fetchRouteFor({}) since a valid route is fetched", topic); - cb(route); + cb(ec, route); }; QueryRouteRequest request; @@ -212,14 +219,10 @@ void ClientImpl::updateRouteInfo() { if (!topics.empty()) { for (const auto& topic : topics) { - fetchRouteFor(topic, std::bind(&ClientImpl::updateRouteCache, this, topic, std::placeholders::_1)); + fetchRouteFor( + topic, std::bind(&ClientImpl::updateRouteCache, this, topic, std::placeholders::_1, std::placeholders::_2)); } } - -#ifdef ENABLE_TRACING - updateTraceProvider(); -#endif - SPDLOG_DEBUG("Topic route info updated"); } @@ -238,9 +241,9 @@ void ClientImpl::heartbeat() { Signature::sign(this, metadata); for (const auto& target : hosts) { - auto callback = [target](bool ok, const HeartbeatResponse& response) { - if (!ok) { - SPDLOG_WARN("Failed to send heartbeat request to {}", target); + auto callback = [target](const std::error_code& ec, const HeartbeatResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to heartbeat against {}. Cause: {}", target, ec.message()); return; } SPDLOG_DEBUG("Heartbeat to {} OK", target); @@ -249,15 +252,16 @@ void ClientImpl::heartbeat() { } } -void ClientImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) { +void ClientImpl::onTopicRouteReady(const std::string& topic, const std::error_code& ec, + const TopicRouteDataPtr& route) { if (route) { SPDLOG_DEBUG("Received route data for topic={}", topic); } - updateRouteCache(topic, route); + updateRouteCache(topic, ec, route); // Take all pending callbacks - std::vector> pending_requests; + std::vector> pending_requests; { absl::MutexLock lk(&inflight_route_requests_mtx_); assert(inflight_route_requests_.contains(topic)); @@ -268,13 +272,13 @@ void ClientImpl::onTopicRouteReady(const std::string& topic, const TopicRouteDat SPDLOG_DEBUG("Apply cached callbacks with acquired route data for topic={}", topic); for (const auto& cb : pending_requests) { - cb(route); + cb(ec, route); } } -void ClientImpl::updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) { - if (!route || route->partitions().empty()) { - SPDLOG_WARN("Yuck! route for {} is invalid", topic); +void ClientImpl::updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec || !route || route->partitions().empty()) { + SPDLOG_WARN("Yuck! route for {} is invalid. Cause: {}", topic, ec.message()); return; } @@ -385,6 +389,18 @@ void ClientImpl::onMultiplexingResponse(const InvocationContext& hosts) { + absl::MutexLock lk(&isolated_endpoints_mtx_); + for (auto it = isolated_endpoints_.begin(); it != isolated_endpoints_.end();) { + if (hosts.end() != std::find_if(hosts.begin(), hosts.end(), [&](const std::string& item) { return *it == item; })) { + SPDLOG_INFO("Drop isolated-endoint[{}] as it has been removed from route table", *it); + isolated_endpoints_.erase(it++); + } else { + it++; + } + } +} + void ClientImpl::healthCheck() { std::vector endpoints; { @@ -395,14 +411,14 @@ void ClientImpl::healthCheck() { } std::weak_ptr base(self()); - auto callback = [base](const std::string& endpoint, - const InvocationContext* invocation_context) { + auto callback = [base](const std::error_code& ec, const InvocationContext* invocation_context) { std::shared_ptr ptr = base.lock(); - if (ptr) { - ptr->onHealthCheckResponse(endpoint, invocation_context); - } else { + if (!ptr) { SPDLOG_INFO("BaseImpl has been destructed"); + return; } + + ptr->onHealthCheckResponse(ec, invocation_context); }; for (const auto& endpoint : endpoints) { @@ -418,31 +434,16 @@ void ClientImpl::schedule(const std::string& task_name, const std::functiongetScheduler().schedule(task, task_name, delay, std::chrono::milliseconds(0)); } -void ClientImpl::onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) { - if (!ctx) { - SPDLOG_WARN("ClientInstance does not have RPC client for {}. It might have been offline and thus cleaned", - endpoint); - { - absl::MutexLock lk(&isolated_endpoints_mtx_); - isolated_endpoints_.erase(endpoint); - } +void ClientImpl::onHealthCheckResponse(const std::error_code& ec, const InvocationContext* ctx) { + if (ec) { + SPDLOG_WARN("Health check to server[host={}] failed. Cause: {}", ec.message()); return; } - assert(endpoint == ctx->remote_address); - - if (ctx->status.ok()) { - if (google::rpc::Code::OK == ctx->response.common().status().code()) { - SPDLOG_INFO("Health check to server[host={}] passed. Move it back to active node pool", endpoint); - absl::MutexLock lk(&isolated_endpoints_mtx_); - isolated_endpoints_.erase(endpoint); - } else { - SPDLOG_INFO("Health check to server[host={}] failed due to application layer reason: {}", - ctx->response.common().DebugString()); - } - } else { - SPDLOG_INFO("Health check to server[host={}] failed due to transport layer reason: {}", endpoint, - ctx->status.error_message()); + SPDLOG_INFO("Health check to server[host={}] passed. Remove it from isolated endpoint pool", ctx->remote_address); + { + absl::MutexLock lk(&isolated_endpoints_mtx_); + isolated_endpoints_.erase(ctx->remote_address); } } diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 1049055a8..4a9ef0637 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -1,16 +1,19 @@ +#include +#include +#include +#include + #include "ConsumeMessageService.h" #include "MessageAccessor.h" #include "ProcessQueue.h" #include "PushConsumerImpl.h" -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN ConsumeFifoMessageService ::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener) - : ConsumeMessageService(std::move(consumer), thread_count, message_listener) {} + : ConsumeMessageService(std::move(consumer), thread_count, message_listener) { +} void ConsumeFifoMessageService::start() { ConsumeMessageService::start(); @@ -78,7 +81,9 @@ void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& pro } } -MessageListenerType ConsumeFifoMessageService::messageListenerType() { return MessageListenerType::FIFO; } +MessageListenerType ConsumeFifoMessageService::messageListenerType() { + return MessageListenerType::FIFO; +} void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message) { ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); @@ -145,31 +150,32 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q } } -void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok) { +void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, + const std::error_code& ec) { auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { SPDLOG_WARN("ProcessQueue has destructed."); return; } - if (ok) { - SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), - message.getMsgId()); - process_queue_ptr->unbindFifoConsumeTask(); - signalDispatcher(); - } else { - SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]", process_queue_ptr->simpleName(), - message.getMsgId()); + + if (ec) { + SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]. Cause: {}", + process_queue_ptr->simpleName(), message.getMsgId(), ec.message()); auto consumer = consumer_.lock(); if (!consumer) { SPDLOG_WARN("Consumer instance has destructed"); return; } - auto task = std::bind(&ConsumeFifoMessageService::scheduleAckTask, this, process_queue, message); int32_t duration = 100; consumer->schedule("Ack-FIFO-Message-On-Failure", task, std::chrono::milliseconds(duration)); SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message.getTopic(), message.getMsgId(), duration); + } else { + SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), + message.getMsgId()); + process_queue_ptr->unbindFifoConsumeTask(); + signalDispatcher(); } } diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index d74274f1d..e73674ea3 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "absl/memory/memory.h" @@ -25,7 +26,8 @@ ROCKETMQ_NAMESPACE_BEGIN ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener_ptr) - : ConsumeMessageService(std::move(consumer), thread_count, message_listener_ptr) {} + : ConsumeMessageService(std::move(consumer), thread_count, message_listener_ptr) { +} void ConsumeStandardMessageService::start() { ConsumeMessageService::start(); @@ -88,7 +90,9 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& } } -MessageListenerType ConsumeStandardMessageService::messageListenerType() { return MessageListenerType::STANDARD; } +MessageListenerType ConsumeStandardMessageService::messageListenerType() { + return MessageListenerType::STANDARD; +} void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs) { @@ -197,12 +201,12 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce { for (auto& span : spans) { switch (status) { - case ConsumeMessageResult::SUCCESS: - span.SetStatus(opencensus::trace::StatusCode::OK); - break; - case ConsumeMessageResult::FAILURE: - span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); - break; + case ConsumeMessageResult::SUCCESS: + span.SetStatus(opencensus::trace::StatusCode::OK); + break; + case ConsumeMessageResult::FAILURE: + span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); + break; } span.End(); } @@ -219,26 +223,26 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce process_queue_ptr->release(msg.getBody().size(), msg.getQueueOffset()); if (status == ConsumeMessageResult::SUCCESS) { - auto callback = [process_queue_ptr, message_id](bool ok) { - if (ok) { + auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]. Cause: {}", + process_queue_ptr->simpleName(), message_id, ec.message()); + } else { SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); - } else { - SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]", process_queue_ptr->simpleName(), - message_id); } }; consumer->ack(msg, callback); } else { - auto callback = [process_queue_ptr, message_id](bool ok) { - if (ok) { - SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); - } else { - SPDLOG_INFO( - "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Message will be re-consumed " - "after default invisible time", - process_queue_ptr->simpleName(), message_id); + auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN("Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Cause: {} Message will be " + "re-consumed after default invisible time", + process_queue_ptr->simpleName(), message_id, ec.message()); + return; } + + SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); }; consumer->nack(msg, callback); } diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index f72e64fec..2780dca4e 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "absl/strings/str_split.h" @@ -24,8 +26,14 @@ std::chrono::milliseconds DefaultMQProducer::getSendMsgTimeout() const { return absl::ToChronoMilliseconds(impl_->getIoTimeout()); } -SendResult DefaultMQProducer::send(const MQMessage& message, const std::string& message_group) { - return impl_->send(message, message_group); +SendResult DefaultMQProducer::send(MQMessage& message, const std::string& message_group) { + message.bindMessageGroup(message_group); + std::error_code ec; + auto&& send_result = impl_->send(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); } void DefaultMQProducer::setSendMsgTimeout(std::chrono::milliseconds timeout) { @@ -50,43 +58,109 @@ void DefaultMQProducer::enableTracing(bool enabled) { impl_->enableTracing(enabl bool DefaultMQProducer::isTracingEnabled() { return impl_->isTracingEnabled(); } -SendResult DefaultMQProducer::send(const rocketmq::MQMessage& message, bool filter_active_broker) { - return impl_->send(message); +SendResult DefaultMQProducer::send(const MQMessage& message, bool filter_active_broker) { + std::error_code ec; + auto&& send_result = impl_->send(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); } -SendResult DefaultMQProducer::send(const MQMessage& msg, const MQMessageQueue& mq) { return impl_->send(msg, mq); } +SendResult DefaultMQProducer::send(const MQMessage& message, std::error_code& ec) noexcept { + return impl_->send(message, ec); +} + +SendResult DefaultMQProducer::send(MQMessage& msg, const MQMessageQueue& mq) { + msg.bindMessageQueue(mq); + std::error_code ec; + auto&& send_result = impl_->send(msg, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); +} + +SendResult DefaultMQProducer::send(MQMessage& msg, MessageQueueSelector* selector, void* arg) { + std::error_code ec; + auto&& list = impl_->listMessageQueue(msg.getTopic(), ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } -SendResult DefaultMQProducer::send(const MQMessage& msg, MessageQueueSelector* selector, void* arg) { - return impl_->send(msg, selector, arg); + auto&& message_queue = selector->select(list, msg, arg); + msg.bindMessageQueue(message_queue); + + auto&& send_result = impl_->send(msg, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(send_result); } -SendResult DefaultMQProducer::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, +SendResult DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, bool select_active_broker) { - return impl_->send(message, selector, arg, retry_times); + return send(message, selector, arg); } void DefaultMQProducer::send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker) { impl_->send(message, send_callback); } -void DefaultMQProducer::send(const MQMessage& message, const MQMessageQueue& message_queue, - SendCallback* send_callback) { - impl_->send(message, message_queue, send_callback); +void DefaultMQProducer::send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback) { + message.bindMessageQueue(message_queue); + impl_->send(message, send_callback); } -void DefaultMQProducer::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, +void DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback) { - impl_->send(message, selector, arg, send_callback); + std::error_code ec; + + // TODO: make querying route async + auto&& list = impl_->listMessageQueue(message.getTopic(), ec); + if (ec) { + send_callback->onFailure(ec); + } + + if (list.empty()) { + send_callback->onFailure(ErrorCode::ServiceUnavailable); + } + + auto&& message_queue = selector->select(list, message, arg); + message.bindMessageQueue(message_queue); + + impl_->send(message, send_callback); } -void DefaultMQProducer::sendOneway(const MQMessage& message, bool select_active_broker) { impl_->sendOneway(message); } +void DefaultMQProducer::sendOneway(const MQMessage& message, bool select_active_broker) { + std::error_code ec; + impl_->sendOneway(message, ec); +} -void DefaultMQProducer::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { - impl_->sendOneway(message, message_queue); +void DefaultMQProducer::sendOneway(MQMessage& message, const MQMessageQueue& message_queue) { + message.bindMessageQueue(message_queue); + std::error_code ec; + impl_->sendOneway(message, ec); + if (ec) { + SPDLOG_INFO("Failed to send message in one-way: {}", ec.message()); + } } -void DefaultMQProducer::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { - impl_->sendOneway(message, selector, arg); +void DefaultMQProducer::sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg) { + std::error_code ec; + auto&& list = impl_->listMessageQueue(message.getTopic(), ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + + if (list.empty()) { + ec = ErrorCode::ServiceUnavailable; + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + + auto&& message_queue = selector->select(list, message, arg); + message.bindMessageQueue(message_queue); + impl_->sendOneway(message, ec); } void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { @@ -98,7 +172,12 @@ void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { impl_->maxAt int DefaultMQProducer::getMaxAttemptTimes() const { return impl_->maxAttemptTimes(); } std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { - return impl_->getTopicMessageQueueInfo(topic); + std::error_code ec; + auto&& list = impl_->listMessageQueue(topic, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } + return std::move(list); } void DefaultMQProducer::setUnitName(std::string unit_name) { impl_->setUnitName(std::move(unit_name)); } @@ -120,7 +199,11 @@ void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credential void DefaultMQProducer::setRegion(const std::string& region) { impl_->region(region); } TransactionPtr DefaultMQProducer::prepare(MQMessage& message) { - auto transaction = impl_->prepare(message); + std::error_code ec; + auto transaction = impl_->prepare(message, ec); + if (ec) { + THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); + } return transaction; } diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index f5c455e55..da7c97d7e 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "ClientManagerImpl.h" @@ -77,12 +78,12 @@ void ProcessQueueImpl::receiveMessage() { auto policy = consumer->receiveMessageAction(); switch (policy) { - case ReceiveMessageAction::POLLING: - popMessage(); - break; - case ReceiveMessageAction::PULL: - pullMessage(); - break; + case ReceiveMessageAction::POLLING: + popMessage(); + break; + case ReceiveMessageAction::PULL: + pullMessage(); + break; } } @@ -115,29 +116,61 @@ void ProcessQueueImpl::pullMessage() { auto timeout = consumer->getLongPollingTimeout(); auto callback = [this](const InvocationContext* invocation_context) { - if (!invocation_context) { - MQException e("Transport layer failure", -1, __FILE__, __LINE__); - receive_callback_->onException(e); - return; - } - - if (invocation_context->status.ok()) { - if (google::rpc::Code::OK == invocation_context->response.common().status().code()) { - ReceiveMessageResult result; - client_manager_->processPullResult(invocation_context->context, invocation_context->response, result, - invocation_context->remote_address); - receive_callback_->onSuccess(result); - } else { - auto status = invocation_context->response.common().status(); - MQException e(status.message(), status.code(), __FILE__, __LINE__); - receive_callback_->onException(e); + auto status = invocation_context->status; + if (status.ok()) { + const auto& common = invocation_context->response.common(); + + switch (common.status().code()) { + case google::rpc::Code::OK: { + ReceiveMessageResult result; + client_manager_->processPullResult(invocation_context->context, invocation_context->response, result, + invocation_context->remote_address); + receive_callback_->onSuccess(result); + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + std::error_code ec = ErrorCode::Forbidden; + receive_callback_->onFailure(ec); + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + std::error_code ec = ErrorCode::Unauthorized; + receive_callback_->onFailure(ec); + } break; + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}", common.status().message()); + std::error_code ec = ErrorCode::GatewayTimeout; + receive_callback_->onFailure(ec); + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + std::error_code ec = ErrorCode::BadRequest; + receive_callback_->onFailure(ec); + } break; + case google::rpc::Code::FAILED_PRECONDITION: { + SPDLOG_WARN("FailedPrecondition: {}", common.status().message()); + std::error_code ec = ErrorCode::PreconditionRequired; + receive_callback_->onFailure(ec); + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}", common.status().message()); + std::error_code ec = ErrorCode::InternalServerError; + receive_callback_->onFailure(ec); + } break; + default: { + SPDLOG_WARN("Unimplemented: Please upgrade to use latest SDK release"); + std::error_code ec = ErrorCode::NotImplemented; + receive_callback_->onFailure(ec); + } break; } } else { - MQException e(invocation_context->status.error_message(), invocation_context->status.error_code(), __FILE__, - __LINE__); - receive_callback_->onException(e); + SPDLOG_WARN("Failed to receive valid gRPC response from server. gRPC-status[code={}, message={}]", + status.error_code(), status.error_message()); + std::error_code ec = ErrorCode::RequestTimeout; + receive_callback_->onFailure(ec); } }; + client_manager_->pullMessage(message_queue_.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(timeout), callback); } @@ -161,13 +194,14 @@ void ProcessQueueImpl::cacheMessages(const std::vector& messages) const std::string& msg_id = message.getMsgId(); if (!filter_expression_.accept(message)) { const std::string& topic = message.getTopic(); - auto callback = [topic, msg_id](bool ok) { - if (ok) { + auto callback = [topic, msg_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN( + "Failed to ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression. Cause: {}", + topic, msg_id, ec.message()); + } else { SPDLOG_DEBUG("Ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression", topic, msg_id); - } else { - SPDLOG_WARN("Failed to ack message[Topic={}, MsgId={}] directly as it fails to pass filter expression", - topic, msg_id); } }; consumer->ack(message, callback); @@ -248,14 +282,14 @@ void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expres if (optional.has_value()) { auto expression = optional.value(); switch (expression.type_) { - case TAG: - filter_expression->set_type(rmq::FilterType::TAG); - filter_expression->set_expression(expression.content_); - break; - case SQL92: - filter_expression->set_type(rmq::FilterType::SQL); - filter_expression->set_expression(expression.content_); - break; + case TAG: + filter_expression->set_type(rmq::FilterType::TAG); + filter_expression->set_expression(expression.content_); + break; + case SQL92: + filter_expression->set_type(rmq::FilterType::SQL); + filter_expression->set_expression(expression.content_); + break; } } else { filter_expression->set_type(rmq::FilterType::TAG); @@ -306,12 +340,20 @@ void ProcessQueueImpl::wrapPullMessageRequest(absl::flat_hash_map ProcessQueueImpl::getConsumer() { return consumer_; } +std::weak_ptr ProcessQueueImpl::getConsumer() { + return consumer_; +} -std::shared_ptr ProcessQueueImpl::getClientManager() { return client_manager_; } +std::shared_ptr ProcessQueueImpl::getClientManager() { + return client_manager_; +} -MQMessageQueue ProcessQueueImpl::getMQMessageQueue() { return message_queue_; } +MQMessageQueue ProcessQueueImpl::getMQMessageQueue() { + return message_queue_; +} -const FilterExpression& ProcessQueueImpl::getFilterExpression() const { return filter_expression_; } +const FilterExpression& ProcessQueueImpl::getFilterExpression() const { + return filter_expression_; +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 9f174fd1d..85f534433 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -1,6 +1,9 @@ #include "ProducerImpl.h" #include +#include +#include +#include #include "absl/strings/str_join.h" #include "opencensus/trace/propagation/trace_context.h" @@ -20,7 +23,6 @@ #include "UniqueIdGenerator.h" #include "UtilAll.h" #include "rocketmq/ErrorCode.h" -#include "rocketmq/MQClientException.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" #include "rocketmq/Transaction.h" @@ -55,9 +57,9 @@ void ProducerImpl::shutdown() { bool ProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } -void ProducerImpl::ensureRunning() const { +void ProducerImpl::ensureRunning(std::error_code& ec) const noexcept { if (!isRunning()) { - THROW_MQ_EXCEPTION(MQClientException, "Invoke #start() first", ILLEGAL_STATE); + ec = ErrorCode::IllegalState; } } @@ -118,159 +120,111 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM return message_id; } -SendResult ProducerImpl::send(const MQMessage& message) { - ensureRunning(); +SendResult ProducerImpl::send(const MQMessage& message, std::error_code& ec) noexcept { + ensureRunning(ec); + if (ec) { + return {}; + } + auto topic_publish_info = getPublishInfo(message.getTopic()); if (!topic_publish_info) { - THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + ec = ErrorCode::NotFound; + return {}; } std::vector message_queue_list; - takeMessageQueuesRoundRobin(topic_publish_info, message_queue_list, max_attempt_times_); - if (message_queue_list.empty()) { - THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); - } - - AwaitSendCallback callback; - send0(message, &callback, message_queue_list, max_attempt_times_); - callback.await(); - if (callback) { - return callback.sendResult(); - } - THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); -} + if (!message.messageGroup().empty() || message.messageQueue()) { + auto&& list = listMessageQueue(message.getTopic(), ec); + if (ec) { + return {}; + } -SendResult ProducerImpl::send(const MQMessage& message, const std::string& message_group) { - MessageGroupQueueSelector selector(message_group); - return send(message, &selector, nullptr); -} + if (list.empty()) { + ec = ErrorCode::ServiceUnavailable; + return {}; + } -SendResult ProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue) { - ensureRunning(); - std::vector message_queue_list{withServiceAddress(message_queue)}; - AwaitSendCallback callback; - send0(message, &callback, message_queue_list, max_attempt_times_); - callback.await(); - if (callback) { - return callback.sendResult(); + if (!message.messageGroup().empty()) { + std::size_t hash_code = std::hash{}(message.messageGroup()); + hash_code = hash_code & std::numeric_limits::max(); + std::size_t r = hash_code % list.size(); + message_queue_list.push_back(list[r]); + } else { + for (const auto& entry : list) { + if (entry == message.messageQueue()) { + message_queue_list.push_back(entry); + break; + } + } + } + } else { + takeMessageQueuesRoundRobin(topic_publish_info, message_queue_list, max_attempt_times_); } - THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); -} -SendResult ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg) { - ensureRunning(); - std::vector message_queue_list; - executeMessageQueueSelector(message, selector, arg, message_queue_list); if (message_queue_list.empty()) { - THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); + ec = ErrorCode::ServiceUnavailable; + return {}; } + AwaitSendCallback callback; send0(message, &callback, message_queue_list, max_attempt_times_); callback.await(); - if (callback) { - return callback.sendResult(); - } - THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); -} -SendResult ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int max_attempts) { - ensureRunning(); - std::vector message_queue_list; - executeMessageQueueSelector(message, selector, arg, message_queue_list); - if (message_queue_list.empty()) { - THROW_MQ_EXCEPTION(MQClientException, "No topic route available", NO_TOPIC_ROUTE_INFO); - } - AwaitSendCallback callback; - send0(message, &callback, message_queue_list, max_attempts); - callback.await(); if (callback) { return callback.sendResult(); } - THROW_MQ_EXCEPTION(MQClientException, callback.errorMessage(), FAILED_TO_SEND_MESSAGE); + + ec = callback.errorCode(); + return {}; } void ProducerImpl::send(const MQMessage& message, SendCallback* cb) { - ensureRunning(); - auto callback = [this, message, cb](const TopicPublishInfoPtr& publish_info) { - if (!publish_info) { - MQClientException e("Failed to acquire topic route data", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); - cb->onException(e); + std::error_code ec; + ensureRunning(ec); + if (ec) { + cb->onFailure(ec); + } + + auto callback = [this, message, cb](const std::error_code& ec, const TopicPublishInfoPtr& publish_info) { + if (ec) { + cb->onFailure(ec); return; } std::vector message_queue_list; - takeMessageQueuesRoundRobin(publish_info, message_queue_list, max_attempt_times_); - send0(message, cb, message_queue_list, max_attempt_times_); - }; - - asyncPublishInfo(message.getTopic(), callback); -} - -void ProducerImpl::send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* callback) { - ensureRunning(); - std::vector message_queue_list{withServiceAddress(message_queue)}; - send0(message, callback, message_queue_list, max_attempt_times_); -} - -void ProducerImpl::send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* callback) { - ensureRunning(); - - auto cb = [this, message, selector, callback, arg](const TopicPublishInfoPtr& ptr) { - if (!ptr) { - MQClientException e("Failed to acquire topic route", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); - callback->onException(e); - return; + if (!message.messageGroup().empty() || message.messageQueue()) { + auto&& list = publish_info->getMessageQueueList(); + if (!message.messageGroup().empty()) { + std::size_t hash_code = std::hash{}(message.messageGroup()); + hash_code = hash_code & std::numeric_limits::max(); + std::size_t r = hash_code % list.size(); + message_queue_list.push_back(list[r]); + } else { + for (const auto& entry : list) { + if (entry == message.messageQueue()) { + message_queue_list.push_back(entry); + break; + } + } + } + } else { + takeMessageQueuesRoundRobin(publish_info, message_queue_list, max_attempt_times_); } - MQMessageQueue queue = selector->select(ptr->getMessageQueueList(), message, arg); - std::vector message_queue_list{queue}; - - send0(message, callback, message_queue_list, max_attempt_times_); - }; - - asyncPublishInfo(message.getTopic(), cb); -} - -void ProducerImpl::sendOneway(const MQMessage& message) { - ensureRunning(); - auto callback = [this, message](const TopicPublishInfoPtr& ptr) { - if (!ptr) { - SPDLOG_WARN("Failed acquire topic publish info for {}", message.getTopic()); + if (message_queue_list.empty()) { + cb->onFailure(ErrorCode::ServiceUnavailable); return; } - MQMessageQueue message_queue; - absl::flat_hash_set isolated; - isolatedEndpoints(isolated); - ptr->selectOneActiveMessageQueue(isolated, message_queue); - std::vector list{message_queue}; - send0(message, onewaySendCallback(), list, 1); - }; - asyncPublishInfo(message.getTopic(), callback); -} - -void ProducerImpl::sendOneway(const MQMessage& message, const MQMessageQueue& message_queue) { - ensureRunning(); - std::vector list{withServiceAddress(message_queue)}; - send0(message, onewaySendCallback(), list, 1); -} - -void ProducerImpl::sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg) { - ensureRunning(); - auto callback = [this, message, selector, arg](const TopicPublishInfoPtr& ptr) { - if (!ptr) { - SPDLOG_WARN("No topic route for {}", message.getTopic()); - return; - } - MQMessageQueue queue = selector->select(ptr->getMessageQueueList(), message, arg); - std::vector list{queue}; - send0(message, onewaySendCallback(), list, 1); + send0(message, cb, message_queue_list, max_attempt_times_); }; asyncPublishInfo(message.getTopic(), callback); } +void ProducerImpl::sendOneway(const MQMessage& message, std::error_code& ec) { send(message, ec); } + void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { transaction_state_checker_ = std::move(checker); } @@ -279,9 +233,8 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { const std::string& target = callback->messageQueue().serviceAddress(); if (target.empty()) { SPDLOG_WARN("Failed to resolve broker address from MessageQueue"); - MQClientException e("Failed to resolve broker address", FAILED_TO_RESOLVE_BROKER_ADDRESS_FROM_TOPIC_ROUTE, __FILE__, - __LINE__); - callback->onException(e); + std::error_code ec = ErrorCode::BadGateway; + callback->onFailure(ec); return; } @@ -333,20 +286,20 @@ void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std:: assert(callback); if (!validate(message)) { - MQClientException e("Message is illegal", MESSAGE_ILLEGAL, __FILE__, __LINE__); - callback->onException(e); + std::error_code ec = ErrorCode::BadRequest; + callback->onFailure(ec); return; } if (list.empty()) { - MQClientException e("Topic route not found", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); - callback->onException(e); + std::error_code ec = ErrorCode::NotFound; + callback->onFailure(ec); return; } if (max_attempt_times <= 0) { - MQClientException e("Retry times illegal", ERR_INVALID_MAX_ATTEMPT_TIME, __FILE__, __LINE__); - callback->onException(e); + std::error_code ec = ErrorCode::BadConfiguration; + callback->onFailure(ec); return; } MQMessageQueue message_queue = list[0]; @@ -403,26 +356,16 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& absl::Mutex mtx; absl::CondVar cv; - auto cb = [&, span](bool rpc_ok, const EndTransactionResponse& response) { + auto cb = [&, span](const std::error_code& ec, const EndTransactionResponse& response) { completed = true; - if (!rpc_ok) { + if (ec) { { span.SetStatus(opencensus::trace::StatusCode::ABORTED); - span.AddAnnotation("gRPC tier failure"); - span.End(); - } - - SPDLOG_WARN("Failed to send {} transaction request to {}", action, target); - success = false; - } else if (response.common().status().code() != google::rpc::Code::OK) { - { - span.SetStatus(opencensus::trace::ABORTED); - span.AddAnnotation(response.common().status().DebugString()); + span.AddAnnotation(ec.message()); span.End(); } + SPDLOG_WARN("Failed to send {} transaction request to {}. Cause: ", action, target, ec.message()); success = false; - SPDLOG_WARN("Server[host={}] failed to {} transaction. Reason: {}", target, action, - response.common().DebugString()); } else { { span.SetStatus(opencensus::trace::StatusCode::OK); @@ -450,17 +393,21 @@ void ProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints endpoints.insert(isolated_endpoints_.begin(), isolated_endpoints_.end()); } -MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_queue) { +MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec) { if (!message_queue.serviceAddress().empty()) { return message_queue; } if (message_queue.getTopic().empty() || message_queue.getBrokerName().empty() || message_queue.getQueueId() < 0) { - MQClientException e("Message queue is illegal", MESSAGE_QUEUE_ILLEGAL, __FILE__, __LINE__); - throw e; + ec = ErrorCode::BadRequest; + return {}; + } + + std::vector list = listMessageQueue(message_queue.getTopic(), ec); + if (ec) { + return {}; } - std::vector list = getTopicMessageQueueInfo(message_queue.getTopic()); for (const auto& item : list) { if (item == message_queue) { return item; @@ -468,8 +415,8 @@ MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_qu } if (list.empty()) { - MQClientException e("No topic route available", NO_TOPIC_ROUTE_INFO, __FILE__, __LINE__); - throw e; + ec = ErrorCode::NotFound; + return {}; } else { return *list.begin(); } @@ -485,17 +432,16 @@ void ProducerImpl::isolateEndpoint(const std::string& target) { isolated_endpoints_.insert(target); } -std::unique_ptr ProducerImpl::prepare(MQMessage& message) { - try { - message.messageType(MessageType::TRANSACTION); - SendResult send_result = send(message); - return std::unique_ptr(new TransactionImpl( - send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), - send_result.traceContext(), ProducerImpl::shared_from_this())); - } catch (const MQClientException& e) { - SPDLOG_ERROR("Failed to send transaction message. Cause: {}", e.what()); +std::unique_ptr ProducerImpl::prepare(MQMessage& message, std::error_code& ec) { + message.messageType(MessageType::TRANSACTION); + SendResult send_result = send(message, ec); + if (ec) { return nullptr; } + + return std::unique_ptr(new TransactionImpl( + send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), + send_result.traceContext(), ProducerImpl::shared_from_this())); } bool ProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, @@ -509,7 +455,7 @@ bool ProducerImpl::rollback(const std::string& message_id, const std::string& tr } void ProducerImpl::asyncPublishInfo(const std::string& topic, - const std::function& cb) { + const std::function& cb) { TopicPublishInfoPtr ptr; { absl::MutexLock lock(&topic_publish_info_mtx_); @@ -517,12 +463,13 @@ void ProducerImpl::asyncPublishInfo(const std::string& topic, ptr = topic_publish_info_table_.at(topic); } } + std::error_code ec; if (ptr) { - cb(ptr); + cb(ec, ptr); } else { - auto callback = [this, topic, cb](const TopicRouteDataPtr& route) { - if (!route) { - cb(nullptr); + auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + cb(ec, nullptr); return; } @@ -531,8 +478,9 @@ void ProducerImpl::asyncPublishInfo(const std::string& topic, absl::MutexLock lk(&topic_publish_info_mtx_); topic_publish_info_table_.insert_or_assign(topic, publish_info); } - cb(publish_info); + cb(ec, publish_info); }; + getRouteFor(topic, callback); } } @@ -542,9 +490,11 @@ TopicPublishInfoPtr ProducerImpl::getPublishInfo(const std::string& topic) { absl::Mutex mtx; absl::CondVar cv; TopicPublishInfoPtr topic_publish_info; - auto cb = [&](const TopicPublishInfoPtr& ptr) { + std::error_code error_code; + auto cb = [&](const std::error_code& ec, const TopicPublishInfoPtr& ptr) { absl::MutexLock lk(&mtx); topic_publish_info = ptr; + error_code = ec; complete = true; cv.SignalAll(); }; @@ -555,35 +505,9 @@ TopicPublishInfoPtr ProducerImpl::getPublishInfo(const std::string& topic) { absl::MutexLock lk(&mtx); cv.Wait(&mtx); } - return topic_publish_info; -} -bool ProducerImpl::executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, void* arg, - std::vector& result) { - TopicPublishInfoPtr publish_info; - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto callback = [&](const TopicPublishInfoPtr& ptr) { - absl::MutexLock lk(&mtx); - publish_info = ptr; - completed = true; - cv.SignalAll(); - }; - asyncPublishInfo(message.getTopic(), callback); - - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } - - if (!publish_info) { - THROW_MQ_EXCEPTION(MQClientException, "Failed to acquire topic route data", NO_TOPIC_ROUTE_INFO); - } - - MQMessageQueue queue = selector->select(publish_info->getMessageQueueList(), message, arg); - result.emplace_back(std::move(queue)); - return true; + // TODO: propogate error_code to caller + return topic_publish_info; } void ProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, @@ -594,17 +518,19 @@ void ProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publis publish_info->takeMessageQueues(isolated, message_queues, number); } -std::vector ProducerImpl::getTopicMessageQueueInfo(const std::string& topic) { +std::vector ProducerImpl::listMessageQueue(const std::string& topic, std::error_code& ec) { absl::Mutex mtx; absl::CondVar cv; bool completed = false; TopicPublishInfoPtr ptr; - auto await_callback = [&](const TopicPublishInfoPtr& publish_info) { + auto await_callback = [&](const std::error_code& error_code, const TopicPublishInfoPtr& publish_info) { absl::MutexLock lk(&mtx); ptr = publish_info; + ec = error_code; completed = true; cv.SignalAll(); }; + asyncPublishInfo(topic, await_callback); while (!completed) { @@ -612,8 +538,8 @@ std::vector ProducerImpl::getTopicMessageQueueInfo(const std::st cv.Wait(&mtx); } - if (!ptr) { - THROW_MQ_EXCEPTION(MQClientException, "Failed to acquire topic publish_info", NO_TOPIC_ROUTE_INFO); + if (ec) { + return {}; } return ptr->getMessageQueueList(); diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index df4514677..daec94349 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -2,8 +2,12 @@ #include "ClientManagerFactory.h" #include "InvocationContext.h" #include "Signature.h" -#include "rocketmq/MessageModel.h" #include "apache/rocketmq/v1/definition.pb.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQClientException.h" +#include "rocketmq/MessageModel.h" +#include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -43,15 +47,21 @@ std::future> PullConsumerImpl::queuesFor(const std:: return promise->get_future(); } } - auto callback = [promise](const TopicRouteDataPtr& route) { - if (route) { - std::vector message_queues; - for (const auto& partition : route->partitions()) { - message_queues.emplace_back(partition.asMessageQueue()); - } - promise->set_value(message_queues); + + auto callback = [promise](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); + promise->set_exception(std::make_exception_ptr(e)); + return; + } + + std::vector message_queues; + for (const auto& partition : route->partitions()) { + message_queues.emplace_back(partition.asMessageQueue()); } + promise->set_value(message_queues); }; + getRouteFor(topic, callback); return promise->get_future(); } @@ -85,13 +95,13 @@ std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { // TODO: Use std::unique_ptr if C++14 is adopted. auto promise_ptr = std::make_shared>(); - auto callback = [promise_ptr](bool ok, const QueryOffsetResponse& response) { - if (ok) { - promise_ptr->set_value(response.offset()); + auto callback = [promise_ptr](const std::error_code& ec, const QueryOffsetResponse& response) { + if (ec) { + MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); + promise_ptr->set_exception(std::make_exception_ptr(e)); return; } - MQClientException e("Failed to query offset", -1, __FILE__, __LINE__); - promise_ptr->set_exception(std::make_exception_ptr(e)); + promise_ptr->set_value(response.offset()); }; client_manager_->queryOffset(query.message_queue.serviceAddress(), metadata, request, @@ -119,28 +129,63 @@ void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { auto callback = [this, target_host, cb](const InvocationContext* invocation_context) { if (!invocation_context || !invocation_context->status.ok()) { - MQClientException exception(fmt::format("Server[{}] is not reachable", target_host), -1, __FILE__, __LINE__); - cb->onException(exception); + std::error_code ec = ErrorCode::RequestTimeout; + cb->onFailure(ec); return; } auto response = invocation_context->response; auto biz_status = response.common().status(); - if (google::rpc::Code::OK != biz_status.code()) { - MQClientException exception(response.common().status().message(), biz_status.code(), __FILE__, __LINE__); - cb->onException(exception); - return; - } - std::vector messages; - for (const auto& item : response.messages()) { - MQMessageExt message_ext; - if (client_manager_->wrapMessage(item, message_ext)) { - messages.emplace_back(message_ext); + switch (biz_status.code()) { + case google::rpc::Code::OK: { + std::vector messages; + for (const auto& item : response.messages()) { + MQMessageExt message_ext; + if (client_manager_->wrapMessage(item, message_ext)) { + messages.emplace_back(message_ext); + } } + PullResult pull_result(response.min_offset(), response.max_offset(), response.next_offset(), std::move(messages)); + cb->onSuccess(pull_result); + } break; + + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}", response.common().status().message()); + std::error_code ec = ErrorCode::Forbidden; + cb->onFailure(ec); + } break; + + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}", response.common().status().message()); + std::error_code ec = ErrorCode::Unauthorized; + cb->onFailure(ec); + } break; + + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("GatewayTimeout: {}", response.common().status().message()); + std::error_code ec = ErrorCode::GatewayTimeout; + cb->onFailure(ec); + } break; + + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("BadRequest: {}", response.common().status().message()); + std::error_code ec = ErrorCode::BadRequest; + cb->onFailure(ec); + } break; + + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("ServerIntervalError: {}", response.common().status().message()); + std::error_code ec = ErrorCode::InternalServerError; + cb->onFailure(ec); + } break; + + default: { + SPDLOG_WARN("Unsupported response code. Please upgrade to lastest SDK"); + std::error_code ec = ErrorCode::NotImplemented; + cb->onFailure(ec); + } break; } - PullResult pull_result(response.min_offset(), response.max_offset(), response.next_offset(), std::move(messages)); - cb->onSuccess(pull_result); }; absl::flat_hash_map metadata; diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 99550b124..c0508c181 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -1,5 +1,12 @@ #include "PushConsumerImpl.h" +#include +#include +#include +#include + +#include "apache/rocketmq/v1/definition.pb.h" + #include "AsyncReceiveMessageCallback.h" #include "ClientManagerFactory.h" #include "MessageAccessor.h" @@ -9,16 +16,15 @@ #include "Signature.h" #include "rocketmq/MQClientException.h" #include "rocketmq/MessageModel.h" -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN -PushConsumerImpl::PushConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) {} +PushConsumerImpl::PushConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) { +} -PushConsumerImpl::~PushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); } +PushConsumerImpl::~PushConsumerImpl() { + SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); +} void PushConsumerImpl::start() { ClientImpl::start(); @@ -152,11 +158,12 @@ void PushConsumerImpl::scanAssignments() { std::string topic = entry.first; const auto& filter_expression = entry.second; SPDLOG_DEBUG("Scan assignments for {}", topic); - auto callback = [this, topic, filter_expression](const TopicAssignmentPtr& assignments) { - if (assignments && !assignments->assignmentList().empty()) { + auto callback = [this, topic, filter_expression](const std::error_code& ec, + const TopicAssignmentPtr& assignments) { + if (ec) { + SPDLOG_WARN("Failed to acquire assignments for topic={} from load balancer. Cause: {}", topic, ec.message()); + } else if (assignments && !assignments->assignmentList().empty()) { syncProcessQueue(topic, assignments, filter_expression); - } else { - SPDLOG_WARN("Failed to acquire assignments for topic={} from load balancer for the first time", topic); } }; queryAssignment(topic, callback); @@ -193,15 +200,15 @@ void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, cons request.set_client_id(client_id); } -void PushConsumerImpl::queryAssignment(const std::string& topic, - const std::function& cb) { +void PushConsumerImpl::queryAssignment( + const std::string& topic, const std::function& cb) { - auto callback = [this, topic, cb](const TopicRouteDataPtr& topic_route) { + auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& topic_route) { TopicAssignmentPtr topic_assignment; if (MessageModel::BROADCASTING == message_model_) { - if (!topic_route) { - SPDLOG_WARN("Failed to get valid route entries for topic={}", topic); - cb(topic_assignment); + if (ec) { + SPDLOG_WARN("Failed to get valid route entries for topic={}. Cause: {}", topic, ec.message()); + cb(ec, topic_assignment); } std::vector assignments; @@ -210,7 +217,7 @@ void PushConsumerImpl::queryAssignment(const std::string& topic, assignments.emplace_back(Assignment(partition.asMessageQueue())); } topic_assignment = std::make_shared(std::move(assignments)); - cb(topic_assignment); + cb(ec, topic_assignment); return; } @@ -226,16 +233,16 @@ void PushConsumerImpl::queryAssignment(const std::string& topic, absl::flat_hash_map metadata; Signature::sign(this, metadata); - - auto assignment_callback = [this, cb, topic, broker_host](bool ok, const QueryAssignmentResponse& response) { - if (ok) { + auto assignment_callback = [this, cb, topic, broker_host](const std::error_code& ec, + const QueryAssignmentResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); + cb(ec, nullptr); + } else { SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, group_name_, response.assignments().size()); SPDLOG_TRACE("Query assignment response for {} is: {}", topic, response.DebugString()); - cb(std::make_shared(response)); - } else { - SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); - cb(nullptr); + cb(ec, std::make_shared(response)); } }; @@ -348,42 +355,45 @@ bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const } switch (receive_message_policy_) { - case ReceiveMessageAction::PULL: { - int64_t offset = -1; - if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { - // Query latest offset from server. - QueryOffsetRequest request; - request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); - request.mutable_partition()->set_id(message_queue.getQueueId()); - request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); - request.set_policy(rmq::QueryOffsetPolicy::END); - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - auto callback = [broker_host, message_queue, process_queue_ptr](bool ok, const QueryOffsetResponse& response) { - if (ok) { - assert(response.offset() >= 0); - process_queue_ptr->nextOffset(response.offset()); - process_queue_ptr->receiveMessage(); - } else { - SPDLOG_WARN("Failed to acquire latest offset for partition[{}] from server[host={}]", - message_queue.simpleName(), broker_host); - } - }; - client_manager_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + case ReceiveMessageAction::PULL: { + int64_t offset = -1; + if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { + // Query latest offset from server. + QueryOffsetRequest request; + request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); + request.mutable_partition()->set_id(message_queue.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); + request.set_policy(rmq::QueryOffsetPolicy::END); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + auto callback = [broker_host, message_queue, process_queue_ptr](const std::error_code& ec, + const QueryOffsetResponse& response) { + if (ec) { + SPDLOG_WARN("Failed to acquire latest offset for partition[{}] from server[host={}]. Cause: {}", + message_queue.simpleName(), broker_host, ec.message()); + } else { + assert(response.offset() >= 0); + process_queue_ptr->nextOffset(response.offset()); + process_queue_ptr->receiveMessage(); + } + }; + client_manager_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + } + break; } - break; - } - case ReceiveMessageAction::POLLING: - process_queue_ptr->receiveMessage(); - break; + case ReceiveMessageAction::POLLING: + process_queue_ptr->receiveMessage(); + break; } return true; } -std::shared_ptr PushConsumerImpl::getConsumeMessageService() { return consume_message_service_; } +std::shared_ptr PushConsumerImpl::getConsumeMessageService() { + return consume_message_service_; +} -void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { +void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { const std::string& target_host = MessageAccessor::targetEndpoint(msg); assert(!target_host.empty()); SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, @@ -395,7 +405,7 @@ void PushConsumerImpl::ack(const MQMessageExt& msg, const std::functionack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); } -void PushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { +void PushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { std::string target_host = MessageAccessor::targetEndpoint(msg); absl::flat_hash_map metadata; @@ -452,7 +462,9 @@ void PushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessage request.set_receipt_handle(msg.receiptHandle()); } -uint32_t PushConsumerImpl::consumeThreadPoolSize() const { return consume_thread_pool_size_; } +uint32_t PushConsumerImpl::consumeThreadPoolSize() const { + return consume_thread_pool_size_; +} void PushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { if (thread_pool_size >= 1) { @@ -460,7 +472,9 @@ void PushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { } } -uint32_t PushConsumerImpl::consumeBatchSize() const { return consume_batch_size_; } +uint32_t PushConsumerImpl::consumeBatchSize() const { + return consume_batch_size_; +} void PushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { @@ -492,15 +506,6 @@ void PushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) } } -#ifdef ENABLE_TRACING -nostd::shared_ptr DefaultMQPushConsumerImpl::getTracer() { - if (nullptr == client_manager_) { - return nostd::shared_ptr(nullptr); - } - return client_manager_->getTracer(); -} -#endif - void PushConsumerImpl::iterateProcessQueue(const std::function& callback) { absl::MutexLock lock(&process_queue_table_mtx_); for (const auto& item : process_queue_table_) { @@ -527,11 +532,11 @@ void PushConsumerImpl::fetchRoutes() { absl::Mutex mtx; absl::CondVar cv; int acquired = 0; - auto callback = [&](const TopicRouteDataPtr& route) { + auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr& route) { absl::MutexLock lk(&mtx); countdown--; cv.SignalAll(); - if (route) { + if (!ec) { acquired++; } }; @@ -552,12 +557,12 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { if (message_listener_) { switch (message_listener_->listenerType()) { - case MessageListenerType::FIFO: - request.set_fifo_flag(true); - break; - case MessageListenerType::STANDARD: - request.set_fifo_flag(false); - break; + case MessageListenerType::FIFO: + request.set_fifo_flag(true); + break; + case MessageListenerType::STANDARD: + request.set_fifo_flag(false); + break; } } @@ -566,14 +571,14 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); switch (message_model_) { - case MessageModel::BROADCASTING: - consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); - break; - case MessageModel::CLUSTERING: - consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); - break; - default: - break; + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; + default: + break; } auto subscriptions = consumer_data->mutable_subscriptions(); @@ -586,12 +591,12 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { subscription->mutable_topic()->set_name(entry.first); subscription->mutable_expression()->set_expression(entry.second.content_); switch (entry.second.type_) { - case ExpressionType::TAG: - subscription->mutable_expression()->set_type(rmq::FilterType::TAG); - break; - case ExpressionType::SQL92: - subscription->mutable_expression()->set_type(rmq::FilterType::SQL); - break; + case ExpressionType::TAG: + subscription->mutable_expression()->set_type(rmq::FilterType::TAG); + break; + case ExpressionType::SQL92: + subscription->mutable_expression()->set_type(rmq::FilterType::SQL); + break; } subscriptions->AddAllocated(subscription); } diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index 8b067ca0d..ae680e387 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -10,11 +10,11 @@ ROCKETMQ_NAMESPACE_BEGIN -void OnewaySendCallback::onException(const MQException& e) { - SPDLOG_WARN("Failed to one-way send message. Message: {}", e.what()); +void OnewaySendCallback::onFailure(const std::error_code& ec) noexcept { + SPDLOG_WARN("Failed to one-way send message. Message: {}", ec.message()); } -void OnewaySendCallback::onSuccess(SendResult& send_result) { +void OnewaySendCallback::onSuccess(SendResult& send_result) noexcept { SPDLOG_DEBUG("Send message in one-way OK. MessageId: {}", send_result.getMsgId()); } @@ -30,23 +30,21 @@ void AwaitSendCallback::await() { } } -void AwaitSendCallback::onSuccess(SendResult& send_result) { +void AwaitSendCallback::onSuccess(SendResult& send_result) noexcept { send_result_ = send_result; - success_ = true; completed_ = true; absl::MutexLock lk(&mtx_); cv_.SignalAll(); } -void AwaitSendCallback::onException(const MQException& e) { - success_ = false; - error_message_ = e.what(); +void AwaitSendCallback::onFailure(const std::error_code& ec) noexcept { completed_ = true; + ec_ = ec; absl::MutexLock lk(&mtx_); cv_.SignalAll(); } -void RetrySendCallback::onSuccess(SendResult& send_result) { +void RetrySendCallback::onSuccess(SendResult& send_result) noexcept { { // Mark end of send-message span. span_.SetStatus(opencensus::trace::StatusCode::OK); @@ -58,7 +56,7 @@ void RetrySendCallback::onSuccess(SendResult& send_result) { delete this; } -void RetrySendCallback::onException(const MQException& e) { +void RetrySendCallback::onFailure(const std::error_code& ec) noexcept { { // Mark end of the send-message span. span_.SetStatus(opencensus::trace::StatusCode::INTERNAL); @@ -67,7 +65,7 @@ void RetrySendCallback::onException(const MQException& e) { if (++attempt_times_ >= max_attempt_times_) { SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, max_attempt_times_); - callback_->onException(e); + callback_->onFailure(ec); delete this; return; } @@ -75,14 +73,14 @@ void RetrySendCallback::onException(const MQException& e) { std::shared_ptr producer = producer_.lock(); if (!producer) { SPDLOG_WARN("Producer has been destructed"); - callback_->onException(e); + callback_->onFailure(ec); delete this; return; } if (candidates_.empty()) { SPDLOG_WARN("No alternative hosts to perform additional retries"); - callback_->onException(e); + callback_->onFailure(ec); delete this; return; } diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h index 0c415c37b..597819a71 100644 --- a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -2,6 +2,7 @@ #include "ProcessQueue.h" #include "ReceiveMessageCallback.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -14,7 +15,7 @@ class AsyncReceiveMessageCallback : public ReceiveMessageCallback, void onSuccess(ReceiveMessageResult& result) override; - void onException(MQException& e) override; + void onFailure(const std::error_code& ec) override; void receiveMessageLater(); diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index 95d957238..971bb6eda 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -1,3 +1,7 @@ +#pragma once + +#include + #include "absl/synchronization/mutex.h" #include "rocketmq/AsyncCallback.h" @@ -8,22 +12,24 @@ class AwaitPullCallback : public PullCallback { public: explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) {} - void onSuccess(const PullResult& pull_result) override; + void onSuccess(const PullResult& pull_result) noexcept override; - void onException(const MQException& e) override; + void onFailure(const std::error_code& ec) noexcept override; bool await(); - bool hasFailure() const { return has_failure_; } + bool hasFailure() const { return ec_.operator bool(); } bool isCompleted() const { return completed_; } + const std::error_code& errorCode() const noexcept { return ec_; } + private: PullResult& pull_result_; absl::Mutex mtx_; absl::CondVar cv_; bool completed_{false}; - bool has_failure_{false}; + std::error_code ec_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 40a9756a8..342bdcc0b 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -2,6 +2,7 @@ #include #include +#include #include "absl/strings/string_view.h" #include "apache/rocketmq/v1/definition.pb.h" @@ -28,11 +29,12 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { virtual void shutdown(); - void getRouteFor(const std::string& topic, const std::function& cb) + void getRouteFor(const std::string& topic, const std::function& cb) LOCKS_EXCLUDED(inflight_route_requests_mtx_, topic_route_table_mtx_); /** - * Gather collection of endpoints that are reachable from latest topic route table. + * Gather collection of endpoints that are reachable from latest topic route + * table. * * @param endpoints */ @@ -45,7 +47,9 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { return State::STARTING == state || State::STARTED == state; } - void healthCheck() LOCKS_EXCLUDED(isolated_endpoints_mtx_) override; + void onRemoteEndpointRemoval(const std::vector& hosts) override LOCKS_EXCLUDED(isolated_endpoints_mtx_); + + void healthCheck() override LOCKS_EXCLUDED(isolated_endpoints_mtx_); void schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) override; @@ -62,7 +66,7 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); absl::Mutex topic_route_table_mtx_ ACQUIRED_AFTER(inflight_route_requests_mtx_); // protects topic_route_table_ - absl::flat_hash_map>> + absl::flat_hash_map>> inflight_route_requests_ GUARDED_BY(inflight_route_requests_mtx_); absl::Mutex inflight_route_requests_mtx_ ACQUIRED_BEFORE(topic_route_table_mtx_); // Protects inflight_route_requests_ static const char* UPDATE_ROUTE_TASK_NAME; @@ -91,7 +95,8 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) {} /** - * Concrete publisher/subscriber client is expected to fill other type-specific resources. + * Concrete publisher/subscriber client is expected to fill other + * type-specific resources. */ virtual ClientResourceBundle resourceBundle() { ClientResourceBundle resource_bundle; @@ -106,36 +111,38 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { private: /** - * This is a low-level API that fetches route data from name server through gRPC unary request/response. Once - * request/response is completed, either timeout or response arrival in time, callback would get invoked. + * This is a low-level API that fetches route data from name server through + * gRPC unary request/response. Once request/response is completed, either + * timeout or response arrival in time, callback would get invoked. * @param topic * @param cb */ - void fetchRouteFor(const std::string& topic, const std::function& cb); + void fetchRouteFor(const std::string& topic, + const std::function& cb); /** * Callback to execute once route data is fetched from name server. * @param topic * @param route */ - void onTopicRouteReady(const std::string& topic, const TopicRouteDataPtr& route) + void onTopicRouteReady(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(inflight_route_requests_mtx_); /** - * Update local cache for the topic. Note, route differences are logged in INFO level since route bears fundamental - * importance. + * Update local cache for the topic. Note, route differences are logged in + * INFO level since route bears fundamental importance. * * @param topic * @param route */ - void updateRouteCache(const std::string& topic, const TopicRouteDataPtr& route) + void updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(topic_route_table_mtx_); void multiplexing(const std::string& target, const MultiplexingRequest& request); void onMultiplexingResponse(const InvocationContext* ctx); - void onHealthCheckResponse(const std::string& endpoint, const InvocationContext* ctx) + void onHealthCheckResponse(const std::error_code& endpoint, const InvocationContext* ctx) LOCKS_EXCLUDED(isolated_endpoints_mtx_); void fillGenericPollingRequest(MultiplexingRequest& request); diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index a205705e4..de4e62fd3 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "ProcessQueue.h" #include "RateLimiter.h" @@ -123,7 +124,7 @@ class ConsumeFifoMessageService : public ConsumeMessageService, void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); - void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, const std::error_code& ec); void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index 43459c2c0..263b11f5a 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "absl/strings/string_view.h" @@ -35,24 +36,15 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void shutdown() override; - SendResult send(const MQMessage& message); - SendResult send(const MQMessage& message, const std::string& message_group); - SendResult send(const MQMessage& message, const MQMessageQueue& message_queue); - SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg); - - SendResult send(const MQMessage& message, MessageQueueSelector* selector, void* arg, int max_attempts); + SendResult send(const MQMessage& message, std::error_code& ec) noexcept; void send(const MQMessage& message, SendCallback* callback); - void send(const MQMessage& message, const MQMessageQueue& message_queue, SendCallback* callback); - void send(const MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* callback); - void sendOneway(const MQMessage& message); - void sendOneway(const MQMessage& message, const MQMessageQueue& message_queue); - void sendOneway(const MQMessage& message, MessageQueueSelector* selector, void* arg); + void sendOneway(const MQMessage& message, std::error_code& ec); void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); - std::unique_ptr prepare(MQMessage& message); + std::unique_ptr prepare(MQMessage& message, std::error_code& ec); bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, const std::string& target); @@ -81,7 +73,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void setFailedTimes(int times) { failed_times_ = times; } - std::vector getTopicMessageQueueInfo(const std::string& topic); + std::vector listMessageQueue(const std::string& topic, std::error_code& ec); uint32_t compressBodyThreshold() const { return compress_body_threshold_; } @@ -112,14 +104,12 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t LocalTransactionStateCheckerPtr transaction_state_checker_; - void asyncPublishInfo(const std::string& topic, const std::function& cb) + void asyncPublishInfo(const std::string& topic, + const std::function& cb) LOCKS_EXCLUDED(topic_publish_info_mtx_); TopicPublishInfoPtr getPublishInfo(const std::string& topic); - bool executeMessageQueueSelector(const MQMessage& message, MessageQueueSelector* selector, void* arg, - std::vector& result); - void takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, std::vector& message_queues, int number); @@ -128,7 +118,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t bool isRunning() const; - void ensureRunning() const; + void ensureRunning(std::error_code& ec) const noexcept; bool validate(const MQMessage& message); @@ -139,7 +129,7 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); - MQMessageQueue withServiceAddress(const MQMessageQueue& message_queue); + MQMessageQueue withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index c71bd1e44..484272771 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -2,6 +2,7 @@ #include #include +#include #include "ConsumeMessageService.h" #include "Consumer.h" @@ -20,7 +21,7 @@ class PushConsumer : virtual public Consumer { virtual MessageModel messageModel() const = 0; - virtual void ack(const MQMessageExt& msg, const std::function& callback) = 0; + virtual void ack(const MQMessageExt& msg, const std::function& callback) = 0; virtual void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) = 0; @@ -32,7 +33,7 @@ class PushConsumer : virtual public Consumer { virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; - virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; + virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; virtual std::shared_ptr getConsumeMessageService() = 0; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index c03653bfb..7499593c1 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "absl/strings/string_view.h" @@ -72,7 +73,8 @@ class PushConsumerImpl : virtual public ClientImpl, * @param topic Topic to query * @return shared pointer to topic assignment info */ - void queryAssignment(const std::string& topic, const std::function& cb); + void queryAssignment(const std::string& topic, + const std::function& cb); void syncProcessQueue(const std::string& topic, const TopicAssignmentPtr& topic_assignment, const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); @@ -98,7 +100,7 @@ class PushConsumerImpl : virtual public ClientImpl, std::shared_ptr getConsumeMessageService() override; - void ack(const MQMessageExt& msg, const std::function& callback) override; + void ack(const MQMessageExt& msg, const std::function& callback) override; /** * Negative acknowledge the given message; Refer to @@ -109,7 +111,7 @@ class PushConsumerImpl : virtual public ClientImpl, * * @param message Message to negate on the broker side. */ - void nack(const MQMessageExt& message, const std::function& callback) override; + void nack(const MQMessageExt& message, const std::function& callback) override; void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) override; diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index bdcc92803..3063f93de 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "absl/container/flat_hash_map.h" #include "absl/synchronization/mutex.h" @@ -9,6 +10,7 @@ #include "TransactionImpl.h" #include "rocketmq/AsyncCallback.h" +#include "rocketmq/ErrorCode.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" @@ -18,69 +20,65 @@ using SendMessageRequest = apache::rocketmq::v1::SendMessageRequest; class OnewaySendCallback : public SendCallback { public: - void onSuccess(SendResult &send_result) override; + void onSuccess(SendResult& send_result) noexcept override; - void onException(const MQException &e) override; + void onFailure(const std::error_code& ec) noexcept override; }; -OnewaySendCallback *onewaySendCallback(); +OnewaySendCallback* onewaySendCallback(); class AwaitSendCallback : public SendCallback { public: - void onSuccess(SendResult &send_result) override; + void onSuccess(SendResult& send_result) noexcept override; - void onException(const MQException &e) override; + void onFailure(const std::error_code& ec) noexcept override; void await(); - explicit operator bool() const { return success_; } + explicit operator bool() const { return !ec_.operator bool(); } - const SendResult &sendResult() const { return send_result_; } + const SendResult& sendResult() const { return send_result_; } - const std::string &errorMessage() const { return error_message_; } + const std::error_code& errorCode() const { return ec_; } private: absl::Mutex mtx_; absl::CondVar cv_; bool completed_{false}; - bool success_{false}; SendResult send_result_; - std::string error_message_; + std::error_code ec_; }; class ProducerImpl; class RetrySendCallback : public SendCallback { public: - RetrySendCallback(std::weak_ptr producer, MQMessage message, - int max_attempt_times, SendCallback *callback, - std::vector candidates) - : producer_(std::move(producer)), message_(std::move(message)), - max_attempt_times_(max_attempt_times), callback_(callback), - candidates_(std::move(candidates)), - span_(opencensus::trace::Span::BlankSpan()) {} + RetrySendCallback(std::weak_ptr producer, MQMessage message, int max_attempt_times, + SendCallback* callback, std::vector candidates) + : producer_(std::move(producer)), message_(std::move(message)), max_attempt_times_(max_attempt_times), + callback_(callback), candidates_(std::move(candidates)), span_(opencensus::trace::Span::BlankSpan()) {} - void onSuccess(SendResult &send_result) override; + void onSuccess(SendResult& send_result) noexcept override; - void onException(const MQException &e) override; + void onFailure(const std::error_code& ec) noexcept override; - MQMessage &message() { return message_; } + MQMessage& message() { return message_; } int attemptTime() const { return attempt_times_; } - const MQMessageQueue &messageQueue() const { + const MQMessageQueue& messageQueue() const { int index = attempt_times_ % candidates_.size(); return candidates_[index]; } - opencensus::trace::Span &span() { return span_; } + opencensus::trace::Span& span() { return span_; } private: std::weak_ptr producer_; MQMessage message_; int attempt_times_{0}; int max_attempt_times_; - SendCallback *callback_{nullptr}; + SendCallback* callback_{nullptr}; /** * @brief Once the first publish attempt failed, the following routable diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index feb93677b..059577db3 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -2,6 +2,7 @@ #include "ConsumerMock.h" #include "PushConsumer.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -11,7 +12,7 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(MessageModel, messageModel, (), (const override)); - MOCK_METHOD(void, ack, (const MQMessageExt&, const std::function&), (override)); + MOCK_METHOD(void, ack, (const MQMessageExt&, const std::function&), (override)); MOCK_METHOD(void, forwardToDeadLetterQueue, (const MQMessageExt&, const std::function&), (override)); @@ -23,7 +24,7 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(void, updateOffset, (const MQMessageQueue&, int64_t), (override)); - MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); + MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); diff --git a/src/test/cpp/ut/api/BUILD.bazel b/src/test/cpp/ut/api/BUILD.bazel new file mode 100644 index 000000000..eda5f4bc8 --- /dev/null +++ b/src/test/cpp/ut/api/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "error_code_test", + srcs = [ + "ErrorCodeTest.cpp", + ], + deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/api/ErrorCodeTest.cpp b/src/test/cpp/ut/api/ErrorCodeTest.cpp new file mode 100644 index 000000000..42b525f17 --- /dev/null +++ b/src/test/cpp/ut/api/ErrorCodeTest.cpp @@ -0,0 +1,14 @@ +#include "rocketmq/ErrorCode.h" +#include "gtest/gtest.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(ErrorCodeTest, testErrorCode) { + std::error_code ec = ErrorCode::BadRequest; + if (ec) { + std::cout << ec.message() << std::endl; + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index 8cfa79ef0..e14ed0249 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -4,6 +4,7 @@ #include "apache/rocketmq/v1/definition.pb.h" #include "gtest/gtest.h" #include +#include ROCKETMQ_NAMESPACE_BEGIN @@ -20,7 +21,9 @@ class ClientManagerTest : public testing::Test { metadata_.insert({"name", "Donald.J.Trump"}); } - void TearDown() override { client_manager_->shutdown(); } + void TearDown() override { + client_manager_->shutdown(); + } protected: std::string resource_namespace_{"mq://test"}; @@ -65,7 +68,7 @@ TEST_F(ClientManagerTest, testResolveRoute) { QueryRouteRequest request; request.mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_topic()->set_name(topic_); - auto callback = [&](bool, const TopicRouteDataPtr&) { + auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr&) { absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); @@ -97,7 +100,7 @@ TEST_F(ClientManagerTest, testQueryAssignment) { .WillRepeatedly(testing::Invoke(mock_query_assignment)); QueryAssignmentRequest request; bool callback_invoked = false; - auto callback = [&](bool ok, const QueryAssignmentResponse& response) { callback_invoked = true; }; + auto callback = [&](const std::error_code& ec, const QueryAssignmentResponse& response) { callback_invoked = true; }; client_manager_->queryAssignment(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); @@ -164,7 +167,7 @@ TEST_F(ClientManagerTest, testReceiveMessage_Failure) { .WillRepeatedly(testing::Invoke(mock_async_receive)); ReceiveMessageRequest request; - EXPECT_CALL(*receive_message_callback_, onException).Times(testing::AtLeast(1)); + EXPECT_CALL(*receive_message_callback_, onFailure).Times(testing::AtLeast(1)); client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), receive_message_callback_); @@ -193,7 +196,7 @@ TEST_F(ClientManagerTest, testAck) { EXPECT_CALL(*rpc_client_, asyncAck).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_ack)); AckMessageRequest request; bool callback_invoked = false; - auto callback = [&](bool ok) { callback_invoked = true; }; + auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; client_manager_->ack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); @@ -222,7 +225,7 @@ TEST_F(ClientManagerTest, testNack) { EXPECT_CALL(*rpc_client_, asyncNack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_nack)); NackMessageRequest request; bool callback_invoked = false; - auto callback = [&](bool ok) { callback_invoked = true; }; + auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; client_manager_->nack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); @@ -268,7 +271,8 @@ TEST_F(ClientManagerTest, testForwardMessageToDeadLetterQueue) { EXPECT_TRUE(callback_invoked); } -TEST_F(ClientManagerTest, testMultiplexingCall) {} +TEST_F(ClientManagerTest, testMultiplexingCall) { +} TEST_F(ClientManagerTest, testEndTransaction) { bool completed = false; @@ -288,7 +292,7 @@ TEST_F(ClientManagerTest, testEndTransaction) { .WillRepeatedly(testing::Invoke(mock_end_transaction)); EndTransactionRequest request; bool callback_invoked = false; - auto callback = [&](bool ok, const EndTransactionResponse& response) { callback_invoked = true; }; + auto callback = [&](const std::error_code& ec, const EndTransactionResponse& response) { callback_invoked = true; }; client_manager_->endTransaction(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); { @@ -342,8 +346,9 @@ TEST_F(ClientManagerTest, testHealthCheck) { .WillRepeatedly(testing::Invoke(mock_health_check)); HealthCheckRequest request; bool callback_invoked = false; - auto callback = [&](const std::string& target_host, - const InvocationContext* invocation_context) { callback_invoked = true; }; + auto callback = [&](const std::error_code& ec, const InvocationContext* invocation_context) { + callback_invoked = true; + }; client_manager_->healthCheck(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); { diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp index 3739c33ef..b2862714d 100644 --- a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp +++ b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp @@ -1,10 +1,13 @@ +#include +#include + #include "AwaitPullCallback.h" #include "MessageAccessor.h" +#include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/PullResult.h" #include "gtest/gtest.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -51,8 +54,8 @@ TEST(AwaitPullCallbackTest, testOnFailure) { AwaitPullCallback callback(pull_result); std::string topic{"Test"}; auto task = [&]() { - MQClientException e("test exception", -1, __FILE__, __LINE__); - callback.onException(e); + std::error_code ec = ErrorCode::NotImplemented; + callback.onFailure(ec); }; std::thread t(task); diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 3d8b3d316..e7b879941 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -1,12 +1,14 @@ #include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include + #include "MQClientTest.h" #include "ProducerImpl.h" #include "rocketmq/CredentialsProvider.h" #include "rocketmq/MQSelector.h" -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN @@ -111,13 +113,15 @@ class UnitTestSendCallback : public SendCallback { public: UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) {} - void onSuccess(SendResult& send_result) override { + + void onSuccess(SendResult& send_result) noexcept override { absl::MutexLock lk(&mtx_); msg_id_ = send_result.getMsgId(); completed_ = true; cv_.SignalAll(); } - void onException(const MQException& e) override { + + void onFailure(const std::error_code& e) noexcept override { absl::MutexLock lk(&mtx_); completed_ = true; cv_.SignalAll(); @@ -136,7 +140,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { producer->withNameServerResolver(name_server_resolver_); producer->setCredentialsProvider(credentials_provider_); producer->start(); - + MQMessage message; message.setTopic(topic_); message.setBody(body_); @@ -166,7 +170,9 @@ TEST_F(DefaultMQProducerUnitTest, testSendMessage) { message.setTopic(topic_); message.setBody(body_); - SendResult send_result = producer->send(message); + std::error_code ec; + SendResult send_result = producer->send(message, ec); + EXPECT_FALSE(ec); ASSERT_EQ(send_result.getMsgId(), message_id_); producer->shutdown(); } diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index a9e253b5e..b8375b3ba 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -1,4 +1,5 @@ #include +#include #include "ClientManagerFactory.h" #include "ClientManagerMock.h" @@ -80,11 +81,13 @@ TEST_F(ProducerImplTest, testSend) { SchedulerImpl scheduler; scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -103,7 +106,9 @@ TEST_F(ProducerImplTest, testSend) { producer_->start(); MQMessage message(topic_, tag_, message_body_); - producer_->send(message); + std::error_code ec; + producer_->send(message, ec); + EXPECT_FALSE(ec); EXPECT_TRUE(cb_invoked); producer_->shutdown(); scheduler.shutdown(); @@ -113,11 +118,13 @@ TEST_F(ProducerImplTest, testSend_WithMessageGroup) { SchedulerImpl scheduler; scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -136,28 +143,26 @@ TEST_F(ProducerImplTest, testSend_WithMessageGroup) { producer_->start(); MQMessage message(topic_, tag_, message_body_); - producer_->send(message, message_group_); + message.bindMessageGroup(message_group_); + std::error_code ec; + producer_->send(message, ec); + EXPECT_FALSE(ec); EXPECT_TRUE(cb_invoked); producer_->shutdown(); scheduler.shutdown(); } -class TestMessageQueueSelector : public MessageQueueSelector { -public: - MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) override { - return *mqs.begin(); - } -}; - TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { SchedulerImpl scheduler; scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -175,10 +180,17 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); producer_->start(); - auto selector = absl::make_unique(); - MQMessage message(topic_, tag_, message_body_); - producer_->send(message, selector.get(), nullptr); + + std::error_code ec; + auto&& list = producer_->listMessageQueue(topic_, ec); + + EXPECT_FALSE(list.empty()); + + message.bindMessageQueue(list[0]); + + producer_->send(message, ec); + EXPECT_TRUE(cb_invoked); producer_->shutdown(); scheduler.shutdown(); @@ -187,12 +199,13 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { class TestSendCallback : public SendCallback { public: TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) {} - void onSuccess(SendResult& send_result) override { + void onSuccess(SendResult& send_result) noexcept override { absl::MutexLock lk(&mtx_); completed_ = true; cv_.SignalAll(); } - void onException(const MQException& e) override { + + void onFailure(const std::error_code& ec) noexcept override { absl::MutexLock lk(&mtx_); completed_ = true; cv_.SignalAll(); @@ -208,11 +221,13 @@ TEST_F(ProducerImplTest, testAsyncSend) { SchedulerImpl scheduler; scheduler.start(); ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 90f32d80b..26d3de855 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "ClientManagerFactory.h" #include "ClientManagerMock.h" @@ -78,11 +79,13 @@ TEST_F(PullConsumerImplTest, testStartShutdown) { TEST_F(PullConsumerImplTest, testQueuesFor) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -100,12 +103,12 @@ TEST_F(PullConsumerImplTest, testQueuesFor) { class TestPullCallback : public PullCallback { public: TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) {} - void onSuccess(const PullResult& pull_result) override { + void onSuccess(const PullResult& pull_result) noexcept override { success_ = true; failure_ = false; } - void onException(const MQException& e) override { + void onFailure(const std::error_code& e) noexcept override { failure_ = true; success_ = false; } @@ -117,11 +120,13 @@ class TestPullCallback : public PullCallback { TEST_F(PullConsumerImplTest, testPull) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -179,11 +184,13 @@ TEST_F(PullConsumerImplTest, testPull) { TEST_F(PullConsumerImplTest, testPull_gRPC_error) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -224,11 +231,13 @@ TEST_F(PullConsumerImplTest, testPull_gRPC_error) { TEST_F(PullConsumerImplTest, testPull_biz_error) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -269,11 +278,13 @@ TEST_F(PullConsumerImplTest, testPull_biz_error) { TEST_F(PullConsumerImplTest, testQueryOffset) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -289,7 +300,10 @@ TEST_F(PullConsumerImplTest, testQueryOffset) { auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { cb(true, response); }; + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; EXPECT_CALL(*client_manager_, queryOffset) .Times(testing::AtLeast(1)) @@ -307,11 +321,13 @@ TEST_F(PullConsumerImplTest, testQueryOffset) { TEST_F(PullConsumerImplTest, testQueryOffset_End) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -327,7 +343,10 @@ TEST_F(PullConsumerImplTest, testQueryOffset_End) { auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { cb(true, response); }; + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; EXPECT_CALL(*client_manager_, queryOffset) .Times(testing::AtLeast(1)) @@ -345,11 +364,13 @@ TEST_F(PullConsumerImplTest, testQueryOffset_End) { TEST_F(PullConsumerImplTest, testQueryOffset_Timepoint) { pull_consumer_->start(); - auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, - const QueryRouteRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(true, topic_route_data_); - }; + auto mock_resolve_route = + [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + std::error_code ec; + cb(ec, topic_route_data_); + }; EXPECT_CALL(*client_manager_, resolveRoute) .Times(testing::AtLeast(1)) @@ -365,7 +386,10 @@ TEST_F(PullConsumerImplTest, testQueryOffset_Timepoint) { auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { cb(true, response); }; + const std::function& cb) { + std::error_code ec; + cb(ec, response); + }; EXPECT_CALL(*client_manager_, queryOffset) .Times(testing::AtLeast(1)) diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 0789af4ac..4bacd982c 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -1,6 +1,7 @@ #include #include "gtest/gtest.h" +#include #include "ClientManagerFactory.h" #include "ClientManagerMock.h" @@ -11,7 +12,6 @@ #include "grpc/grpc.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/MessageListener.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -24,7 +24,8 @@ class TestStandardMessageListener : public StandardMessageListener { class PushConsumerImplTest : public testing::Test { public: - PushConsumerImplTest() : message_listener_(absl::make_unique()) {} + PushConsumerImplTest() : message_listener_(absl::make_unique()) { + } void SetUp() override { grpc_init(); @@ -39,7 +40,9 @@ class PushConsumerImplTest : public testing::Test { push_consumer_->registerMessageListener(message_listener_.get()); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } protected: std::string name_server_list_{"10.0.0.1:9876"}; @@ -63,7 +66,10 @@ TEST_F(PushConsumerImplTest, testAck) { ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) { cb(true); }; + std::chrono::milliseconds timeout, const std::function& cb) { + std::error_code ec; + cb(ec); + }; EXPECT_CALL(*client_manager_, ack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(ack_cb)); @@ -72,7 +78,7 @@ TEST_F(PushConsumerImplTest, testAck) { bool completed = false; absl::Mutex mtx; absl::CondVar cv; - auto callback = [&](bool ok) { + auto callback = [&](const std::error_code& ec) { absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); @@ -105,7 +111,10 @@ TEST_F(PushConsumerImplTest, testNack) { ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) { cb(true); }; + std::chrono::milliseconds timeout, const std::function& cb) { + std::error_code ec; + cb(ec); + }; EXPECT_CALL(*client_manager_, nack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(nack_cb)); @@ -114,7 +123,7 @@ TEST_F(PushConsumerImplTest, testNack) { bool completed = false; absl::Mutex mtx; absl::CondVar cv; - auto callback = [&](bool ok) { + auto callback = [&](const std::error_code& ec) { absl::MutexLock lk(&mtx); completed = true; cv.SignalAll(); From 3cb027430de2790cd3f67746cafbba5343ec3fa7 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 26 Sep 2021 12:07:48 +0800 Subject: [PATCH 074/133] Improve error handling of NotifyClientTermination --- src/main/cpp/client/ClientManagerImpl.cpp | 55 ++++++++++++++----- src/main/cpp/client/include/ClientManager.h | 6 +- .../cpp/client/include/ClientManagerImpl.h | 6 +- .../client/mocks/include/ClientManagerMock.h | 2 +- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 679ce3f39..7cc042eb9 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1427,7 +1427,8 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( auto callback = [cb](const InvocationContext* invocation_context) { if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to transmit SendMessageToDeadLetterQueueRequest to {}", invocation_context->remote_address); + SPDLOG_WARN("Failed to transmit SendMessageToDeadLetterQueueRequest to host={}", + invocation_context->remote_address); cb(invocation_context); return; } @@ -1439,12 +1440,15 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); } -bool ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, - const NotifyClientTerminationRequest& request, - std::chrono::milliseconds timeout) { +std::error_code ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; auto client = getRpcClient(target_host); if (!client) { - return false; + SPDLOG_WARN("Failed to create RpcClient for host={}", target_host); + ec = ErrorCode::RequestTimeout; + return ec; } grpc::ClientContext context; @@ -1456,18 +1460,41 @@ bool ClientManagerImpl::notifyClientTermination(const std::string& target_host, NotifyClientTerminationResponse response; grpc::Status status = client->notifyClientTermination(&context, request, &response); if (!status.ok()) { - SPDLOG_WARN("Failed to write NotifyClientTermination request to wire. gRPC-code: {}, gRPC-message: {}, host={}", - status.error_code(), status.error_message(), target_host); - return false; + SPDLOG_WARN("NotifyClientTermination failed. gRPC-code={}, gRPC-message={}, host={}", status.error_code(), + status.error_message(), target_host); + ec = ErrorCode::RequestTimeout; + return ec; } - if (google::rpc::Code::OK == response.common().status().code()) { - SPDLOG_INFO("Notify client termination to {}", target_host); - return true; - } + const auto& common = response.common(); - SPDLOG_WARN("Failed to notify client termination to {}", target_host); - return false; + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("NotifyClientTermination OK. host={}", target_host); + break; + } + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::InternalServerError; + break; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: Cause={}, host={}", common.status().message(), target_host); + ec = ErrorCode::Forbidden; + break; + } + default: { + SPDLOG_WARN("NotImplemented. Please upgrade to latest SDK release. host={}", target_host); + ec = ErrorCode::NotImplemented; + break; + } + } + return ec; } void ClientManagerImpl::logStats() { diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 28d0ef16f..8cd1ea580 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -87,9 +87,9 @@ class ClientManager { std::chrono::milliseconds timeout, const std::function*)>& cb) = 0; - virtual bool notifyClientTermination(const std::string& target_host, const Metadata& metadata, - const NotifyClientTerminationRequest& request, - std::chrono::milliseconds timeout) = 0; + virtual std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) = 0; virtual State state() const = 0; }; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 2cb179b1d..4432f412b 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -177,9 +177,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::chrono::milliseconds timeout, const std::function*)>& cb) override; - bool notifyClientTermination(const std::string& target_host, const Metadata& metadata, - const NotifyClientTerminationRequest& request, - std::chrono::milliseconds timeout) override; + std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, + const NotifyClientTerminationRequest& request, + std::chrono::milliseconds timeout) override; void trace(bool trace) { trace_ = trace; diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index f5e5bc2d6..1ac53eb4c 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -88,7 +88,7 @@ class ClientManagerMock : public ClientManager { (const std::function*)>&)), (override)); - MOCK_METHOD(bool, notifyClientTermination, + MOCK_METHOD(std::error_code, notifyClientTermination, (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), (override)); From 59c98789f91a7ef2d744844e4bc6fd54908fbf97 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 26 Sep 2021 17:03:35 +0800 Subject: [PATCH 075/133] BugFix: Add signature --- src/main/cpp/client/ClientManagerImpl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 7cc042eb9..e7b04c348 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1343,6 +1343,11 @@ void ClientManagerImpl::queryOffset(const std::string& target_host, const Metada auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); + + for (const auto& entry : metadata) { + invocation_context->context.AddMetadata(entry.first, entry.second); + } + auto callback = [cb](const InvocationContext* invocation_context) { std::error_code ec; From a4338df672575a9809acd069c3674b3f3e3d29be Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 26 Sep 2021 17:21:44 +0800 Subject: [PATCH 076/133] BugFix: fix pull-message missing signature metadata --- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 13 +++++++++---- src/main/cpp/rocketmq/include/ProcessQueueImpl.h | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index da7c97d7e..adfe10970 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -108,11 +108,17 @@ void ProcessQueueImpl::popMessage() { void ProcessQueueImpl::pullMessage() { rmq::PullMessageRequest request; absl::flat_hash_map metadata; - wrapPullMessageRequest(metadata, request); + auto consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_INFO("Owner consumer has destructed"); + return; + } + + Signature::sign(consumer.get(), metadata); + wrapPullMessageRequest(request); syncIdleState(); SPDLOG_DEBUG("Try to pull message from {}", message_queue_.simpleName()); - auto consumer = consumer_.lock(); auto timeout = consumer->getLongPollingTimeout(); auto callback = [this](const InvocationContext* invocation_context) { @@ -324,8 +330,7 @@ void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_mapset_nanos(nano_seconds); } -void ProcessQueueImpl::wrapPullMessageRequest(absl::flat_hash_map& metadata, - rmq::PullMessageRequest& request) { +void ProcessQueueImpl::wrapPullMessageRequest(rmq::PullMessageRequest& request) { std::shared_ptr consumer = consumer_.lock(); assert(consumer); request.set_client_id(consumer->clientId()); diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index dcda003bf..c22acbec5 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -183,8 +183,7 @@ class ProcessQueueImpl : virtual public ProcessQueue { rmq::ReceiveMessageRequest& request); void pullMessage(); - void wrapPullMessageRequest(absl::flat_hash_map& metadata, - rmq::PullMessageRequest& request); + void wrapPullMessageRequest(rmq::PullMessageRequest& request); void wrapFilterExpression(rmq::FilterExpression* filter_expression); From bb236ce089e7d53c8f9ff4fa4d5802ecf9a9fd9f Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 26 Sep 2021 21:48:14 +0800 Subject: [PATCH 077/133] Guard io_context#run with try-catch --- src/main/cpp/base/HttpClientImpl.cpp | 22 ++++++------- src/main/cpp/base/ThreadPoolImpl.cpp | 23 ++++++++++++-- src/main/cpp/base/include/HttpClientImpl.h | 4 +-- src/main/cpp/scheduler/SchedulerImpl.cpp | 34 ++++++++++++++++----- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 5 +-- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/main/cpp/base/HttpClientImpl.cpp b/src/main/cpp/base/HttpClientImpl.cpp index e4fbfa512..d4b484325 100644 --- a/src/main/cpp/base/HttpClientImpl.cpp +++ b/src/main/cpp/base/HttpClientImpl.cpp @@ -10,13 +10,11 @@ ROCKETMQ_NAMESPACE_BEGIN -HttpClientImpl::HttpClientImpl() {} - -HttpClientImpl::~HttpClientImpl() {} - -void HttpClientImpl::start() {} +void HttpClientImpl::start() { +} -void HttpClientImpl::shutdown() {} +void HttpClientImpl::shutdown() { +} /** * @brief We current implement this function in sync mode since async http request in CURL is sort of unnecessarily @@ -34,12 +32,12 @@ void HttpClientImpl::get( std::string key; switch (protocol) { - case HttpProtocol::HTTP: - key = fmt::format("http://{}:{}", host, port); - break; - case HttpProtocol::HTTPS: - key = fmt::format("https://{}:{}", host, port); - break; + case HttpProtocol::HTTP: + key = fmt::format("http://{}:{}", host, port); + break; + case HttpProtocol::HTTPS: + key = fmt::format("https://{}:{}", host, port); + break; } std::shared_ptr client; diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp index dee83ceee..675c40260 100644 --- a/src/main/cpp/base/ThreadPoolImpl.cpp +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -8,13 +8,16 @@ #include "spdlog/spdlog.h" #include #include +#include +#include ROCKETMQ_NAMESPACE_BEGIN ThreadPoolImpl::ThreadPoolImpl(std::uint16_t workers) : work_guard_( absl::make_unique>(context_.get_executor())), - workers_(workers) {} + workers_(workers) { +} void ThreadPoolImpl::start() { for (std::uint16_t i = 0; i < workers_; i++) { @@ -24,7 +27,23 @@ void ThreadPoolImpl::start() { absl::MutexLock lk(&start_mtx_); start_cv_.SignalAll(); } - context_.run(); + + while (true) { + try { + std::error_code ec; + context_.run(ec); + if (ec) { + SPDLOG_WARN("Error raised from ThreadPool: {}", ec.message()); + } + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised from ThreadPool: {}", e.what()); + } + + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_INFO("A thread-pool worker quit"); + break; + } + } }); threads_.emplace_back(std::move(worker)); } diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h index e8f37aef5..bf93b0c36 100644 --- a/src/main/cpp/base/include/HttpClientImpl.h +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -12,9 +12,7 @@ ROCKETMQ_NAMESPACE_BEGIN class HttpClientImpl : public HttpClient { public: - HttpClientImpl(); - - ~HttpClientImpl() override; + ~HttpClientImpl() override = default; void start() override; diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index f50079f3f..e8de4b2b5 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -1,19 +1,23 @@ #include "SchedulerImpl.h" -#include "LoggerImpl.h" -#include "absl/memory/memory.h" -#include "asio/error_code.hpp" -#include "asio/executor_work_guard.hpp" -#include "asio/io_context.hpp" -#include "asio/steady_timer.hpp" #include #include #include #include +#include #include #include +#include #include +#include "absl/memory/memory.h" +#include "asio/error_code.hpp" +#include "asio/executor_work_guard.hpp" +#include "asio/io_context.hpp" +#include "asio/steady_timer.hpp" + +#include "LoggerImpl.h" + ROCKETMQ_NAMESPACE_BEGIN SchedulerImpl::SchedulerImpl() @@ -35,7 +39,23 @@ void SchedulerImpl::start() { start_cv_.SignalAll(); } } - context_.run(); + + while (true) { + try { + std::error_code ec; + context_.run(ec); + if (ec) { + SPDLOG_WARN("Error raised from thread-pool: {}", ec.message()); + } + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised from thread-pool: {}", e.what()); + } + + if (State::STARTED != state_.load(std::memory_order_relaxed)) { + SPDLOG_INFO("A scheduler worker quit"); + break; + } + } }); threads_.emplace_back(std::move(worker)); } diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 16fd55fab..638910d2e 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -11,7 +12,8 @@ ROCKETMQ_NAMESPACE_BEGIN class SchedulerTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + } protected: }; @@ -105,5 +107,4 @@ TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { scheduler.shutdown(); } - ROCKETMQ_NAMESPACE_END \ No newline at end of file From b59fb6801ffd24f1fb3b137fff000e5b7eda0353 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 27 Sep 2021 15:36:41 +0800 Subject: [PATCH 078/133] Make exceptions into account for Scheduler and thread-pool --- src/main/cpp/base/ThreadPoolImpl.cpp | 5 +++- src/main/cpp/scheduler/SchedulerImpl.cpp | 18 +++++++++++- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 32 +++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp index 675c40260..ab2b8f365 100644 --- a/src/main/cpp/base/ThreadPoolImpl.cpp +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -29,16 +29,19 @@ void ThreadPoolImpl::start() { } while (true) { +#ifdef __EXCEPTIONS try { +#endif std::error_code ec; context_.run(ec); if (ec) { SPDLOG_WARN("Error raised from ThreadPool: {}", ec.message()); } +#ifdef __EXCEPTIONS } catch (std::exception& e) { SPDLOG_WARN("Exception raised from ThreadPool: {}", e.what()); } - +#endif if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_INFO("A thread-pool worker quit"); break; diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index e8de4b2b5..d92622800 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -41,15 +41,19 @@ void SchedulerImpl::start() { } while (true) { +#ifdef __EXCEPTIONS try { +#endif std::error_code ec; context_.run(ec); if (ec) { SPDLOG_WARN("Error raised from thread-pool: {}", ec.message()); } +#ifdef __EXCEPTIONS } catch (std::exception& e) { SPDLOG_WARN("Exception raised from thread-pool: {}", e.what()); } +#endif if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_INFO("A scheduler worker quit"); @@ -133,7 +137,19 @@ void SchedulerImpl::execute(const asio::error_code& ec, asio::steady_timer* time SPDLOG_INFO("Execute task: {}. Use-count: {}", timer_task->task_name, timer_task.use_count()); // Execute the actual callback. - timer_task->callback(); +#ifdef __EXCEPTIONS + try { +#endif + timer_task->callback(); +#ifdef __EXCEPTIONS + } catch (std::exception& e) { + SPDLOG_WARN("Exception raised: {}", e.what()); + } catch (std::string& e) { + SPDLOG_WARN("Exception raised: {}", e); + } catch (...) { + SPDLOG_WARN("Unknown exception type raised"); + } +#endif if (timer_task->interval.count()) { timer->expires_at(timer->expiry() + timer_task->interval); diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 638910d2e..827f0140d 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -107,4 +107,36 @@ TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { scheduler.shutdown(); } + +TEST_F(SchedulerTest, testException) { + SchedulerImpl scheduler; + scheduler.start(); + absl::Mutex mtx; + absl::CondVar cv; + int callback_fire_count{0}; + auto callback = [&]() { + { + absl::MutexLock lock(&mtx); + cv.Signal(); + callback_fire_count++; + } + + std::exception e; + throw e; + }; + + scheduler.schedule(callback, "test-exception", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); + + // Wait till callback is executed. + { + absl::MutexLock lock(&mtx); + if (callback_fire_count <= 5) { + cv.Wait(&mtx); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(3)); + + scheduler.shutdown(); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file From 0e355296a40ae26f4e5a4ca9cc3bec01fcc692f1 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 28 Sep 2021 20:50:11 +0800 Subject: [PATCH 079/133] Fix order consumer (#24) Fix order consumer --- api/rocketmq/AdminServer.h | 22 +- api/rocketmq/AsyncCallback.h | 8 +- api/rocketmq/ConsumeType.h | 16 +- api/rocketmq/Credentials.h | 37 +- api/rocketmq/CredentialsProvider.h | 4 +- api/rocketmq/ErrorCategory.h | 4 +- api/rocketmq/ErrorCode.h | 3 +- api/rocketmq/ExpressionType.h | 6 +- api/rocketmq/Logger.h | 21 +- api/rocketmq/MQClientException.h | 19 +- api/rocketmq/MQMessage.h | 58 ++-- api/rocketmq/MQMessageQueue.h | 58 ++-- api/rocketmq/MessageListener.h | 20 +- api/rocketmq/MessageModel.h | 3 +- api/rocketmq/MessageType.h | 3 +- api/rocketmq/PullResult.h | 19 +- api/rocketmq/SendResult.h | 68 ++-- api/rocketmq/State.h | 9 +- api/rocketmq/Transaction.h | 3 +- example/rocketmq/BUILD.bazel | 10 - example/rocketmq/BenchmarkPushConsumer.cpp | 5 +- example/rocketmq/ExampleAsyncProducer.cpp | 15 +- .../rocketmq/ExampleBroadcastPushConsumer.cpp | 9 +- example/rocketmq/ExampleProducer.cpp | 2 +- example/rocketmq/ExamplePushConsumer.cpp | 3 +- .../PushConsumerWithCustomExecutor.cpp | 27 +- example/rocketmq/PushConsumerWithThrottle.cpp | 3 +- example/rocketmq/SqlConsumer.cpp | 2 +- example/rocketmq/SqlProducer.cpp | 18 +- src/main/cpp/admin/AdminServiceImpl.cpp | 36 +- src/main/cpp/admin/include/AdminClient.h | 3 +- src/main/cpp/admin/include/AdminServerImpl.h | 7 +- src/main/cpp/admin/include/ServerCall.h | 45 +-- src/main/cpp/base/ErrorCategory.cpp | 116 +++---- src/main/cpp/base/HostInfo.cpp | 4 +- src/main/cpp/base/MQMessage.cpp | 93 +++-- src/main/cpp/base/MQMessageExt.cpp | 34 +- src/main/cpp/base/TopAddressing.cpp | 7 +- src/main/cpp/base/UtilAll.cpp | 6 +- src/main/cpp/base/include/DigestType.h | 3 +- src/main/cpp/base/include/Encoding.h | 3 +- src/main/cpp/base/include/Histogram.h | 4 +- src/main/cpp/base/include/HttpClient.h | 6 +- src/main/cpp/base/include/InvocationContext.h | 3 +- src/main/cpp/base/include/MixAll.h | 6 +- src/main/cpp/base/include/RateLimiter.h | 7 +- src/main/cpp/base/include/TopAddressing.h | 2 +- src/main/cpp/client/ClientConfigImpl.cpp | 39 ++- src/main/cpp/client/ClientManagerFactory.cpp | 8 +- src/main/cpp/client/ClientManagerImpl.cpp | 321 ++++++++---------- src/main/cpp/client/RpcClientImpl.cpp | 12 +- src/main/cpp/client/TopicAssignmentInfo.cpp | 24 +- src/main/cpp/client/include/Assignment.h | 11 +- src/main/cpp/client/include/Broker.h | 27 +- .../cpp/client/include/ClientConfigImpl.h | 51 ++- src/main/cpp/client/include/ClientManager.h | 5 +- .../cpp/client/include/ClientManagerImpl.h | 8 +- .../cpp/client/include/ConsumeMessageType.h | 3 +- .../client/include/InterceptorContinuation.h | 7 +- src/main/cpp/client/include/LogInterceptor.h | 3 +- .../cpp/client/include/OpenSSLCompatible.h | 16 +- src/main/cpp/client/include/Partition.h | 27 +- .../client/include/ReceiveMessageCallback.h | 7 +- .../cpp/client/include/ReceiveMessageResult.h | 117 +------ src/main/cpp/client/include/RpcClientImpl.h | 3 +- .../cpp/client/include/SendMessageContext.h | 48 ++- src/main/cpp/client/include/ServiceAddress.h | 44 ++- src/main/cpp/client/include/TlsHelper.h | 4 +- src/main/cpp/client/include/Topic.h | 11 +- .../cpp/client/include/TopicAssignmentInfo.h | 20 +- src/main/cpp/client/include/TopicRouteData.h | 16 +- .../client/mocks/include/ClientManagerMock.h | 6 +- .../include/ReceiveMessageCallbackMock.h | 4 +- .../cpp/client/mocks/include/RpcClientMock.h | 4 +- .../cpp/concurrent/include/CountdownLatch.h | 6 +- src/main/cpp/log/include/LoggerImpl.h | 41 ++- .../rocketmq/AsyncReceiveMessageCallback.cpp | 87 ++--- src/main/cpp/rocketmq/ClientImpl.cpp | 161 ++++----- .../rocketmq/ConsumeFifoMessageService.cpp | 8 +- ...vice.cpp => ConsumeMessageServiceBase.cpp} | 23 +- .../ConsumeStandardMessageService.cpp | 8 +- src/main/cpp/rocketmq/CredentialsProvider.cpp | 28 +- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 55 ++- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 15 +- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 59 +++- .../rocketmq/DynamicNameServerResolver.cpp | 4 +- src/main/cpp/rocketmq/FilterExpression.cpp | 20 +- .../rocketmq/MessageGroupQueueSelector.cpp | 5 +- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 80 ++--- src/main/cpp/rocketmq/ProducerImpl.cpp | 89 +++-- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 98 ++---- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 114 ++++--- .../cpp/rocketmq/StaticNameServerResolver.cpp | 7 +- src/main/cpp/rocketmq/TransactionImpl.cpp | 8 +- .../include/AsyncReceiveMessageCallback.h | 16 +- .../cpp/rocketmq/include/AwaitPullCallback.h | 15 +- src/main/cpp/rocketmq/include/ClientImpl.h | 15 +- .../rocketmq/include/ClientResourceBundle.h | 5 +- .../include/ConsumeFifoMessageService.h | 34 ++ .../rocketmq/include/ConsumeMessageService.h | 112 +----- .../include/ConsumeMessageServiceBase.h | 84 +++++ .../include/ConsumeStandardMessageService.h | 26 ++ src/main/cpp/rocketmq/include/Consumer.h | 7 +- .../cpp/rocketmq/include/FilterExpression.h | 2 +- .../cpp/rocketmq/include/ProcessQueueImpl.h | 25 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 28 +- .../cpp/rocketmq/include/PullConsumerImpl.h | 7 +- src/main/cpp/rocketmq/include/PushConsumer.h | 3 - .../cpp/rocketmq/include/PushConsumerImpl.h | 52 +-- .../rocketmq/include/ReceiveMessageAction.h | 25 -- src/main/cpp/rocketmq/include/SendCallbacks.h | 27 +- .../include/StaticNameServerResolver.h | 8 +- .../include/StsCredentialsProviderImpl.h | 4 +- .../cpp/rocketmq/include/TransactionImpl.h | 3 +- .../mocks/include/ConsumeMessageServiceMock.h | 26 ++ .../cpp/rocketmq/mocks/include/ConsumerMock.h | 2 +- .../rocketmq/mocks/include/PushConsumerMock.h | 5 +- .../cpp/tracing/exporters/OtlpExporter.cpp | 106 +++--- .../tracing/exporters/include/OtlpExporter.h | 29 +- src/test/cpp/it/RpcClientTest.cpp | 7 +- src/test/cpp/it/TopAddressingTest.cpp | 4 +- src/test/cpp/it/TopicPublishInfoTest.cpp | 50 +-- src/test/cpp/ut/base/HistogramTest.cpp | 3 +- src/test/cpp/ut/base/HostInfoTest.cpp | 16 +- src/test/cpp/ut/base/HttpClientTest.cpp | 3 +- .../cpp/ut/base/InvocationContextTest.cpp | 2 +- src/test/cpp/ut/base/MQMessageExtTest.cpp | 13 +- src/test/cpp/ut/base/MQMessageTest.cpp | 3 +- src/test/cpp/ut/base/RateLimiterTest.cpp | 4 +- src/test/cpp/ut/base/ThreadPoolTest.cpp | 4 +- src/test/cpp/ut/base/TopAddressingTest.cpp | 4 +- .../cpp/ut/base/UniqueIdGeneratorTest.cpp | 2 +- src/test/cpp/ut/base/UtilAllTest.cpp | 6 +- .../ut/client/ClientManagerFactoryTest.cpp | 10 +- src/test/cpp/ut/client/ClientManagerTest.cpp | 27 +- src/test/cpp/ut/client/ClientTest.cpp | 7 +- src/test/cpp/ut/client/RpcClientTest.cpp | 4 +- src/test/cpp/ut/client/TracingUtilityTest.cpp | 3 +- src/test/cpp/ut/grpc/GrpcTimer.cpp | 11 +- src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 12 +- .../ConsumeStandardMessageServiceTest.cpp | 9 +- src/test/cpp/ut/rocketmq/ConsumerTest.cpp | 4 +- .../ut/rocketmq/CredentialsProviderTest.cpp | 5 +- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 3 +- .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 7 +- .../DynamicNameServerResolverTest.cpp | 7 +- src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 37 +- .../cpp/ut/rocketmq/FilterExpressionTest.cpp | 9 +- .../MessageGroupQueueSelectorTest.cpp | 2 +- src/test/cpp/ut/rocketmq/MessageTest.cpp | 2 +- src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 52 +-- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 11 +- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 51 ++- .../rocketmq/StaticNameServerResolverTest.cpp | 11 +- .../StsCredentialsProviderImplTest.cpp | 4 +- .../tracing/exporters/OtlpExportersTest.cpp | 7 +- .../ut/tracing/exporters/SpanContextTest.cpp | 1 - tools/format.sh | 6 + 158 files changed, 2056 insertions(+), 1655 deletions(-) rename src/main/cpp/rocketmq/{ConsumeMessageService.cpp => ConsumeMessageServiceBase.cpp} (75%) create mode 100644 src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h create mode 100644 src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h create mode 100644 src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h delete mode 100644 src/main/cpp/rocketmq/include/ReceiveMessageAction.h create mode 100644 src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h create mode 100644 tools/format.sh diff --git a/api/rocketmq/AdminServer.h b/api/rocketmq/AdminServer.h index 70d97c4f2..afd5dee23 100644 --- a/api/rocketmq/AdminServer.h +++ b/api/rocketmq/AdminServer.h @@ -5,21 +5,21 @@ namespace rocketmq { namespace admin { -class AdminServer { -public: - virtual ~AdminServer() = default; + class AdminServer { + public: + virtual ~AdminServer() = default; - virtual bool start() = 0; + virtual bool start() = 0; - virtual bool stop() = 0; + virtual bool stop() = 0; - virtual int port() const = 0; -}; + virtual int port() const = 0; + }; -class AdminFacade { -public: - static AdminServer& getServer(); -}; + class AdminFacade { + public: + static AdminServer& getServer(); + }; } // namespace admin } // namespace rocketmq \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index f7d5ae72d..782b90c77 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -17,18 +17,18 @@ class SendCallback : public AsyncCallback { public: ~SendCallback() override = default; - virtual void onSuccess(SendResult &send_result) noexcept = 0; + virtual void onSuccess(SendResult& send_result) noexcept = 0; - virtual void onFailure(const std::error_code &ec) noexcept = 0; + virtual void onFailure(const std::error_code& ec) noexcept = 0; }; class PullCallback : public AsyncCallback { public: ~PullCallback() override = default; - virtual void onSuccess(const PullResult &pull_result) noexcept = 0; + virtual void onSuccess(const PullResult& pull_result) noexcept = 0; - virtual void onFailure(const std::error_code &ec) noexcept = 0; + virtual void onFailure(const std::error_code& ec) noexcept = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ConsumeType.h b/api/rocketmq/ConsumeType.h index fb8514865..d0337dc51 100644 --- a/api/rocketmq/ConsumeType.h +++ b/api/rocketmq/ConsumeType.h @@ -1,19 +1,21 @@ #pragma once -#include -#include #include +#include +#include #include "MQMessageQueue.h" ROCKETMQ_NAMESPACE_BEGIN -enum ConsumeType { +enum ConsumeType +{ CONSUME_ACTIVELY, CONSUME_PASSIVELY, }; -enum ConsumeFromWhere { +enum ConsumeFromWhere +{ CONSUME_FROM_LAST_OFFSET, CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST, CONSUME_FROM_MIN_OFFSET, @@ -22,12 +24,14 @@ enum ConsumeFromWhere { CONSUME_FROM_TIMESTAMP, }; -enum ConsumeInitialMode { +enum ConsumeInitialMode +{ MIN, MAX, }; -enum QueryOffsetPolicy : uint8_t { +enum QueryOffsetPolicy : uint8_t +{ BEGINNING = 0, END = 1, TIME_POINT = 2, diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index 33374f637..aba5fc731 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -10,35 +10,52 @@ ROCKETMQ_NAMESPACE_BEGIN class Credentials { public: - Credentials() : expiration_instant_(std::chrono::system_clock::time_point::max()) {} + Credentials() : expiration_instant_(std::chrono::system_clock::time_point::max()) { + } Credentials(std::string access_key, std::string access_secret) : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), - expiration_instant_(std::chrono::system_clock::time_point::max()) {} + expiration_instant_(std::chrono::system_clock::time_point::max()) { + } Credentials(std::string access_key, std::string access_secret, std::string session_token, std::chrono::system_clock::time_point expiration) : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)), - session_token_(std::move(session_token)), expiration_instant_(expiration) {} + session_token_(std::move(session_token)), expiration_instant_(expiration) { + } bool operator==(const Credentials& rhs) const { return access_key_ == rhs.access_key_ && access_secret_ == rhs.access_secret_ && session_token_ == rhs.session_token_ && expiration_instant_ == rhs.expiration_instant_; } - bool operator!=(const Credentials& rhs) const { return !(*this == rhs); } + bool operator!=(const Credentials& rhs) const { + return !(*this == rhs); + } - bool empty() const { return access_key_.empty() || access_secret_.empty(); } + bool empty() const { + return access_key_.empty() || access_secret_.empty(); + } - bool expired() const { return std::chrono::system_clock::now() > expiration_instant_; } + bool expired() const { + return std::chrono::system_clock::now() > expiration_instant_; + } - const std::string& accessKey() const { return access_key_; } + const std::string& accessKey() const { + return access_key_; + } - const std::string& accessSecret() const { return access_secret_; } + const std::string& accessSecret() const { + return access_secret_; + } - const std::string& sessionToken() const { return session_token_; } + const std::string& sessionToken() const { + return session_token_; + } - std::chrono::system_clock::time_point expirationInstant() const { return expiration_instant_; } + std::chrono::system_clock::time_point expirationInstant() const { + return expiration_instant_; + } private: std::string access_key_; diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index 93ba8a2a1..0a61126d2 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -66,7 +66,9 @@ class ConfigFileCredentialsProvider : public CredentialsProvider { * For test purpose only. * @return */ - static const char* credentialFile() { return CREDENTIAL_FILE_; } + static const char* credentialFile() { + return CREDENTIAL_FILE_; + } private: std::chrono::system_clock::duration refresh_interval_{std::chrono::seconds(10)}; diff --git a/api/rocketmq/ErrorCategory.h b/api/rocketmq/ErrorCategory.h index 316db57e4..fe928163c 100644 --- a/api/rocketmq/ErrorCategory.h +++ b/api/rocketmq/ErrorCategory.h @@ -11,7 +11,9 @@ class ErrorCategory : public std::error_category { return instance; } - const char* name() const noexcept override { return "RocketMQ"; } + const char* name() const noexcept override { + return "RocketMQ"; + } std::string message(int code) const override; diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index 0e09dd674..af3078052 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -8,7 +8,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class ErrorCode : int { +enum class ErrorCode : int +{ Success = 0, /** diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h index 93855af3b..fbf99ace6 100644 --- a/api/rocketmq/ExpressionType.h +++ b/api/rocketmq/ExpressionType.h @@ -6,6 +6,10 @@ ROCKETMQ_NAMESPACE_BEGIN -enum ExpressionType : int8_t { TAG = 0, SQL92 = 1 }; +enum ExpressionType : int8_t +{ + TAG = 0, + SQL92 = 1 +}; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h index dec8778f3..586619927 100644 --- a/api/rocketmq/Logger.h +++ b/api/rocketmq/Logger.h @@ -1,11 +1,11 @@ #pragma once #include +#include #include -#include #include #include -#include +#include #include "RocketMQ.h" @@ -15,7 +15,14 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class Level : uint8_t { Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4 }; +enum class Level : uint8_t +{ + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4 +}; class Logger { public: @@ -23,15 +30,15 @@ class Logger { /** * @brief Set log level for file sink. Its default log level is: Info. - * - * @param level + * + * @param level */ virtual void setLevel(Level level) = 0; /** * @brief Set log level for stdout, aka, console. Its default log level is: Warn. - * - * @param level + * + * @param level */ virtual void setConsoleLevel(Level level) = 0; diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h index 3b1385438..9e77bf7a2 100644 --- a/api/rocketmq/MQClientException.h +++ b/api/rocketmq/MQClientException.h @@ -33,11 +33,17 @@ class MQException : public std::exception { ~MQException() noexcept override = default; - const char* what() const noexcept override { return m_msg.c_str(); } + const char* what() const noexcept override { + return m_msg.c_str(); + } - int GetError() const noexcept { return m_error; } + int GetError() const noexcept { + return m_error; + } - virtual const char* GetType() const noexcept { return m_type.c_str(); } + virtual const char* GetType() const noexcept { + return m_type.c_str(); + } protected: int m_error; @@ -56,8 +62,11 @@ inline std::ostream& operator<<(std::ostream& os, const MQException& e) { class name : public MQException { \ public: \ name(const std::string& msg, int error, const char* file, int line) throw() \ - : MQException(msg, error, file, #name, line) {} \ - virtual const char* GetType() const throw() { return m_type.c_str(); } \ + : MQException(msg, error, file, #name, line) { \ + } \ + virtual const char* GetType() const throw() { \ + return m_type.c_str(); \ + } \ }; DEFINE_MQ_CLIENT_EXCEPTION(MQClientException) diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 30dac5913..46c479cd6 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -21,30 +21,28 @@ class MessageAccessor; class MQMessage { public: MQMessage(); - MQMessage(const std::string &topic, const std::string &body); - MQMessage(const std::string &topic, const std::string &tags, - const std::string &body); - MQMessage(const std::string &topic, const std::string &tags, - const std::string &keys, const std::string &body); + MQMessage(const std::string& topic, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& body); + MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); virtual ~MQMessage(); - MQMessage(const MQMessage &other); - MQMessage &operator=(const MQMessage &other); + MQMessage(const MQMessage& other); + MQMessage& operator=(const MQMessage& other); - const std::string &getMsgId() const; + const std::string& getMsgId() const; - void setProperty(const std::string &name, const std::string &value); - std::string getProperty(const std::string &name) const; + void setProperty(const std::string& name, const std::string& value); + std::string getProperty(const std::string& name) const; - const std::string &getTopic() const; - void setTopic(const std::string &topic); - void setTopic(const char *data, int len); + const std::string& getTopic() const; + void setTopic(const std::string& topic); + void setTopic(const char* data, int len); std::string getTags() const; - void setTags(const std::string &tags); + void setTags(const std::string& tags); - const std::vector &getKeys() const; + const std::vector& getKeys() const; /** * @brief Add a unique key for the message @@ -52,27 +50,27 @@ class MQMessage { * attach the given key to the message. Better rename it. * @param key Unique key in perspective of bussiness logic. */ - void setKey(const std::string &key); - void setKeys(const std::vector &keys); + void setKey(const std::string& key); + void setKeys(const std::vector& keys); int getDelayTimeLevel() const; void setDelayTimeLevel(int level); - const std::string &traceContext() const; - void traceContext(const std::string &trace_context); + const std::string& traceContext() const; + void traceContext(const std::string& trace_context); std::string getBornHost() const; std::chrono::system_clock::time_point deliveryTimestamp() const; - const std::string &getBody() const; - void setBody(const char *data, int len); - void setBody(const std::string &body); + const std::string& getBody() const; + void setBody(const char* data, int len); + void setBody(const std::string& body); uint32_t bodyLength() const; - const std::map &getProperties() const; - void setProperties(const std::map &properties); + const std::map& getProperties() const; + void setProperties(const std::map& properties); void messageType(MessageType message_type); MessageType messageType() const; @@ -81,16 +79,20 @@ class MQMessage { message_group_ = std::string(message_group.data(), message_group.length()); } - void bindMessageQueue(const MQMessageQueue &message_queue) { + void bindMessageQueue(const MQMessageQueue& message_queue) { message_queue_ = message_queue; } - const std::string &messageGroup() const { return message_group_; } + const std::string& messageGroup() const { + return message_group_; + } - const MQMessageQueue &messageQueue() const { return message_queue_; } + const MQMessageQueue& messageQueue() const { + return message_queue_; + } protected: - MessageImpl *impl_; + MessageImpl* impl_; friend class MessageAccessor; diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h index 4beb3476e..6e84fa858 100644 --- a/api/rocketmq/MQMessageQueue.h +++ b/api/rocketmq/MQMessageQueue.h @@ -13,19 +13,21 @@ class MQMessageQueue { MQMessageQueue() = default; MQMessageQueue(std::string topic, std::string broker_name, int queue_id) - : topic_(std::move(topic)), broker_name_(std::move(broker_name)), - queue_id_(queue_id) {} + : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) { + } - MQMessageQueue(const MQMessageQueue &other) { this->operator=(other); } + MQMessageQueue(const MQMessageQueue& other) { + this->operator=(other); + } - MQMessageQueue(MQMessageQueue &&other) noexcept { + MQMessageQueue(MQMessageQueue&& other) noexcept { topic_ = std::move(other.topic_); broker_name_ = std::move(other.broker_name_); queue_id_ = other.queue_id_; service_address_ = other.service_address_; } - MQMessageQueue &operator=(const MQMessageQueue &other) { + MQMessageQueue& operator=(const MQMessageQueue& other) { if (this == &other) { return *this; } @@ -37,31 +39,39 @@ class MQMessageQueue { return *this; } - const std::string &getTopic() const { return topic_; } + const std::string& getTopic() const { + return topic_; + } - void setTopic(const std::string &topic) { topic_ = topic; } + void setTopic(const std::string& topic) { + topic_ = topic; + } - const std::string &getBrokerName() const { return broker_name_; } + const std::string& getBrokerName() const { + return broker_name_; + } - void setBrokerName(const std::string &broker_name) { + void setBrokerName(const std::string& broker_name) { broker_name_ = broker_name; } - int getQueueId() const { return queue_id_; } + int getQueueId() const { + return queue_id_; + } - void setQueueId(int queue_id) { queue_id_ = queue_id; } + void setQueueId(int queue_id) { + queue_id_ = queue_id; + } - bool operator!=(const MQMessageQueue &rhs) const { - return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || - queue_id_ != rhs.queue_id_; + bool operator!=(const MQMessageQueue& rhs) const { + return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || queue_id_ != rhs.queue_id_; } - bool operator==(const MQMessageQueue &mq) const { - return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && - queue_id_ == mq.queue_id_; + bool operator==(const MQMessageQueue& mq) const { + return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && queue_id_ == mq.queue_id_; } - bool operator<(const MQMessageQueue &mq) const { + bool operator<(const MQMessageQueue& mq) const { if (topic_ != mq.topic_) { return topic_ < mq.topic_; } @@ -77,7 +87,7 @@ class MQMessageQueue { return !topic_.empty() && !broker_name_.empty() && queue_id_ >= 0; } - int compareTo(const MQMessageQueue &mq) const { + int compareTo(const MQMessageQueue& mq) const { if (this == &mq) { return 0; } @@ -99,16 +109,18 @@ class MQMessageQueue { std::string toString() const { std::stringstream ss; - ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ - << ", queueId=" << queue_id_ << "]"; + ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ << ", queueId=" << queue_id_ << "]"; return ss.str(); } - template friend H AbslHashValue(H h, const MQMessageQueue &mq) { + template + friend H AbslHashValue(H h, const MQMessageQueue& mq) { return H::combine(std::move(h), mq.topic_, mq.broker_name_, mq.queue_id_); } - const std::string &serviceAddress() const { return service_address_; } + const std::string& serviceAddress() const { + return service_address_; + } void serviceAddress(std::string service_address) { service_address_ = std::move(service_address); diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h index d8c388c54..fbe6a1945 100644 --- a/api/rocketmq/MessageListener.h +++ b/api/rocketmq/MessageListener.h @@ -5,9 +5,17 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class ConsumeMessageResult : uint8_t { SUCCESS = 0, FAILURE = 1 }; +enum class ConsumeMessageResult : uint8_t +{ + SUCCESS = 0, + FAILURE = 1 +}; -enum class MessageListenerType : uint8_t { STANDARD = 0, FIFO = 1 }; +enum class MessageListenerType : uint8_t +{ + STANDARD = 0, + FIFO = 1 +}; class MessageListener { public: @@ -20,12 +28,16 @@ class StandardMessageListener : public MessageListener { public: virtual ConsumeMessageResult consumeMessage(const std::vector& msgs) = 0; - MessageListenerType listenerType() override { return MessageListenerType::STANDARD; } + MessageListenerType listenerType() override { + return MessageListenerType::STANDARD; + } }; class FifoMessageListener : public MessageListener { public: - MessageListenerType listenerType() override { return MessageListenerType::FIFO; } + MessageListenerType listenerType() override { + return MessageListenerType::FIFO; + } virtual ConsumeMessageResult consumeMessage(const MQMessageExt& msgs) = 0; }; diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h index 12fb37f70..7227f5228 100644 --- a/api/rocketmq/MessageModel.h +++ b/api/rocketmq/MessageModel.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class MessageModel : int8_t { +enum class MessageModel : int8_t +{ BROADCASTING, CLUSTERING, }; diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/MessageType.h index 01d01721c..3e04f3e11 100644 --- a/api/rocketmq/MessageType.h +++ b/api/rocketmq/MessageType.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class MessageType : int8_t { +enum class MessageType : int8_t +{ NORMAL = 0, FIFO = 1, DELAY = 2, diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h index acd611eaf..57e9890c1 100644 --- a/api/rocketmq/PullResult.h +++ b/api/rocketmq/PullResult.h @@ -10,15 +10,24 @@ ROCKETMQ_NAMESPACE_BEGIN class PullResult { public: PullResult(int64_t min, int64_t max, int64_t next, std::vector messages) - : min_(min), max_(max), next_(next), messages_(std::move(messages)) {} + : min_(min), max_(max), next_(next), messages_(std::move(messages)) { + } - int64_t min() const { return min_; } + int64_t min() const { + return min_; + } - int64_t max() const { return max_; } + int64_t max() const { + return max_; + } - int64_t next() const { return next_; } + int64_t next() const { + return next_; + } - const std::vector& messages() const { return messages_; } + const std::vector& messages() const { + return messages_; + } private: int64_t min_; diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 554827c1a..7765141b5 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class SendStatus : int8_t { +enum class SendStatus : int8_t +{ SEND_OK, // Deprecated. Remove in the next release. @@ -23,16 +24,17 @@ class SendResult { public: SendResult() = default; - SendResult(const SendStatus &send_status, std::string msg_id, - MQMessageQueue message_queue, long long queue_offset, + SendResult(const SendStatus& send_status, std::string msg_id, MQMessageQueue message_queue, long long queue_offset, std::string region_id) - : send_status_(send_status), message_id_(std::move(msg_id)), - message_queue_(std::move(message_queue)), queue_offset_(queue_offset), - region_id_(std::move(region_id)) {} + : send_status_(send_status), message_id_(std::move(msg_id)), message_queue_(std::move(message_queue)), + queue_offset_(queue_offset), region_id_(std::move(region_id)) { + } - SendResult(const SendResult &other) { this->operator=(other); } + SendResult(const SendResult& other) { + this->operator=(other); + } - SendResult &operator=(const SendResult &other) { + SendResult& operator=(const SendResult& other) { if (this == &other) { return *this; } @@ -46,43 +48,65 @@ class SendResult { return *this; } - const std::string &getMsgId() const { return message_id_; } + const std::string& getMsgId() const { + return message_id_; + } - void setMsgId(const std::string &msg_id) { message_id_ = msg_id; } + void setMsgId(const std::string& msg_id) { + message_id_ = msg_id; + } - const std::string &getRegionId() const { return region_id_; } + const std::string& getRegionId() const { + return region_id_; + } - void setRegionId(const std::string ®ion_id) { region_id_ = region_id; } + void setRegionId(const std::string& region_id) { + region_id_ = region_id; + } - SendStatus getSendStatus() const { return send_status_; } + SendStatus getSendStatus() const { + return send_status_; + } - void setSendStatus(const SendStatus &send_status) { + void setSendStatus(const SendStatus& send_status) { send_status_ = send_status; } - MQMessageQueue &getMessageQueue() const { return message_queue_; } + MQMessageQueue& getMessageQueue() const { + return message_queue_; + } - void setMessageQueue(const MQMessageQueue &message_queue) { + void setMessageQueue(const MQMessageQueue& message_queue) { message_queue_ = message_queue; } - long long getQueueOffset() const { return queue_offset_; } + long long getQueueOffset() const { + return queue_offset_; + } - void setQueueOffset(long long queue_offset) { queue_offset_ = queue_offset; } + void setQueueOffset(long long queue_offset) { + queue_offset_ = queue_offset; + } - const std::string &getTransactionId() const { return transaction_id_; } + const std::string& getTransactionId() const { + return transaction_id_; + } - void setTransactionId(const std::string &transaction_id) { + void setTransactionId(const std::string& transaction_id) { transaction_id_ = transaction_id; } - const std::string &traceContext() const { return trace_context_; } + const std::string& traceContext() const { + return trace_context_; + } void traceContext(std::string trace_context) { trace_context_ = std::move(trace_context); } - operator bool() { return !message_id_.empty(); } + operator bool() { + return !message_id_.empty(); + } private: SendStatus send_status_{SendStatus::SEND_OK}; diff --git a/api/rocketmq/State.h b/api/rocketmq/State.h index db603cc7e..ec3c0a67b 100644 --- a/api/rocketmq/State.h +++ b/api/rocketmq/State.h @@ -6,6 +6,13 @@ ROCKETMQ_NAMESPACE_BEGIN -enum State : uint8_t { CREATED = 0, STARTING = 1, STARTED = 2, STOPPING = 3, STOPPED = 4 }; +enum State : uint8_t +{ + CREATED = 0, + STARTING = 1, + STARTED = 2, + STOPPING = 3, + STOPPED = 4 +}; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index b519b2782..352fdaded 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -25,7 +25,8 @@ class Transaction { using TransactionPtr = std::unique_ptr; -enum class TransactionState : int8_t { +enum class TransactionState : int8_t +{ COMMIT = 0, ROLLBACK = 1, }; diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel index a0655f86a..f0853ba06 100644 --- a/example/rocketmq/BUILD.bazel +++ b/example/rocketmq/BUILD.bazel @@ -6,7 +6,6 @@ cc_binary( "ExampleProducer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -17,7 +16,6 @@ cc_binary( "ExampleAsyncProducer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -28,7 +26,6 @@ cc_binary( "SqlProducer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -39,7 +36,6 @@ cc_binary( "ExamplePushConsumer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -50,7 +46,6 @@ cc_binary( "PushConsumerWithCustomExecutor.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -61,7 +56,6 @@ cc_binary( "PushConsumerWithThrottle.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -72,7 +66,6 @@ cc_binary( "SqlConsumer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -83,7 +76,6 @@ cc_binary( "ExamplePullConsumer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -94,7 +86,6 @@ cc_binary( "BenchmarkPushConsumer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) @@ -105,7 +96,6 @@ cc_binary( "ExampleBroadcastPushConsumer.cpp", ], deps = [ - "//api:rocketmq_interface", "//src/main/cpp/rocketmq:rocketmq_library", ], ) diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp index fae094268..3585b4e4f 100644 --- a/example/rocketmq/BenchmarkPushConsumer.cpp +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -2,15 +2,16 @@ #include #include +#include #include #include -#include using namespace rocketmq; class CounterMessageListener : public StandardMessageListener { public: - explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { + } ConsumeMessageResult consumeMessage(const std::vector& msgs) override { counter_.fetch_add(msgs.size()); diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 78e23e5f6..8cc77db4b 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -77,7 +77,9 @@ class RateLimiter { } } - std::array& partition() { return permits_; } + std::array& partition() { + return permits_; + } int slot() { auto current = std::chrono::steady_clock::now(); @@ -138,11 +140,16 @@ class RateLimiter { class SampleSendCallback : public rocketmq::SendCallback { public: - SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) {} + SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) { + } - void onSuccess(SendResult& send_result) noexcept override { counter_.fetch_add(1, std::memory_order_relaxed); } + void onSuccess(SendResult& send_result) noexcept override { + counter_.fetch_add(1, std::memory_order_relaxed); + } - void onFailure(const std::error_code& ec) noexcept override { error_.fetch_add(1, std::memory_order_relaxed); } + void onFailure(const std::error_code& ec) noexcept override { + error_.fetch_add(1, std::memory_order_relaxed); + } private: std::atomic_int& counter_; diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 807988258..2d59b0e7c 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -29,18 +29,17 @@ int main(int argc, char* argv[]) { logger.setLevel(Level::Debug); logger.init(); - const char* cid = "GID_group003"; - const char *topic = "yc001"; - const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; + const char* cid = "GID_cpp_sdk_standard"; + const char* topic = "cpp_sdk_standard"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; DefaultMQPushConsumer push_consumer(cid); push_consumer.setMessageModel(MessageModel::BROADCASTING); push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr("11.165.223.199:9876"); + push_consumer.setNamesrvAddr("121.43.42.193:80"); MessageListener* listener = new SampleMQMessageListener; push_consumer.setGroupName(cid); - push_consumer.setInstanceName("instance_0"); push_consumer.subscribe(topic, "*"); push_consumer.registerMessageListener(listener); push_consumer.start(); diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index b7fed59a4..a65fc44ad 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -35,7 +35,7 @@ int main(int argc, char* argv[]) { DefaultMQProducer producer("TestGroup"); const char* topic = "cpp_sdk_standard"; - const char* name_server = "47.98.116.189:80"; + const char* name_server = "121.43.42.193:80"; producer.setNamesrvAddr(name_server); producer.compressBodyThreshold(256); diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index a319345cc..697954b5e 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -36,10 +36,9 @@ int main(int argc, char* argv[]) { DefaultMQPushConsumer push_consumer(group_id); push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr("47.98.116.189:80"); + push_consumer.setNamesrvAddr("121.43.42.193:80"); MessageListener* listener = new SampleMQMessageListener; push_consumer.setInstanceName("instance_0"); - // push_consumer.setGroupName(group_id); push_consumer.subscribe(topic, "*"); push_consumer.registerMessageListener(listener); push_consumer.setConsumeThreadCount(4); diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp index 60244d551..000d889af 100644 --- a/example/rocketmq/PushConsumerWithCustomExecutor.cpp +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -11,22 +11,23 @@ ROCKETMQ_NAMESPACE_BEGIN class ExecutorImpl { public: - ExecutorImpl() : state_(State::CREATED) {} + ExecutorImpl() : state_(State::CREATED) { + } virtual ~ExecutorImpl() { switch (state_.load(std::memory_order_relaxed)) { - case CREATED: - case STOPPING: - case STOPPED: - break; - - case STARTING: - case STARTED: - state_.store(State::STOPPED); - if (worker_.joinable()) { - worker_.join(); - } - break; + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + if (worker_.joinable()) { + worker_.join(); + } + break; } } diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp index af8926580..e8969cff0 100644 --- a/example/rocketmq/PushConsumerWithThrottle.cpp +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -10,7 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN class CounterMessageListener : public StandardMessageListener { public: - explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) {} + explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { + } ConsumeMessageResult consumeMessage(const std::vector& msgs) override { counter_.fetch_add(msgs.size()); diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp index 4a690bdad..82b54df20 100644 --- a/example/rocketmq/SqlConsumer.cpp +++ b/example/rocketmq/SqlConsumer.cpp @@ -1,9 +1,9 @@ #include "rocketmq/DefaultMQPushConsumer.h" #include +#include #include #include -#include using namespace rocketmq; diff --git a/example/rocketmq/SqlProducer.cpp b/example/rocketmq/SqlProducer.cpp index 024b6de7b..f9bbe1cbe 100644 --- a/example/rocketmq/SqlProducer.cpp +++ b/example/rocketmq/SqlProducer.cpp @@ -21,15 +21,15 @@ int main(int argc, char* argv[]) { message.setBody(body); message.setProperty("a", std::to_string(i % 5)); switch (i % 3) { - case 0: - message.setTags("TagA"); - break; - case 1: - message.setTags("TagB"); - break; - case 2: - message.setTags("TagC"); - break; + case 0: + message.setTags("TagA"); + break; + case 1: + message.setTags("TagB"); + break; + case 2: + message.setTags("TagC"); + break; } SendResult sendResult = producer.send(message); std::cout << "Message sent with msgId=" << sendResult.getMsgId() diff --git a/src/main/cpp/admin/AdminServiceImpl.cpp b/src/main/cpp/admin/AdminServiceImpl.cpp index b1d1ce93e..283ef5385 100644 --- a/src/main/cpp/admin/AdminServiceImpl.cpp +++ b/src/main/cpp/admin/AdminServiceImpl.cpp @@ -13,24 +13,24 @@ Status applyChange(const rmq::ChangeLogLevelRequest* request) { auto level = request->level(); switch (level) { - case rmq::ChangeLogLevelRequest_Level_TRACE: - logger->set_level(spdlog::level::trace); - break; - case rmq::ChangeLogLevelRequest_Level_DEBUG: - logger->set_level(spdlog::level::debug); - break; - case rmq::ChangeLogLevelRequest_Level_INFO: - logger->set_level(spdlog::level::info); - break; - case rmq::ChangeLogLevelRequest_Level_WARN: - logger->set_level(spdlog::level::warn); - break; - case rmq::ChangeLogLevelRequest_Level_ERROR: - logger->set_level(spdlog::level::err); - break; - default: - logger->set_level(spdlog::level::info); - break; + case rmq::ChangeLogLevelRequest_Level_TRACE: + logger->set_level(spdlog::level::trace); + break; + case rmq::ChangeLogLevelRequest_Level_DEBUG: + logger->set_level(spdlog::level::debug); + break; + case rmq::ChangeLogLevelRequest_Level_INFO: + logger->set_level(spdlog::level::info); + break; + case rmq::ChangeLogLevelRequest_Level_WARN: + logger->set_level(spdlog::level::warn); + break; + case rmq::ChangeLogLevelRequest_Level_ERROR: + logger->set_level(spdlog::level::err); + break; + default: + logger->set_level(spdlog::level::info); + break; } return grpc::Status::OK; } diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h index 046b611c0..9878f3359 100644 --- a/src/main/cpp/admin/include/AdminClient.h +++ b/src/main/cpp/admin/include/AdminClient.h @@ -15,7 +15,8 @@ namespace admin { class AdminClient { public: - explicit AdminClient(std::shared_ptr& channel) : stub_(rmq::Admin::NewStub(channel)) {} + explicit AdminClient(std::shared_ptr& channel) : stub_(rmq::Admin::NewStub(channel)) { + } Status changeLogLevel(const rmq::ChangeLogLevelRequest& request, rmq::ChangeLogLevelResponse& response); diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h index b50aaf51a..ea4b941a6 100644 --- a/src/main/cpp/admin/include/AdminServerImpl.h +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -17,13 +17,16 @@ namespace admin { class AdminServerImpl : public AdminServer { public: AdminServerImpl() - : port_(0), state_(State::CREATED), async_stub_(new rmq::Admin::AsyncService), service_(new AdminServiceImpl) {} + : port_(0), state_(State::CREATED), async_stub_(new rmq::Admin::AsyncService), service_(new AdminServiceImpl) { + } bool start() override; bool stop() override; - int port() const override { return port_; } + int port() const override { + return port_; + } private: int port_; diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h index bcb3ccc13..297b6f1bf 100644 --- a/src/main/cpp/admin/include/ServerCall.h +++ b/src/main/cpp/admin/include/ServerCall.h @@ -10,7 +10,12 @@ namespace rmq = apache::rocketmq::v1; ROCKETMQ_NAMESPACE_BEGIN -enum ServerCallStatus : int8_t { CREATE = 0, PROCESS = 1, FINISH = 2 }; +enum ServerCallStatus : int8_t +{ + CREATE = 0, + PROCESS = 1, + FINISH = 2 +}; class ServerCall { public: @@ -23,27 +28,27 @@ class ServerCall { void proceed() { switch (status_) { - case CREATE: { - status_ = PROCESS; - async_stub_->RequestChangeLogLevel(&context_, &request_, &response_observer_, completion_queue_, - completion_queue_, this); - break; - } - case PROCESS: { - // Create a new ServerCall to serve the next incoming request. - new ServerCall(async_stub_, service_, completion_queue_); + case CREATE: { + status_ = PROCESS; + async_stub_->RequestChangeLogLevel(&context_, &request_, &response_observer_, completion_queue_, + completion_queue_, this); + break; + } + case PROCESS: { + // Create a new ServerCall to serve the next incoming request. + new ServerCall(async_stub_, service_, completion_queue_); - // Now that request_ is already filled with actual data from clients, invoke the actual process function - const grpc::Status rpc_status = service_->ChangeLogLevel(&context_, &request_, &response_); + // Now that request_ is already filled with actual data from clients, invoke the actual process function + const grpc::Status rpc_status = service_->ChangeLogLevel(&context_, &request_, &response_); - status_ = FINISH; - response_observer_.Finish(response_, rpc_status, this); - break; - } - default: { - assert(FINISH == status_); - delete this; - } + status_ = FINISH; + response_observer_.Finish(response_, rpc_status, this); + break; + } + default: { + assert(FINISH == status_); + delete this; + } } } diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp index 9d4072522..dec677225 100644 --- a/src/main/cpp/base/ErrorCategory.cpp +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -5,84 +5,84 @@ ROCKETMQ_NAMESPACE_BEGIN std::string ErrorCategory::message(int code) const { ErrorCode ec = static_cast(code); switch (ec) { - case ErrorCode::Success: - return "Success"; + case ErrorCode::Success: + return "Success"; - case ErrorCode::IllegalState: - return "Client state illegal. Forgot to call start()?"; + case ErrorCode::IllegalState: + return "Client state illegal. Forgot to call start()?"; - case ErrorCode::BadConfiguration: - return "Bad configuration."; + case ErrorCode::BadConfiguration: + return "Bad configuration."; - case ErrorCode::BadRequest: - return "Message is ill-formed. Check validity of your topic, tag, " - "etc"; + case ErrorCode::BadRequest: + return "Message is ill-formed. Check validity of your topic, tag, " + "etc"; - case ErrorCode::Unauthorized: - return "Authentication failed. Possibly caused by invalid credentials."; + case ErrorCode::Unauthorized: + return "Authentication failed. Possibly caused by invalid credentials."; - case ErrorCode::Forbidden: - return "Authenticated user does not have privilege to perform the " - "requested action"; + case ErrorCode::Forbidden: + return "Authenticated user does not have privilege to perform the " + "requested action"; - case ErrorCode::NotFound: - return "Topic not found, which should be created through console or " - "administration API before hand."; + case ErrorCode::NotFound: + return "Topic not found, which should be created through console or " + "administration API before hand."; - case ErrorCode::RequestTimeout: - return "Timeout when connecting, reading from or writing to brokers."; + case ErrorCode::RequestTimeout: + return "Timeout when connecting, reading from or writing to brokers."; - case ErrorCode::PayloadTooLarge: - return "Message body is too large."; + case ErrorCode::PayloadTooLarge: + return "Message body is too large."; - case ErrorCode::PreconditionRequired: - return "State of dependent procedure is not right"; + case ErrorCode::PreconditionRequired: + return "State of dependent procedure is not right"; - case ErrorCode::TooManyRequest: - return "Quota exchausted. The user has sent too many requests in a given " - "amount of time."; + case ErrorCode::TooManyRequest: + return "Quota exchausted. The user has sent too many requests in a given " + "amount of time."; - case ErrorCode::UnavailableForLegalReasons: - return "A server operator has received a legal demand to deny access to " - "a resource or to a set of resources that " - "includes the requested resource."; + case ErrorCode::UnavailableForLegalReasons: + return "A server operator has received a legal demand to deny access to " + "a resource or to a set of resources that " + "includes the requested resource."; - case ErrorCode::HeaderFieldsTooLarge: - return "The server is unwilling to process the request because either an " - "individual header field, or all the header fields collectively, " - "are too large"; + case ErrorCode::HeaderFieldsTooLarge: + return "The server is unwilling to process the request because either an " + "individual header field, or all the header fields collectively, " + "are too large"; - case ErrorCode::InternalServerError: - return "Server side interval error"; + case ErrorCode::InternalServerError: + return "Server side interval error"; - case ErrorCode::NotImplemented: - return "The server either does not recognize the request method, or it " - "lacks the ability to fulfil the request."; + case ErrorCode::NotImplemented: + return "The server either does not recognize the request method, or it " + "lacks the ability to fulfil the request."; - case ErrorCode::BadGateway: - return "The server was acting as a gateway or proxy and received an " - "invalid response from the upstream server."; + case ErrorCode::BadGateway: + return "The server was acting as a gateway or proxy and received an " + "invalid response from the upstream server."; - case ErrorCode::ServiceUnavailable: - return "The server cannot handle the request (because it is overloaded " - "or down for maintenance). Generally, this " - "is a temporary state."; + case ErrorCode::ServiceUnavailable: + return "The server cannot handle the request (because it is overloaded " + "or down for maintenance). Generally, this " + "is a temporary state."; - case ErrorCode::GatewayTimeout: - return "The server was acting as a gateway or proxy and did not receive " - "a timely response from the upstream " - "server."; + case ErrorCode::GatewayTimeout: + return "The server was acting as a gateway or proxy and did not receive " + "a timely response from the upstream " + "server."; - case ErrorCode::ProtocolVersionNotSupported: - return "The server does not support the protocol version used in the " - "request."; + case ErrorCode::ProtocolVersionNotSupported: + return "The server does not support the protocol version used in the " + "request."; - case ErrorCode::InsufficientStorage: - return "The server is unable to store the representation needed to " - "complete the request."; + case ErrorCode::InsufficientStorage: + return "The server is unable to store the representation needed to " + "complete the request."; - default: - return "Not-Implemented"; + default: + return "Not-Implemented"; } } diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp index c30c0a84b..89c9a52a9 100644 --- a/src/main/cpp/base/HostInfo.cpp +++ b/src/main/cpp/base/HostInfo.cpp @@ -30,7 +30,9 @@ void HostInfo::getEnv(const char* env, std::string& holder) { } } -bool HostInfo::hasHostInfo() const { return !unit_.empty() && !stage_.empty(); } +bool HostInfo::hasHostInfo() const { + return !unit_.empty() && !stage_.empty(); +} std::string HostInfo::queryString() const { if (!hasHostInfo()) { diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 1c50649dd..3dd4ca58d 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -10,12 +10,15 @@ ROCKETMQ_NAMESPACE_BEGIN -MQMessage::MQMessage() : MQMessage("", "", "", "") {} +MQMessage::MQMessage() : MQMessage("", "", "", "") { +} -MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) {} +MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) { +} MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& body) - : MQMessage(topic, tags, "", body) {} + : MQMessage(topic, tags, "", body) { +} MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body) @@ -35,9 +38,13 @@ MQMessage::MQMessage(const std::string& topic, const std::string& tags, const st impl_->body_.append(body.data(), body.length()); } -MQMessage::~MQMessage() { delete impl_; } +MQMessage::~MQMessage() { + delete impl_; +} -MQMessage::MQMessage(const MQMessage& other) { impl_ = new MessageImpl(*other.impl_); } +MQMessage::MQMessage(const MQMessage& other) { + impl_ = new MessageImpl(*other.impl_); +} MQMessage& MQMessage::operator=(const MQMessage& other) { if (this == &other) { @@ -47,9 +54,13 @@ MQMessage& MQMessage::operator=(const MQMessage& other) { return *this; } -const std::string& MQMessage::getMsgId() const { return impl_->system_attribute_.message_id; } +const std::string& MQMessage::getMsgId() const { + return impl_->system_attribute_.message_id; +} -std::string MQMessage::getBornHost() const { return impl_->system_attribute_.born_host; } +std::string MQMessage::getBornHost() const { + return impl_->system_attribute_.born_host; +} std::chrono::system_clock::time_point MQMessage::deliveryTimestamp() const { return absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); @@ -67,33 +78,57 @@ std::string MQMessage::getProperty(const std::string& name) const { return it->second; } -const std::string& MQMessage::getTopic() const { return impl_->topic_.name; } +const std::string& MQMessage::getTopic() const { + return impl_->topic_.name; +} -void MQMessage::setTopic(const std::string& topic) { impl_->topic_.name = topic; } +void MQMessage::setTopic(const std::string& topic) { + impl_->topic_.name = topic; +} -void MQMessage::setTopic(const char* data, int len) { impl_->topic_.name = std::string(data, len); } +void MQMessage::setTopic(const char* data, int len) { + impl_->topic_.name = std::string(data, len); +} -std::string MQMessage::getTags() const { return impl_->system_attribute_.tag; } +std::string MQMessage::getTags() const { + return impl_->system_attribute_.tag; +} -void MQMessage::setTags(const std::string& tags) { impl_->system_attribute_.tag = tags; } +void MQMessage::setTags(const std::string& tags) { + impl_->system_attribute_.tag = tags; +} -const std::vector& MQMessage::getKeys() const { return impl_->system_attribute_.keys; } +const std::vector& MQMessage::getKeys() const { + return impl_->system_attribute_.keys; +} -void MQMessage::setKey(const std::string& key) { impl_->system_attribute_.keys.push_back(key); } +void MQMessage::setKey(const std::string& key) { + impl_->system_attribute_.keys.push_back(key); +} -void MQMessage::setKeys(const std::vector& keys) { impl_->system_attribute_.keys = keys; } +void MQMessage::setKeys(const std::vector& keys) { + impl_->system_attribute_.keys = keys; +} -int MQMessage::getDelayTimeLevel() const { return impl_->system_attribute_.delay_level; } +int MQMessage::getDelayTimeLevel() const { + return impl_->system_attribute_.delay_level; +} -void MQMessage::setDelayTimeLevel(int level) { impl_->system_attribute_.delay_level = level; } +void MQMessage::setDelayTimeLevel(int level) { + impl_->system_attribute_.delay_level = level; +} -const std::string& MQMessage::traceContext() const { return impl_->system_attribute_.trace_context; } +const std::string& MQMessage::traceContext() const { + return impl_->system_attribute_.trace_context; +} void MQMessage::traceContext(const std::string& trace_context) { impl_->system_attribute_.trace_context = trace_context; } -const std::string& MQMessage::getBody() const { return impl_->body_; } +const std::string& MQMessage::getBody() const { + return impl_->body_; +} void MQMessage::setBody(const char* body, int len) { impl_->body_.clear(); @@ -101,11 +136,17 @@ void MQMessage::setBody(const char* body, int len) { impl_->body_.append(body, len); } -void MQMessage::setBody(const std::string& body) { impl_->body_ = body; } +void MQMessage::setBody(const std::string& body) { + impl_->body_ = body; +} -uint32_t MQMessage::bodyLength() const { return impl_->body_.length(); } +uint32_t MQMessage::bodyLength() const { + return impl_->body_.length(); +} -const std::map& MQMessage::getProperties() const { return impl_->user_attribute_map_; } +const std::map& MQMessage::getProperties() const { + return impl_->user_attribute_map_; +} void MQMessage::setProperties(const std::map& properties) { for (const auto& it : properties) { @@ -113,8 +154,12 @@ void MQMessage::setProperties(const std::map& properti } } -void MQMessage::messageType(MessageType message_type) { impl_->system_attribute_.message_type = message_type; } +void MQMessage::messageType(MessageType message_type) { + impl_->system_attribute_.message_type = message_type; +} -MessageType MQMessage::messageType() const { return impl_->system_attribute_.message_type; } +MessageType MQMessage::messageType() const { + return impl_->system_attribute_.message_type; +} ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp index a108138ff..af5127183 100644 --- a/src/main/cpp/base/MQMessageExt.cpp +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -3,9 +3,11 @@ ROCKETMQ_NAMESPACE_BEGIN -MQMessageExt::MQMessageExt() : MQMessage() {} +MQMessageExt::MQMessageExt() : MQMessage() { +} -MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) {} +MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) { +} MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { if (this == &other) { @@ -16,27 +18,41 @@ MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { return *this; } -int32_t MQMessageExt::getQueueId() const { return impl_->system_attribute_.partition_id; } +int32_t MQMessageExt::getQueueId() const { + return impl_->system_attribute_.partition_id; +} std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); } -int64_t MQMessageExt::getBornTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); } +int64_t MQMessageExt::getBornTimestamp() const { + return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); +} std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); } -int64_t MQMessageExt::getStoreTimestamp() const { return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); } +int64_t MQMessageExt::getStoreTimestamp() const { + return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); +} -std::string MQMessageExt::getStoreHost() const { return impl_->system_attribute_.store_host; } +std::string MQMessageExt::getStoreHost() const { + return impl_->system_attribute_.store_host; +} -int64_t MQMessageExt::getQueueOffset() const { return impl_->system_attribute_.partition_offset; } +int64_t MQMessageExt::getQueueOffset() const { + return impl_->system_attribute_.partition_offset; +} -int32_t MQMessageExt::getDeliveryAttempt() const { return impl_->system_attribute_.attempt_times; } +int32_t MQMessageExt::getDeliveryAttempt() const { + return impl_->system_attribute_.attempt_times; +} -const std::string& MQMessageExt::receiptHandle() const { return impl_->system_attribute_.receipt_handle; } +const std::string& MQMessageExt::receiptHandle() const { + return impl_->system_attribute_.receipt_handle; +} bool MQMessageExt::operator==(const MQMessageExt& other) { return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index 0c2196920..af6cbabad 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -9,14 +9,17 @@ ROCKETMQ_NAMESPACE_BEGIN -TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") {} +TopAddressing::TopAddressing() : TopAddressing("jmenv.tbsite.net", 8080, "/rocketmq/nsaddr") { +} TopAddressing::TopAddressing(std::string host, int port, std::string path) : host_(std::move(host)), port_(port), path_(std::move(path)), http_client_(absl::make_unique()) { http_client_->start(); } -TopAddressing::~TopAddressing() { http_client_->shutdown(); } +TopAddressing::~TopAddressing() { + http_client_->shutdown(); +} void TopAddressing::fetchNameServerAddresses(const std::function&)>& cb) { SPDLOG_DEBUG("Prepare to send HTTP request, timeout=3s"); diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index 21938f0ad..5753edca5 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -82,12 +82,12 @@ bool UtilAll::macAddress(std::vector& mac) { PIP_ADAPTER_INFO adaptor_info; DWORD buf_len = sizeof(IP_ADAPTER_INFO); char mac_address[18]; - adaptor_info = (IP_ADAPTER_INFO*) malloc(buf_len); + adaptor_info = (IP_ADAPTER_INFO*)malloc(buf_len); if (!adaptor_info) { // TODO: running out of memroy } - if(GetAdaptersInfo(adaptor_info, &buf_len) == NO_ERROR) { + if (GetAdaptersInfo(adaptor_info, &buf_len) == NO_ERROR) { PIP_ADAPTER_INFO item = adaptor_info; do { bool all_zero = true; @@ -105,7 +105,7 @@ bool UtilAll::macAddress(std::vector& mac) { break; } item = item->Next; - } while (item); + } while (item); } else { free(adaptor_info); } diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/base/include/DigestType.h index 93b14cd94..5416ffb3c 100644 --- a/src/main/cpp/base/include/DigestType.h +++ b/src/main/cpp/base/include/DigestType.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class DigestType : int8_t { +enum class DigestType : int8_t +{ CRC32 = 0, MD5 = 1, SHA1 = 2, diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/base/include/Encoding.h index f3af8aab7..a4a0efa88 100644 --- a/src/main/cpp/base/include/Encoding.h +++ b/src/main/cpp/base/include/Encoding.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class Encoding : int8_t { +enum class Encoding : int8_t +{ IDENTITY = 0, GZIP = 1, SNAPPY = 2, diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h index e62d5548a..6254d767b 100644 --- a/src/main/cpp/base/include/Histogram.h +++ b/src/main/cpp/base/include/Histogram.h @@ -36,7 +36,9 @@ class Histogram { * Change labels of histogram duration the initialization phase only. * @return */ - std::vector& labels() { return labels_; } + std::vector& labels() { + return labels_; + } void reportAndReset(std::string& result) { assert(labels_.size() == static_cast::size_type>(capacity_)); diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h index 4ab94761b..6f4cfeede 100644 --- a/src/main/cpp/base/include/HttpClient.h +++ b/src/main/cpp/base/include/HttpClient.h @@ -9,12 +9,14 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class HttpProtocol : int8_t { +enum class HttpProtocol : int8_t +{ HTTP = 1, HTTPS = 2, }; -enum class HttpStatus : int { +enum class HttpStatus : int +{ OK = 200, INTERNAL = 500, }; diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 276bb645c..3178d8c4d 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -38,7 +38,8 @@ struct BaseInvocationContext { std::chrono::steady_clock::time_point start_time{std::chrono::steady_clock::now()}; }; -template struct InvocationContext : public BaseInvocationContext { +template +struct InvocationContext : public BaseInvocationContext { void onCompletion(bool ok) override { /// Client-side Read, Server-side Read, Client-side diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 0eb5e72ab..f201fb545 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -72,11 +72,13 @@ class MixAll { static const char* SPAN_ANNOTATION_AWAIT_CONSUMPTION; static const char* SPAN_ANNOTATION_ATTR_START_TIME; - template static int64_t millisecondsOf(std::chrono::duration duration) { + template + static int64_t millisecondsOf(std::chrono::duration duration) { return std::chrono::duration_cast(duration).count(); } - template static int64_t microsecondsOf(std::chrono::duration duration) { + template + static int64_t microsecondsOf(std::chrono::duration duration) { return std::chrono::duration_cast(duration).count(); } diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h index 060bbd023..a3026a699 100644 --- a/src/main/cpp/base/include/RateLimiter.h +++ b/src/main/cpp/base/include/RateLimiter.h @@ -37,7 +37,8 @@ class RateLimiterObserver { std::thread tick_thread_; }; -template class RateLimiter : public Tick { +template +class RateLimiter : public Tick { public: explicit RateLimiter(uint32_t permit) : permits_{0}, interval_(1000 / PARTITION) { uint32_t avg = permit / PARTITION; @@ -57,7 +58,9 @@ template class RateLimiter : public Tick { ~RateLimiter() override = default; - std::array& partition() { return permits_; } + std::array& partition() { + return permits_; + } int slot() { auto current = std::chrono::steady_clock::now(); diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h index bcdf61006..c013e7af7 100644 --- a/src/main/cpp/base/include/TopAddressing.h +++ b/src/main/cpp/base/include/TopAddressing.h @@ -7,8 +7,8 @@ #include #include -#include "HttpClient.h" #include "HostInfo.h" +#include "HttpClient.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index ff711566e..fe7303d8d 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -1,6 +1,8 @@ #include "ClientConfigImpl.h" +#include #include +#include #ifndef _WIN32 #include @@ -24,10 +26,14 @@ ROCKETMQ_NAMESPACE_BEGIN const char* ClientConfigImpl::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; +std::string ClientConfigImpl::steadyName() { + auto duration = std::chrono::steady_clock::now().time_since_epoch(); + return std::to_string(std::chrono::duration_cast(duration).count()); +} + ClientConfigImpl::ClientConfigImpl(absl::string_view group_name) : group_name_(group_name.data(), group_name.length()), io_timeout_(absl::Seconds(3)), long_polling_timeout_(absl::Seconds(30)) { - instance_name_ = "DEFAULT"; } std::string ClientConfigImpl::clientId() const { @@ -35,26 +41,41 @@ std::string ClientConfigImpl::clientId() const { ss << UtilAll::hostname(); ss << "@"; std::string processID = std::to_string(getpid()); - ss << (instance_name_ == "DEFAULT" ? processID : instance_name_); + ss << processID << "#"; + ss << instance_name_; return ss.str(); } -const std::string& ClientConfigImpl::getInstanceName() const { return instance_name_; } +const std::string& ClientConfigImpl::getInstanceName() const { + return instance_name_; +} -void ClientConfigImpl::setInstanceName(std::string instance_name) { instance_name_ = std::move(instance_name); } +void ClientConfigImpl::setInstanceName(std::string instance_name) { + instance_name_ = std::move(instance_name); +} -const std::string& ClientConfigImpl::getGroupName() const { return group_name_; } +const std::string& ClientConfigImpl::getGroupName() const { + return group_name_; +} -void ClientConfigImpl::setGroupName(std::string group_name) { group_name_ = std::move(group_name); } +void ClientConfigImpl::setGroupName(std::string group_name) { + group_name_ = std::move(group_name); +} -void ClientConfigImpl::setIoTimeout(absl::Duration timeout) { io_timeout_ = timeout; } +void ClientConfigImpl::setIoTimeout(absl::Duration timeout) { + io_timeout_ = timeout; +} void ClientConfigImpl::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { credentials_provider_ = std::move(credentials_provider); } -CredentialsProviderPtr ClientConfigImpl::credentialsProvider() { return credentials_provider_; } +CredentialsProviderPtr ClientConfigImpl::credentialsProvider() { + return credentials_provider_; +} -absl::Duration ClientConfigImpl::getIoTimeout() const { return io_timeout_; } +absl::Duration ClientConfigImpl::getIoTimeout() const { + return io_timeout_; +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 53ab81e84..2ba6be1e2 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -35,8 +35,12 @@ void ClientManagerFactory::addClientManager(const std::string& resource_namespac client_manager_table_.insert_or_assign(resource_namespace, std::weak_ptr(client_manager)); } -ClientManagerFactory::ClientManagerFactory() : admin_server_(admin::AdminFacade::getServer()) { admin_server_.start(); } +ClientManagerFactory::ClientManagerFactory() : admin_server_(admin::AdminFacade::getServer()) { + admin_server_.start(); +} -ClientManagerFactory::~ClientManagerFactory() { admin_server_.stop(); } +ClientManagerFactory::~ClientManagerFactory() { + admin_server_.stop(); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index e7b04c348..6a4068b23 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -7,6 +7,7 @@ #include #include +#include "ReceiveMessageResult.h" #include "google/rpc/code.pb.h" #include "InvocationContext.h" @@ -168,6 +169,8 @@ void ClientManagerImpl::healthCheck( return; } + SPDLOG_DEBUG("Prepare to send health-check to {}. Request: {}", target_host, request.DebugString()); + auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -283,11 +286,8 @@ std::vector ClientManagerImpl::cleanOfflineRpcClients() { void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata& metadata, const HeartbeatRequest& request, std::chrono::milliseconds timeout, const std::function& cb) { + SPDLOG_DEBUG("Prepare to send heartbeat to {}. Request: {}", target_host, request.DebugString()); auto client = getRpcClient(target_host, true); - if (!client) { - return; - } - auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; for (const auto& item : metadata) { @@ -621,32 +621,32 @@ void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metad cb(ec, ptr); } break; case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; cb(ec, nullptr); } break; case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; cb(ec, nullptr); } break; case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::BadRequest; cb(ec, nullptr); } break; case google::rpc::Code::NOT_FOUND: { - SPDLOG_WARN("NotFound: {}", common.status().message()); + SPDLOG_WARN("NotFound: {}. Host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::NotFound; cb(ec, nullptr); } break; case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); + SPDLOG_WARN("InternalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; cb(ec, nullptr); } break; default: { - SPDLOG_WARN("NotImplement: Please upgrade to latest SDK release"); + SPDLOG_WARN("NotImplement: Please upgrade to latest SDK release. Host={}", invocation_context->remote_address); ec = ErrorCode::NotImplemented; cb(ec, nullptr); } break; @@ -678,23 +678,24 @@ void ClientManagerImpl::queryAssignment( SPDLOG_DEBUG("Query assignment OK"); } break; case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; } break; case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; } break; case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::BadRequest; } break; case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; } break; default: { - SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release"); + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. Host={}", + invocation_context->remote_address); ec = ErrorCode::NotImplemented; } break; } @@ -714,7 +715,7 @@ void ClientManagerImpl::queryAssignment( void ClientManagerImpl::receiveMessage(const std::string& target_host, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, const std::shared_ptr& cb) { - SPDLOG_DEBUG("Prepare to pop message from {} asynchronously. Request: {}", target_host, request.DebugString()); + SPDLOG_DEBUG("Prepare to receive message from {} asynchronously. Request: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); @@ -727,166 +728,71 @@ void ClientManagerImpl::receiveMessage(const std::string& target_host, const Met invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); auto callback = [this, cb](const InvocationContext* invocation_context) { - if (invocation_context->status.ok()) { - SPDLOG_DEBUG("Received pop response through gRPC from brokerAddress={}", invocation_context->remote_address); - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - ReceiveMessageResult receive_result; - this->processPopResult(invocation_context->context, invocation_context->response, receive_result, - invocation_context->remote_address); - cb->onSuccess(receive_result); - } break; - - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); - std::error_code ec = ErrorCode::Unauthorized; - cb->onFailure(ec); - } break; - - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); - std::error_code ec = ErrorCode::Forbidden; - cb->onFailure(ec); - } break; - - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); - std::error_code ec = ErrorCode::BadRequest; - cb->onFailure(ec); - } break; - - case google::rpc::Code::DEADLINE_EXCEEDED: { - SPDLOG_WARN("DeadlineExceeded: {}", common.status().message()); - std::error_code ec = ErrorCode::GatewayTimeout; - cb->onFailure(ec); - } break; - - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("IntervalServerError: {}", common.status().message()); - std::error_code ec = ErrorCode::InternalServerError; - cb->onFailure(ec); - } break; - default: { - SPDLOG_WARN("Unsupported code. Please upgrade to use the latest release"); - std::error_code ec = ErrorCode::NotImplemented; - cb->onFailure(ec); - } break; - } + std::error_code ec; + ReceiveMessageResult result; - } else { - SPDLOG_WARN("Failed to pop messages through GRPC from {}, gRPC code: {}, gRPC error message: {}", + // Handle network error. + if (!invocation_context->status.ok()) { + SPDLOG_WARN("Failed to pop messages through gRPC from {}, gRPC code: {}, gRPC error message: {}", invocation_context->remote_address, invocation_context->status.error_code(), invocation_context->status.error_message()); - std::error_code ec = ErrorCode::RequestTimeout; - cb->onFailure(ec); - } - }; - invocation_context->callback = callback; - client->asyncReceive(request, invocation_context); -} - -void ClientManagerImpl::processPopResult(const grpc::ClientContext& client_context, - const ReceiveMessageResponse& response, ReceiveMessageResult& result, - const std::string& target_host) { - // process response to result - ReceiveMessageStatus status; - switch (response.common().status().code()) { - case google::rpc::Code::OK: { - status = ReceiveMessageStatus::OK; - break; - } - case google::rpc::Code::RESOURCE_EXHAUSTED: { - status = ReceiveMessageStatus::RESOURCE_EXHAUSTED; - SPDLOG_WARN("Too many pop requests in broker. Long polling is full in the broker side. BrokerAddress={}", - target_host); - break; - } - - case google::rpc::Code::DEADLINE_EXCEEDED: { - status = ReceiveMessageStatus::DEADLINE_EXCEEDED; - break; - } - case google::rpc::Code::NOT_FOUND: { - status = ReceiveMessageStatus::NOT_FOUND; - break; - } - default: { - SPDLOG_WARN("Pop response indicates server-side error. BrokerAddress={}, Reason={}", target_host, - response.common().DebugString()); - status = ReceiveMessageStatus::INTERNAL; - break; - } - } - - result.sourceHost(target_host); - - std::vector msg_found_list; - if (ReceiveMessageStatus::OK == status) { - for (auto& item : response.messages()) { - MQMessageExt message_ext; - MessageAccessor::setTargetEndpoint(message_ext, target_host); - if (wrapMessage(item, message_ext)) { - msg_found_list.emplace_back(message_ext); - } else { - // TODO: NACK - } + ec = ErrorCode::RequestTimeout; + cb->onCompletion(ec, result); + return; } - } - result.status(status); - auto& delivery_timestamp = response.delivery_timestamp(); - timeval tv{}; - tv.tv_sec = delivery_timestamp.seconds(); - tv.tv_usec = delivery_timestamp.nanos() / 1000; - result.setPopTime(absl::TimeFromTimeval(tv)); - - const auto& protobuf_invisible_duration = response.invisible_duration(); - absl::Duration invisible_period = - absl::Seconds(protobuf_invisible_duration.seconds()) + absl::Nanoseconds(protobuf_invisible_duration.nanos()); - result.invisibleTime(invisible_period); - result.setMsgFoundList(msg_found_list); -} - -void ClientManagerImpl::processPullResult(const grpc::ClientContext& client_context, - const PullMessageResponse& response, ReceiveMessageResult& result, - const std::string& target_host) { - if (google::rpc::Code::OK != response.common().status().code()) { - result.status_ = ReceiveMessageStatus::INTERNAL; - return; - } - result.sourceHost(target_host); - switch (response.common().status().code()) { - case google::rpc::Code::OK: { - assert(!response.messages().empty()); - result.status_ = ReceiveMessageStatus::OK; - for (const auto& item : response.messages()) { - MQMessageExt message; - if (!wrapMessage(item, message)) { - result.status_ = ReceiveMessageStatus::DATA_CORRUPTED; - return; + // Handle application layer logic + result.source_host = invocation_context->remote_address; + const auto& common = invocation_context->response.common(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("ReceivedMessage Resonse: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + for (auto& item : invocation_context->response.messages()) { + MQMessageExt message_ext; + MessageAccessor::setTargetEndpoint(message_ext, invocation_context->remote_address); + if (wrapMessage(item, message_ext)) { + result.messages.emplace_back(message_ext); + } else { + SPDLOG_WARN("A message fails to pass body checksum validation. Skip processing it."); + } } - result.messages_.emplace_back(message); + break; } - break; - } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; - case google::rpc::Code::DEADLINE_EXCEEDED: { - result.status_ = ReceiveMessageStatus::DEADLINE_EXCEEDED; - break; - } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; - case google::rpc::Code::RESOURCE_EXHAUSTED: { - result.status_ = ReceiveMessageStatus::RESOURCE_EXHAUSTED; - break; - } + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; - case google::rpc::Code::OUT_OF_RANGE: { - result.status_ = ReceiveMessageStatus::OUT_OF_RANGE; - result.next_offset_ = response.next_offset(); - break; + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::GatewayTimeout; + } break; + + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("IntervalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("Unsupported code. Please upgrade to use the latest release. Host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; } - } + cb->onCompletion(ec, result); + }; + invocation_context->callback = callback; + client->asyncReceive(request, invocation_context); } State ClientManagerImpl::state() const { @@ -1393,22 +1299,81 @@ void ClientManagerImpl::queryOffset(const std::string& target_host, const Metada client->asyncQueryOffset(request, invocation_context); } -void ClientManagerImpl::pullMessage(const std::string& target_host, const Metadata& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { +void ClientManagerImpl::pullMessage( + const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { + SPDLOG_DEBUG("PullMessage Request: {}, target_host={}", request.DebugString(), target_host); auto client = getRpcClient(target_host); - if (!client) { - cb(nullptr); - return; - } - auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); } - invocation_context->callback = cb; + + auto callback = [cb, this](const InvocationContext* invocation_context) { + std::error_code ec; + ReceiveMessageResult result; + result.source_host = invocation_context->remote_address; + // Handle network issue. + if (!invocation_context->status.ok()) { + ec = ErrorCode::RequestTimeout; + cb(ec, result); + return; + } + + // Handle application layer logic: map status::code to corresponding error_code. + const auto& common = invocation_context->response.common(); + result.min_offset = invocation_context->response.min_offset(); + result.next_offset = invocation_context->response.next_offset(); + result.max_offset = invocation_context->response.max_offset(); + switch (common.status().code()) { + case google::rpc::Code::OK: { + SPDLOG_DEBUG("Received PullMessage Response: {}, host={}", invocation_context->response.DebugString(), + invocation_context->remote_address); + for (const auto& item : invocation_context->response.messages()) { + MQMessageExt message; + if (!wrapMessage(item, message)) { + return; + } + result.messages.emplace_back(message); + } + } break; + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Forbidden; + } break; + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::Unauthorized; + } break; + case google::rpc::Code::DEADLINE_EXCEEDED: { + SPDLOG_WARN("DeadlineExceeded: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::GatewayTimeout; + } break; + case google::rpc::Code::INVALID_ARGUMENT: { + SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::BadRequest; + } break; + case google::rpc::Code::FAILED_PRECONDITION: { + SPDLOG_WARN("FailedPrecondition: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::PreconditionRequired; + } break; + case google::rpc::Code::INTERNAL: { + SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::InternalServerError; + } break; + default: { + SPDLOG_WARN("Unimplemented: Please upgrade to use latest SDK release, host={}", + invocation_context->remote_address); + ec = ErrorCode::NotImplemented; + } break; + } + cb(ec, result); + }; + + invocation_context->callback = callback; client->asyncPull(request, invocation_context); } @@ -1416,12 +1381,8 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, const std::function*)>& cb) { + SPDLOG_DEBUG("ForwardMessageToDeadLetterQueue Request: {}", request.DebugString()); auto client = getRpcClient(target_host); - if (!client) { - cb(nullptr); - return; - } - auto invocation_context = new InvocationContext(); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -1462,6 +1423,8 @@ std::error_code ClientManagerImpl::notifyClientTermination(const std::string& ta context.AddMetadata(item.first, item.second); } + SPDLOG_DEBUG("NotifyClientTermination request: {}", request.DebugString()); + NotifyClientTerminationResponse response; grpc::Status status = client->notifyClientTermination(&context, request, &response); if (!status.ok()) { diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 2337f1a07..12af6d76d 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -37,7 +37,9 @@ void RpcClientImpl::asyncQueryAssignment(const QueryAssignmentRequest& request, invocation_context); } -std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { return completion_queue_; } +std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { + return completion_queue_; +} void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, InvocationContext* invocation_context) { @@ -109,9 +111,13 @@ void RpcClientImpl::addMetadata(grpc::ClientContext& context, } } -bool RpcClientImpl::needHeartbeat() { return need_heartbeat_; } +bool RpcClientImpl::needHeartbeat() { + return need_heartbeat_; +} -void RpcClientImpl::needHeartbeat(bool need_heartbeat) { need_heartbeat_ = need_heartbeat; } +void RpcClientImpl::needHeartbeat(bool need_heartbeat) { + need_heartbeat_ = need_heartbeat; +} void RpcClientImpl::asyncMultiplexingCall(const MultiplexingRequest& request, InvocationContext* invocation_context) { diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index aed343d78..f35f98612 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -32,18 +32,18 @@ TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) : debu for (const auto& address : broker.endpoints().addresses()) { if (service_address.empty()) { switch (broker.endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - service_address.append("ipv4:"); - break; - case rmq::AddressScheme::IPv6: - service_address.append("ipv6:"); - break; - case rmq::AddressScheme::DOMAIN_NAME: - service_address.append("dns:"); - break; - default: - SPDLOG_WARN("Unsupported gRPC naming scheme"); - break; + case rmq::AddressScheme::IPv4: + service_address.append("ipv4:"); + break; + case rmq::AddressScheme::IPv6: + service_address.append("ipv6:"); + break; + case rmq::AddressScheme::DOMAIN_NAME: + service_address.append("dns:"); + break; + default: + SPDLOG_WARN("Unsupported gRPC naming scheme"); + break; } } else { service_address.append(","); diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index 220c6ad17..454a61b39 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -10,7 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN class Assignment { public: - Assignment(MQMessageQueue message_queue) : message_queue_(std::move(message_queue)) {} + Assignment(MQMessageQueue message_queue) : message_queue_(std::move(message_queue)) { + } bool operator==(const Assignment& rhs) const { if (this == &rhs) { @@ -20,9 +21,13 @@ class Assignment { return message_queue_ == rhs.message_queue_; } - bool operator<(const Assignment& rhs) const { return message_queue_ < rhs.message_queue_; } + bool operator<(const Assignment& rhs) const { + return message_queue_ < rhs.message_queue_; + } - const MQMessageQueue& messageQueue() const { return message_queue_; } + const MQMessageQueue& messageQueue() const { + return message_queue_; + } private: MQMessageQueue message_queue_; diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h index 1fc17e111..5464a1d6c 100644 --- a/src/main/cpp/client/include/Broker.h +++ b/src/main/cpp/client/include/Broker.h @@ -10,19 +10,32 @@ ROCKETMQ_NAMESPACE_BEGIN class Broker { public: Broker(std::string name, int id, ServiceAddress service_address) - : name_(std::move(name)), id_(id), service_address_(std::move(service_address)) {} + : name_(std::move(name)), id_(id), service_address_(std::move(service_address)) { + } - const std::string& name() const { return name_; } + const std::string& name() const { + return name_; + } - int32_t id() const { return id_; } + int32_t id() const { + return id_; + } - explicit operator bool() const { return service_address_.operator bool(); } + explicit operator bool() const { + return service_address_.operator bool(); + } - bool operator==(const Broker& other) const { return name_ == other.name_; } + bool operator==(const Broker& other) const { + return name_ == other.name_; + } - bool operator<(const Broker& other) const { return name_ < other.name_; } + bool operator<(const Broker& other) const { + return name_ < other.name_; + } - std::string serviceAddress() const { return service_address_.address(); } + std::string serviceAddress() const { + return service_address_.address(); + } private: std::string name_; diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index d45392d3b..5a3d30b39 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -19,26 +19,29 @@ class ClientConfigImpl : virtual public ClientConfig { ~ClientConfigImpl() override = default; - const std::string &resourceNamespace() const override { + const std::string& resourceNamespace() const override { return resource_namespace_; } void resourceNamespace(absl::string_view resource_namespace) { - resource_namespace_ = - std::string(resource_namespace.data(), resource_namespace.length()); + resource_namespace_ = std::string(resource_namespace.data(), resource_namespace.length()); } std::string clientId() const override; - const std::string &getInstanceName() const; + const std::string& getInstanceName() const; void setInstanceName(std::string instance_name); - const std::string &getGroupName() const override; + const std::string& getGroupName() const override; void setGroupName(std::string group_name); - const std::string &getUnitName() const { return unit_name_; } - void setUnitName(std::string unit_name) { unit_name_ = std::move(unit_name); } + const std::string& getUnitName() const { + return unit_name_; + } + void setUnitName(std::string unit_name) { + unit_name_ = std::move(unit_name); + } absl::Duration getIoTimeout() const override; void setIoTimeout(absl::Duration timeout); @@ -51,8 +54,12 @@ class ClientConfigImpl : virtual public ClientConfig { long_polling_timeout_ = timeout; } - bool isTracingEnabled() { return enable_tracing_.load(); } - void enableTracing(bool enabled) { enable_tracing_.store(enabled); } + bool isTracingEnabled() { + return enable_tracing_.load(); + } + void enableTracing(bool enabled) { + enable_tracing_.store(enabled); + } CredentialsProviderPtr credentialsProvider() override; void setCredentialsProvider(CredentialsProviderPtr credentials_provider); @@ -60,15 +67,25 @@ class ClientConfigImpl : virtual public ClientConfig { void serviceName(std::string service_name) { service_name_ = std::move(service_name); } - const std::string &serviceName() const override { return service_name_; } + const std::string& serviceName() const override { + return service_name_; + } - void region(std::string region) { region_ = std::move(region); } - const std::string ®ion() const override { return region_; } + void region(std::string region) { + region_ = std::move(region); + } + const std::string& region() const override { + return region_; + } - void tenantId(std::string tenant_id) { tenant_id_ = std::move(tenant_id); } - const std::string &tenantId() const override { return tenant_id_; } + void tenantId(std::string tenant_id) { + tenant_id_ = std::move(tenant_id); + } + const std::string& tenantId() const override { + return tenant_id_; + } - static const char *CLIENT_VERSION; + static const char* CLIENT_VERSION; protected: /** @@ -92,7 +109,7 @@ class ClientConfigImpl : virtual public ClientConfig { */ std::string tenant_id_; - std::string instance_name_; + std::string instance_name_{steadyName()}; std::string group_name_; @@ -105,6 +122,8 @@ class ClientConfigImpl : virtual public ClientConfig { absl::Duration long_polling_timeout_; std::atomic enable_tracing_{false}; + + static std::string steadyName(); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 8cd1ea580..20ea165d8 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -80,12 +80,9 @@ class ClientManager { virtual bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, SendCallback* cb) = 0; - virtual void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host) = 0; - virtual void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; + const std::function& cb) = 0; virtual std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 4432f412b..321664c23 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -175,7 +175,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) override; + const std::function& cb) override; std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, @@ -189,15 +189,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::chrono::milliseconds timeout, const std::function& cb) override; - void processPullResult(const grpc::ClientContext& client_context, const PullMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host) override; - State state() const override; private: - void processPopResult(const grpc::ClientContext& client_context, const ReceiveMessageResponse& response, - ReceiveMessageResult& result, const std::string& target_host); - void doHeartbeat(); void pollCompletionQueue(); diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h index e57b5b34e..ae0909f21 100644 --- a/src/main/cpp/client/include/ConsumeMessageType.h +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class ConsumeMessageType : int8_t { +enum class ConsumeMessageType : int8_t +{ ACTIVE = 0, PASSIVE = 1, }; diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h index 05a73d23b..d7578c93b 100644 --- a/src/main/cpp/client/include/InterceptorContinuation.h +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -8,7 +8,8 @@ ROCKETMQ_NAMESPACE_BEGIN class InterceptorContinuation { public: - explicit InterceptorContinuation(grpc::experimental::InterceptorBatchMethods* methods) : methods_(methods) {} + explicit InterceptorContinuation(grpc::experimental::InterceptorBatchMethods* methods) : methods_(methods) { + } ~InterceptorContinuation() { if (!hijacked_) { @@ -16,7 +17,9 @@ class InterceptorContinuation { } } - void hijack() { hijacked_ = true; } + void hijack() { + hijacked_ = true; + } private: grpc::experimental::InterceptorBatchMethods* methods_; diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h index a14bbf00f..809a24c9b 100644 --- a/src/main/cpp/client/include/LogInterceptor.h +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -6,7 +6,8 @@ ROCKETMQ_NAMESPACE_BEGIN class LogInterceptor : public grpc::experimental::Interceptor { public: - explicit LogInterceptor(grpc::experimental::ClientRpcInfo* client_rpc_info) : client_rpc_info_(client_rpc_info) {} + explicit LogInterceptor(grpc::experimental::ClientRpcInfo* client_rpc_info) : client_rpc_info_(client_rpc_info) { + } void Intercept(grpc::experimental::InterceptorBatchMethods* methods) override; diff --git a/src/main/cpp/client/include/OpenSSLCompatible.h b/src/main/cpp/client/include/OpenSSLCompatible.h index f51a47055..e64b95b2a 100644 --- a/src/main/cpp/client/include/OpenSSLCompatible.h +++ b/src/main/cpp/client/include/OpenSSLCompatible.h @@ -268,11 +268,17 @@ int DH_set_length(DH* dh, long length) { return 1; } -const unsigned char* EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX* ctx) { return ctx->iv; } +const unsigned char* EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX* ctx) { + return ctx->iv; +} -unsigned char* EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX* ctx) { return ctx->iv; } +unsigned char* EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX* ctx) { + return ctx->iv; +} -EVP_MD_CTX* EVP_MD_CTX_new(void) { return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); } +EVP_MD_CTX* EVP_MD_CTX_new(void) { + return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); +} void EVP_MD_CTX_free(EVP_MD_CTX* ctx) { EVP_MD_CTX_cleanup(ctx); @@ -334,7 +340,9 @@ void RSA_meth_free(RSA_METHOD* meth) { } } -int RSA_bits(const RSA* r) { return (BN_num_bits(r->n)); } +int RSA_bits(const RSA* r) { + return (BN_num_bits(r->n)); +} RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey) { if (pkey->type != EVP_PKEY_RSA) { diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h index 51753a710..d11018fd6 100644 --- a/src/main/cpp/client/include/Partition.h +++ b/src/main/cpp/client/include/Partition.h @@ -7,20 +7,35 @@ ROCKETMQ_NAMESPACE_BEGIN -enum Permission : int8_t { NONE = 0, READ = 1, WRITE = 2, READ_WRITE = 3 }; +enum Permission : int8_t +{ + NONE = 0, + READ = 1, + WRITE = 2, + READ_WRITE = 3 +}; class Partition { public: Partition(Topic topic, int32_t id, Permission permission, Broker broker) - : topic_(std::move(topic)), broker_(std::move(broker)), id_(id), permission_(permission) {} + : topic_(std::move(topic)), broker_(std::move(broker)), id_(id), permission_(permission) { + } - const Topic& topic() const { return topic_; } + const Topic& topic() const { + return topic_; + } - int32_t id() const { return id_; } + int32_t id() const { + return id_; + } - const Broker& broker() const { return broker_; } + const Broker& broker() const { + return broker_; + } - Permission permission() const { return permission_; } + Permission permission() const { + return permission_; + } MQMessageQueue asMessageQueue() const { MQMessageQueue message_queue(topic_.name(), broker_.name(), id_); diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h index e4088a6f8..6eba0e3f6 100644 --- a/src/main/cpp/client/include/ReceiveMessageCallback.h +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -1,9 +1,10 @@ #pragma once +#include + #include "ReceiveMessageResult.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/ErrorCode.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -11,9 +12,7 @@ class ReceiveMessageCallback : public AsyncCallback { public: ~ReceiveMessageCallback() override = default; - virtual void onSuccess(ReceiveMessageResult &result) = 0; - - virtual void onFailure(const std::error_code &ec) = 0; + virtual void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index badf8e10a..e53607a45 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -10,119 +11,17 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class ReceiveMessageStatus : int32_t { - /** - * Messages are received as expected. - */ - OK, - - /** - * Deadline expired before matched messages are found in the server side. - */ - DEADLINE_EXCEEDED, - - /** - * Resource has been exhausted, perhaps a per-user quota. For example, too many receive-message requests are submitted - * to the same partition at the same time. - */ - RESOURCE_EXHAUSTED, - - /** - * The target partition does not exist, which might have been deleted. - */ - NOT_FOUND, - - /** - * Receive-message operation was attempted past the valid range. For pull operation, clients may try to pull expired - * messages. - */ - OUT_OF_RANGE, - - /** - * Data is corrupted during transfer. - */ - DATA_CORRUPTED, - - /** - * Serious errors occurred in the server side. - */ - INTERNAL -}; - -static const char* EnumStrings[] = { - "FOUND", "DEADLINE_EXCEEDED", "RESOURCE_EXHAUSTED", "NOT_FOUND", "OUT_OF_RANGE", "DATA_CORRUPTED", "INTERNAL"}; - struct ReceiveMessageResult { - ReceiveMessageResult() = default; - - ReceiveMessageResult(ReceiveMessageStatus receive_status, absl::Time pop_time, absl::Duration invisible_period, - long long rest_num, std::vector msg_found_list) - : status_(receive_status), pop_time_(pop_time), invisible_time_(invisible_period), - messages_(std::move(msg_found_list)) {} - - ReceiveMessageResult(const ReceiveMessageResult& other) { - status_ = other.status_; - pop_time_ = other.pop_time_; - invisible_time_ = other.invisible_time_; - messages_ = other.messages_; - source_host_ = other.source_host_; - next_offset_ = other.next_offset_; - } - - ReceiveMessageResult& operator=(const ReceiveMessageResult& other) { - if (this == &other) { - return *this; - } - status_ = other.status_; - pop_time_ = other.pop_time_; - invisible_time_ = other.invisible_time_; - messages_ = other.messages_; - source_host_ = other.source_host_; - next_offset_ = other.next_offset_; - return *this; - } - - ~ReceiveMessageResult() = default; - - std::string toString() const { - std::stringstream ss; - ss << "ReceiveMessageResult[ popStatus=" << EnumStrings[static_cast(status_)] - << ", popTime=" << absl::FormatTime(pop_time_, absl::UTCTimeZone()) - << "[UTC], invisibleTime=" << ToInt64Milliseconds(invisible_time_) - << "ms, len(msgFoundList)=" << messages_.size() << " ]"; - return ss.str(); - } - - void status(ReceiveMessageStatus receive_status) { status_ = receive_status; } - - ReceiveMessageStatus status() const { return status_; } - - void setPopTime(absl::Time pop_time) { pop_time_ = pop_time; } - - absl::Time getPopTime() const { return pop_time_; } - - void invisibleTime(absl::Duration invisible_period) { invisible_time_ = invisible_period; } - - absl::Duration invisibleTime() const { return invisible_time_; } - - void setMsgFoundList(std::vector& msg_found_list) { messages_ = msg_found_list; } - - const std::vector& getMsgFoundList() const { return messages_; } - - const std::string& sourceHost() const { return source_host_; } - - void sourceHost(std::string source_host) { source_host_ = std::move(source_host); } - - ReceiveMessageStatus status_{ReceiveMessageStatus::DEADLINE_EXCEEDED}; - - absl::Time pop_time_; - absl::Duration invisible_time_; + absl::Time pop_time; + absl::Duration invisible_time; - std::vector messages_; + std::vector messages; - std::string source_host_; + std::string source_host; - int64_t next_offset_{0}; + std::int64_t min_offset{0}; + std::int64_t next_offset{0}; + std::int64_t max_offset{0}; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index fbdbe40be..7cc782b49 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -13,7 +13,8 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this completion_queue, std::shared_ptr channel, bool need_heartbeat = true) : completion_queue_(std::move(completion_queue)), channel_(std::move(channel)), - stub_(rmq::MessagingService::NewStub(channel_)), need_heartbeat_(need_heartbeat) {} + stub_(rmq::MessagingService::NewStub(channel_)), need_heartbeat_(need_heartbeat) { + } RpcClientImpl(const RpcClientImpl&) = delete; diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index a54bb8e5d..9a4c36cbc 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -8,18 +8,42 @@ ROCKETMQ_NAMESPACE_BEGIN class SendMessageContext { public: - const std::string& getProducerGroup() const { return producer_group_; }; - void setProducerGroup(const std::string& producer_group) { this->producer_group_ = producer_group; }; - const MQMessage& getMessage() const { return message_; } - void setMessage(const MQMessage& message) { this->message_ = message; } - const MQMessageQueue& getMessageQueue() const { return message_queue_; } - void setMessageQueue(const MQMessageQueue& message_queue) { this->message_queue_ = message_queue; } - const std::string& getBornHost() const { return born_host_; } - void setBornHost(const std::string& born_host) { this->born_host_ = born_host; } - const std::string& getMessageId() const { return message_id_; } - void setMessageId(const std::string& message_id) { this->message_id_ = message_id; } - long long int getQueueOffset() const { return queue_offset_; } - void setQueueOffset(long long queue_offset) { this->queue_offset_ = queue_offset; } + const std::string& getProducerGroup() const { + return producer_group_; + }; + void setProducerGroup(const std::string& producer_group) { + this->producer_group_ = producer_group; + }; + const MQMessage& getMessage() const { + return message_; + } + void setMessage(const MQMessage& message) { + this->message_ = message; + } + const MQMessageQueue& getMessageQueue() const { + return message_queue_; + } + void setMessageQueue(const MQMessageQueue& message_queue) { + this->message_queue_ = message_queue; + } + const std::string& getBornHost() const { + return born_host_; + } + void setBornHost(const std::string& born_host) { + this->born_host_ = born_host; + } + const std::string& getMessageId() const { + return message_id_; + } + void setMessageId(const std::string& message_id) { + this->message_id_ = message_id; + } + long long int getQueueOffset() const { + return queue_offset_; + } + void setQueueOffset(long long queue_offset) { + this->queue_offset_ = queue_offset; + } private: std::string producer_group_; diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h index 067e73494..0708fedc3 100644 --- a/src/main/cpp/client/include/ServiceAddress.h +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -10,7 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN -enum AddressScheme : int8_t { +enum AddressScheme : int8_t +{ IPv4 = 0, IPv6 = 1, DOMAIN_NAME = 2, @@ -18,9 +19,12 @@ enum AddressScheme : int8_t { class Address { public: - Address(absl::string_view host, int32_t port) : host_(host.data(), host.length()), port_(port) {} + Address(absl::string_view host, int32_t port) : host_(host.data(), host.length()), port_(port) { + } - bool operator==(const Address& other) const { return host_ == other.host_ && port_ == other.port_; } + bool operator==(const Address& other) const { + return host_ == other.host_ && port_ == other.port_; + } bool operator<(const Address& other) const { if (host_ < other.host_) { @@ -38,9 +42,13 @@ class Address { return false; } - const std::string& host() const { return host_; } + const std::string& host() const { + return host_; + } - int32_t port() const { return port_; } + int32_t port() const { + return port_; + } private: std::string host_; @@ -58,22 +66,26 @@ class ServiceAddress { std::sort(addresses_.begin(), addresses_.end()); } - AddressScheme scheme() const { return scheme_; } + AddressScheme scheme() const { + return scheme_; + } - explicit operator bool() const { return !addresses_.empty(); } + explicit operator bool() const { + return !addresses_.empty(); + } std::string address() const { std::string result; switch (scheme_) { - case AddressScheme::IPv4: - result.append("ipv4:"); - break; - case AddressScheme::IPv6: - result.append("ipv6:"); - break; - case AddressScheme::DOMAIN_NAME: - result.append("dns:"); - break; + case AddressScheme::IPv4: + result.append("ipv4:"); + break; + case AddressScheme::IPv6: + result.append("ipv6:"); + break; + case AddressScheme::DOMAIN_NAME: + result.append("dns:"); + break; } bool first = true; diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h index 801ecf109..b55a982db 100644 --- a/src/main/cpp/client/include/TlsHelper.h +++ b/src/main/cpp/client/include/TlsHelper.h @@ -37,7 +37,9 @@ class TlsServerAuthorizationChecker : public grpc::experimental::TlsServerAuthor return 0; } - ~TlsServerAuthorizationChecker() override { SPDLOG_DEBUG("~TlsServerAuthorizationChecker() invoked"); } + ~TlsServerAuthorizationChecker() override { + SPDLOG_DEBUG("~TlsServerAuthorizationChecker() invoked"); + } }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h index 360f794ad..20b90e383 100644 --- a/src/main/cpp/client/include/Topic.h +++ b/src/main/cpp/client/include/Topic.h @@ -10,11 +10,16 @@ ROCKETMQ_NAMESPACE_BEGIN class Topic { public: Topic(std::string resource_namespace, std::string name) - : resource_namespace_(std::move(resource_namespace)), name_(std::move(name)) {} + : resource_namespace_(std::move(resource_namespace)), name_(std::move(name)) { + } - const std::string& resourceNamespace() const { return resource_namespace_; } + const std::string& resourceNamespace() const { + return resource_namespace_; + } - const std::string& name() const { return name_; } + const std::string& name() const { + return name_; + } bool operator==(const Topic& other) const { return resource_namespace_ == other.resource_namespace_ && name_ == other.name_; diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h index 41ec33664..fdad08e9f 100644 --- a/src/main/cpp/client/include/TopicAssignmentInfo.h +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -18,15 +18,25 @@ class TopicAssignment { ~TopicAssignment() = default; - const std::vector& assignmentList() const { return assignment_list_; } + const std::vector& assignmentList() const { + return assignment_list_; + } - bool operator==(const TopicAssignment& rhs) const { return assignment_list_ == rhs.assignment_list_; } + bool operator==(const TopicAssignment& rhs) const { + return assignment_list_ == rhs.assignment_list_; + } - bool operator!=(const TopicAssignment& rhs) const { return assignment_list_ != rhs.assignment_list_; } + bool operator!=(const TopicAssignment& rhs) const { + return assignment_list_ != rhs.assignment_list_; + } - const std::string& debugString() const { return debug_string_; } + const std::string& debugString() const { + return debug_string_; + } - static unsigned int getAndIncreaseQueryWhichBroker() { return ++query_which_broker_; } + static unsigned int getAndIncreaseQueryWhichBroker() { + return ++query_which_broker_; + } private: /** diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h index 2de0c8386..265f38270 100644 --- a/src/main/cpp/client/include/TopicRouteData.h +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -20,13 +20,21 @@ class TopicRouteData { std::sort(partitions_.begin(), partitions_.end()); } - const std::vector& partitions() const { return partitions_; } + const std::vector& partitions() const { + return partitions_; + } - const std::string& debugString() const { return debug_string_; } + const std::string& debugString() const { + return debug_string_; + } - bool operator==(const TopicRouteData& other) const { return partitions_ == other.partitions_; } + bool operator==(const TopicRouteData& other) const { + return partitions_ == other.partitions_; + } - bool operator!=(const TopicRouteData& other) const { return !this->operator==(other); } + bool operator!=(const TopicRouteData& other) const { + return !this->operator==(other); + } private: std::vector partitions_; diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 1ac53eb4c..0f1fe7dfb 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -79,13 +79,9 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback*), (override)); - MOCK_METHOD(void, processPullResult, - (const grpc::ClientContext&, const PullMessageResponse&, ReceiveMessageResult&, const std::string&), - (override)); - MOCK_METHOD(void, pullMessage, (const std::string&, const Metadata&, const PullMessageRequest&, std::chrono::milliseconds, - (const std::function*)>&)), + (const std::function&)), (override)); MOCK_METHOD(std::error_code, notifyClientTermination, diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h index 7f4090382..76806770f 100644 --- a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -8,9 +8,7 @@ ROCKETMQ_NAMESPACE_BEGIN class ReceiveMessageCallbackMock : public ReceiveMessageCallback { public: - MOCK_METHOD(void, onSuccess, (ReceiveMessageResult &), (override)); - - MOCK_METHOD(void, onFailure, (const std::error_code &), (override)); + MOCK_METHOD(void, onCompletion, (const std::error_code&, const ReceiveMessageResult&), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index d3f925290..a4363fdf7 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -11,7 +11,9 @@ class RpcClientMock : public RpcClient { public: RpcClientMock(); - ~RpcClientMock() override { std::cout << "~RpcClientMock()" << std::endl; } + ~RpcClientMock() override { + std::cout << "~RpcClientMock()" << std::endl; + } MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h index 6ef2183a0..099663093 100644 --- a/src/main/cpp/concurrent/include/CountdownLatch.h +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -12,8 +12,10 @@ ROCKETMQ_NAMESPACE_BEGIN class CountdownLatch { public: - explicit CountdownLatch(int32_t count) : CountdownLatch(count, "anonymous") {} - CountdownLatch(int32_t count, absl::string_view name) : count_(count), name_(name.data(), name.length()) {} + explicit CountdownLatch(int32_t count) : CountdownLatch(count, "anonymous") { + } + CountdownLatch(int32_t count, absl::string_view name) : count_(count), name_(name.data(), name.length()) { + } void await() LOCKS_EXCLUDED(mtx_); diff --git a/src/main/cpp/log/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h index f2deb3316..2aee66f39 100644 --- a/src/main/cpp/log/include/LoggerImpl.h +++ b/src/main/cpp/log/include/LoggerImpl.h @@ -15,9 +15,13 @@ class LoggerImpl : public Logger { void setConsoleLevel(Level level) override; - void setFileSize(std::size_t file_size) override { file_size_ = file_size; } + void setFileSize(std::size_t file_size) override { + file_size_ = file_size; + } - void setFileCount(std::size_t file_count) override { file_count_ = file_count; } + void setFileCount(std::size_t file_count) override { + file_count_ = file_count; + } void setPattern(std::string pattern) override { pattern_ = std::move(pattern); @@ -34,27 +38,28 @@ class LoggerImpl : public Logger { private: void init0(); - template void setLevel(std::shared_ptr& target, Level level) { + template + void setLevel(std::shared_ptr& target, Level level) { if (!target) { return; } switch (level) { - case Level::Trace: - target->set_level(spdlog::level::trace); - break; - case Level::Debug: - target->set_level(spdlog::level::debug); - break; - case Level::Info: - target->set_level(spdlog::level::info); - break; - case Level::Warn: - target->set_level(spdlog::level::warn); - break; - case Level::Error: - target->set_level(spdlog::level::err); - break; + case Level::Trace: + target->set_level(spdlog::level::trace); + break; + case Level::Debug: + target->set_level(spdlog::level::debug); + break; + case Level::Info: + target->set_level(spdlog::level::info); + break; + case Level::Warn: + target->set_level(spdlog::level::warn); + break; + case Level::Error: + target->set_level(spdlog::level::err); + break; } } diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index 9bd1d5233..d4ac095cf 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -1,9 +1,11 @@ #include "AsyncReceiveMessageCallback.h" + +#include + #include "ClientManagerImpl.h" #include "ConsumeMessageType.h" #include "LoggerImpl.h" #include "PushConsumer.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -12,71 +14,35 @@ AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr pro receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); } -void AsyncReceiveMessageCallback::onSuccess(ReceiveMessageResult& result) { - ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); - if (!process_queue_shared_ptr) { - SPDLOG_WARN("Process queue has been released. Drop PopResult: {}", result.toString()); +void AsyncReceiveMessageCallback::onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) { + ProcessQueueSharedPtr process_queue = process_queue_.lock(); + if (!process_queue) { + SPDLOG_INFO("Process queue has been destructed."); return; } - std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); + std::shared_ptr impl = process_queue->getConsumer().lock(); if (!impl->active()) { + SPDLOG_INFO("Consumer is not active any more. It should be quitting"); return; } - auto receive_message_action = process_queue_shared_ptr->getConsumer().lock()->receiveMessageAction(); - - switch (result.status()) { - case ReceiveMessageStatus::OK: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", - result.sourceHost(), result.getMsgFoundList().size(), process_queue_shared_ptr->simpleName()); - process_queue_shared_ptr->cacheMessages(result.getMsgFoundList()); - impl->getConsumeMessageService()->signalDispatcher(); + auto consumer = process_queue->getConsumer().lock(); + if (!consumer) { + return; + } - if (ReceiveMessageAction::PULL == receive_message_action) { - process_queue_shared_ptr->nextOffset(result.next_offset_); - } - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::DATA_CORRUPTED: - if (ReceiveMessageAction::POLLING == receive_message_action) { - process_queue_shared_ptr->cacheMessages(result.messages_); - impl->getConsumeMessageService()->signalDispatcher(); - } - checkThrottleThenReceive(); - break; - - case ReceiveMessageStatus::OUT_OF_RANGE: - assert(ReceiveMessageAction::PULL == receive_message_action); - process_queue_shared_ptr->nextOffset(result.next_offset_); - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::DEADLINE_EXCEEDED: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=DEADLINE_EXCEEDED, queue={}", - result.sourceHost(), process_queue_shared_ptr->simpleName()); - checkThrottleThenReceive(); - break; - case ReceiveMessageStatus::INTERNAL: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=UNKNOWN, queue={}", result.sourceHost(), - process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - case ReceiveMessageStatus::RESOURCE_EXHAUSTED: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=RESOURCE_EXHAUSTED, queue={}", - result.sourceHost(), process_queue_shared_ptr->simpleName()); + if (ec) { + SPDLOG_WARN("Receive message from {} failed. Cause: {}. Attempt later.", process_queue->simpleName(), ec.message()); receiveMessageLater(); - break; - case ReceiveMessageStatus::NOT_FOUND: - SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=NOT_FOUND, queue={}", result.sourceHost(), - process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; - default: - SPDLOG_WARN("Unknown receive message status: {} from broker[host={}], queue={}", result.status(), - result.sourceHost(), process_queue_shared_ptr->simpleName()); - receiveMessageLater(); - break; + return; } + + SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", + result.source_host, result.messages.size(), process_queue->simpleName()); + process_queue->cacheMessages(result.messages); + impl->getConsumeMessageService()->signalDispatcher(); + checkThrottleThenReceive(); } const char* AsyncReceiveMessageCallback::RECEIVE_LATER_TASK_NAME = "receive-later-task"; @@ -99,15 +65,6 @@ void AsyncReceiveMessageCallback::checkThrottleThenReceive() { } } -void AsyncReceiveMessageCallback::onFailure(const std::error_code& ec) { - auto process_queue_ptr = process_queue_.lock(); - if (process_queue_ptr) { - SPDLOG_WARN("pop message error:{}, pop message later. Queue={}", ec.message(), process_queue_ptr->simpleName()); - // pop message later - receiveMessageLater(); - } -} - void AsyncReceiveMessageCallback::receiveMessageLater() { auto process_queue = process_queue_.lock(); if (!process_queue) { diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 60a404987..b4a247673 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -24,12 +25,14 @@ ROCKETMQ_NAMESPACE_BEGIN -ClientImpl::ClientImpl(absl::string_view group_name) : ClientConfigImpl(group_name), state_(State::CREATED) {} +ClientImpl::ClientImpl(absl::string_view group_name) : ClientConfigImpl(group_name), state_(State::CREATED) { +} void ClientImpl::start() { State expected = CREATED; if (!state_.compare_exchange_strong(expected, State::STARTING)) { - SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); + SPDLOG_ERROR("Attempt to start ClientImpl failed. Expecting: {} Actual: {}", State::CREATED, + state_.load(std::memory_order_relaxed)); return; } @@ -56,21 +59,21 @@ void ClientImpl::start() { route_update_handle_ = client_manager_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, std::chrono::seconds(10), std::chrono::seconds(30)); - state_.store(State::STARTED); } void ClientImpl::shutdown() { - state_.store(State::STOPPING, std::memory_order_relaxed); - - name_server_resolver_->shutdown(); - - if (route_update_handle_) { - client_manager_->getScheduler().cancel(route_update_handle_); + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED)) { + name_server_resolver_->shutdown(); + if (route_update_handle_) { + client_manager_->getScheduler().cancel(route_update_handle_); + } + notifyClientTermination(); + client_manager_.reset(); + } else { + SPDLOG_ERROR("Try to shutdown ClientImpl, but its state is not as expected. Expecting: {}, Actual: {}", + State::STOPPING, state_.load(std::memory_order_relaxed)); } - - notifyClientTermination(); - - client_manager_.reset(); } const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; @@ -321,71 +324,71 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextresponse.type_case()) { - case MultiplexingResponse::TypeCase::kPrintThreadStackRequest: { - MultiplexingRequest request; - request.mutable_print_thread_stack_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::UNIMPLEMENTED); - request.mutable_print_thread_stack_response()->set_stack_trace( - "Print thread stack trace is not supported by C++ SDK"); - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - client_manager_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); - break; - } + case MultiplexingResponse::TypeCase::kPrintThreadStackRequest: { + MultiplexingRequest request; + request.mutable_print_thread_stack_response()->mutable_common()->mutable_status()->set_code( + google::rpc::Code::UNIMPLEMENTED); + request.mutable_print_thread_stack_response()->set_stack_trace( + "Print thread stack trace is not supported by C++ SDK"); + absl::flat_hash_map metadata; + Signature::sign(this, metadata); + client_manager_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), + std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); + break; + } - case MultiplexingResponse::TypeCase::kVerifyMessageConsumptionRequest: { - auto data = ctx->response.verify_message_consumption_request().message(); - MQMessageExt message; - MultiplexingRequest request; - if (!client_manager_->wrapMessage(data, message)) { - SPDLOG_WARN("Message to verify consumption is corrupted"); + case MultiplexingResponse::TypeCase::kVerifyMessageConsumptionRequest: { + auto data = ctx->response.verify_message_consumption_request().message(); + MQMessageExt message; + MultiplexingRequest request; + if (!client_manager_->wrapMessage(data, message)) { + SPDLOG_WARN("Message to verify consumption is corrupted"); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( + google::rpc::Code::INVALID_ARGUMENT); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message( + "Message to verify is corrupted"); + multiplexing(ctx->remote_address, request); + return; + } + std::string&& result = verifyMessageConsumption(message); request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::INVALID_ARGUMENT); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message( - "Message to verify is corrupted"); + google::rpc::Code::OK); + request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message(result); multiplexing(ctx->remote_address, request); - return; + break; } - std::string&& result = verifyMessageConsumption(message); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::OK); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message(result); - multiplexing(ctx->remote_address, request); - break; - } - case MultiplexingResponse::TypeCase::kResolveOrphanedTransactionRequest: { - auto orphan = ctx->response.resolve_orphaned_transaction_request().orphaned_transactional_message(); - MQMessageExt message; - if (client_manager_->wrapMessage(orphan, message)) { - MessageAccessor::setTargetEndpoint(message, ctx->remote_address); - const std::string& transaction_id = ctx->response.resolve_orphaned_transaction_request().transaction_id(); - resolveOrphanedTransactionalMessage(transaction_id, message); - } else { - SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " - "verification failure."); + case MultiplexingResponse::TypeCase::kResolveOrphanedTransactionRequest: { + auto orphan = ctx->response.resolve_orphaned_transaction_request().orphaned_transactional_message(); + MQMessageExt message; + if (client_manager_->wrapMessage(orphan, message)) { + MessageAccessor::setTargetEndpoint(message, ctx->remote_address); + const std::string& transaction_id = ctx->response.resolve_orphaned_transaction_request().transaction_id(); + resolveOrphanedTransactionalMessage(transaction_id, message); + } else { + SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " + "verification failure."); + } + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; } - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); - break; - } - case MultiplexingResponse::TypeCase::kPollingResponse: { - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); - break; - } + case MultiplexingResponse::TypeCase::kPollingResponse: { + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; + } - default: { - SPDLOG_WARN("Unsupported multiplex type"); - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); - break; - } + default: { + SPDLOG_WARN("Unsupported multiplex type"); + MultiplexingRequest request; + fillGenericPollingRequest(request); + multiplexing(ctx->remote_address, request); + break; + } } } @@ -452,15 +455,15 @@ void ClientImpl::fillGenericPollingRequest(MultiplexingRequest& request) { auto polling_request = request.mutable_polling_request(); polling_request->set_client_id(clientId()); switch (resource_bundle.group_type) { - case GroupType::PUBLISHER: - polling_request->mutable_producer_group()->set_resource_namespace(resource_namespace_); - polling_request->mutable_producer_group()->set_name(group_name_); - break; - - case GroupType::SUBSCRIBER: - polling_request->mutable_consumer_group()->set_resource_namespace(resource_namespace_); - polling_request->mutable_consumer_group()->set_name(group_name_); - break; + case GroupType::PUBLISHER: + polling_request->mutable_producer_group()->set_resource_namespace(resource_namespace_); + polling_request->mutable_producer_group()->set_name(group_name_); + break; + + case GroupType::SUBSCRIBER: + polling_request->mutable_consumer_group()->set_resource_namespace(resource_namespace_); + polling_request->mutable_consumer_group()->set_name(group_name_); + break; } auto topics = polling_request->mutable_topics(); for (const auto& item : resource_bundle.topics) { diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 4a9ef0637..435d8c4ba 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -3,7 +3,7 @@ #include #include -#include "ConsumeMessageService.h" +#include "ConsumeFifoMessageService.h" #include "MessageAccessor.h" #include "ProcessQueue.h" #include "PushConsumerImpl.h" @@ -12,11 +12,11 @@ ROCKETMQ_NAMESPACE_BEGIN ConsumeFifoMessageService ::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener) - : ConsumeMessageService(std::move(consumer), thread_count, message_listener) { + : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { } void ConsumeFifoMessageService::start() { - ConsumeMessageService::start(); + ConsumeMessageServiceBase::start(); State expected = State::STARTING; if (state_.compare_exchange_strong(expected, State::STARTED)) { SPDLOG_DEBUG("ConsumeMessageOrderlyService started"); @@ -32,7 +32,7 @@ void ConsumeFifoMessageService::shutdown() { State expected = State::STARTED; if (state_.compare_exchange_strong(expected, STOPPING)) { - ConsumeMessageService::shutdown(); + ConsumeMessageServiceBase::shutdown(); SPDLOG_INFO("ConsumeMessageOrderlyService shut down"); } } diff --git a/src/main/cpp/rocketmq/ConsumeMessageService.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp similarity index 75% rename from src/main/cpp/rocketmq/ConsumeMessageService.cpp rename to src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp index 8ca682781..468915d54 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp @@ -1,16 +1,17 @@ -#include "ConsumeMessageService.h" +#include "ConsumeMessageServiceBase.h" #include "LoggerImpl.h" #include "PushConsumer.h" #include "ThreadPoolImpl.h" ROCKETMQ_NAMESPACE_BEGIN -ConsumeMessageService::ConsumeMessageService(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener) +ConsumeMessageServiceBase::ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener) : state_(State::CREATED), thread_count_(thread_count), pool_(absl::make_unique(thread_count_)), - consumer_(std::move(consumer)), message_listener_(message_listener) {} + consumer_(std::move(consumer)), message_listener_(message_listener) { +} -void ConsumeMessageService::start() { +void ConsumeMessageServiceBase::start() { State expected = State::CREATED; if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { pool_->start(); @@ -30,20 +31,20 @@ void ConsumeMessageService::start() { } } -void ConsumeMessageService::signalDispatcher() { +void ConsumeMessageServiceBase::signalDispatcher() { absl::MutexLock lk(&dispatch_mtx_); // Wake up dispatch_thread_ dispatch_cv_.Signal(); } -void ConsumeMessageService::throttle(const std::string& topic, uint32_t threshold) { +void ConsumeMessageServiceBase::throttle(const std::string& topic, std::uint32_t threshold) { absl::MutexLock lk(&rate_limiter_table_mtx_); std::shared_ptr> rate_limiter = std::make_shared>(threshold); rate_limiter_table_.insert_or_assign(topic, rate_limiter); rate_limiter_observer_.subscribe(rate_limiter); } -void ConsumeMessageService::shutdown() { +void ConsumeMessageServiceBase::shutdown() { State expected = State::STOPPING; if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { pool_->shutdown(); @@ -60,12 +61,12 @@ void ConsumeMessageService::shutdown() { } } -bool ConsumeMessageService::hasConsumeRateLimiter(const std::string& topic) const { +bool ConsumeMessageServiceBase::hasConsumeRateLimiter(const std::string& topic) const { absl::MutexLock lk(&rate_limiter_table_mtx_); return rate_limiter_table_.contains(topic); } -std::shared_ptr> ConsumeMessageService::rateLimiter(const std::string& topic) const { +std::shared_ptr> ConsumeMessageServiceBase::rateLimiter(const std::string& topic) const { if (!hasConsumeRateLimiter(topic)) { return nullptr; } @@ -73,7 +74,7 @@ std::shared_ptr> ConsumeMessageService::rateLimiter(const std::s return rate_limiter_table_[topic]; } -void ConsumeMessageService::dispatch() { +void ConsumeMessageServiceBase::dispatch() { std::shared_ptr consumer = consumer_.lock(); if (!consumer) { SPDLOG_WARN("The consumer has already destructed"); diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index e73674ea3..2ba308285 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -10,7 +10,7 @@ #include "opencensus/trace/propagation/trace_context.h" #include "opencensus/trace/span.h" -#include "ConsumeMessageService.h" +#include "ConsumeStandardMessageService.h" #include "LoggerImpl.h" #include "MessageAccessor.h" #include "MixAll.h" @@ -26,11 +26,11 @@ ROCKETMQ_NAMESPACE_BEGIN ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener_ptr) - : ConsumeMessageService(std::move(consumer), thread_count, message_listener_ptr) { + : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener_ptr) { } void ConsumeStandardMessageService::start() { - ConsumeMessageService::start(); + ConsumeMessageServiceBase::start(); State expected = State::STARTING; if (state_.compare_exchange_strong(expected, State::STARTED)) { SPDLOG_DEBUG("ConsumeMessageConcurrentlyService started"); @@ -44,7 +44,7 @@ void ConsumeStandardMessageService::shutdown() { State expected = State::STARTED; if (state_.compare_exchange_strong(expected, State::STOPPING)) { - ConsumeMessageService::shutdown(); + ConsumeMessageServiceBase::shutdown(); SPDLOG_DEBUG("ConsumeMessageConcurrentlyService shut down"); } } diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index 3d7992671..0200750c9 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -18,9 +18,12 @@ ROCKETMQ_NAMESPACE_BEGIN StaticCredentialsProvider::StaticCredentialsProvider(std::string access_key, std::string access_secret) - : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)) {} + : access_key_(std::move(access_key)), access_secret_(std::move(access_secret)) { +} -Credentials StaticCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } +Credentials StaticCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY = "ROCKETMQ_ACCESS_KEY"; const char* EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET = "ROCKETMQ_ACCESS_SECRET"; @@ -91,19 +94,28 @@ ConfigFileCredentialsProvider::ConfigFileCredentialsProvider() { } ConfigFileCredentialsProvider::ConfigFileCredentialsProvider(std::string config_file, - std::chrono::milliseconds refresh_interval) {} + std::chrono::milliseconds refresh_interval) { +} -Credentials ConfigFileCredentialsProvider::getCredentials() { return Credentials(access_key_, access_secret_); } +Credentials ConfigFileCredentialsProvider::getCredentials() { + return Credentials(access_key_, access_secret_); +} StsCredentialsProvider::StsCredentialsProvider(std::string ram_role_name) - : impl_(absl::make_unique(std::move(ram_role_name))) {} + : impl_(absl::make_unique(std::move(ram_role_name))) { +} -Credentials StsCredentialsProvider::getCredentials() { return impl_->getCredentials(); } +Credentials StsCredentialsProvider::getCredentials() { + return impl_->getCredentials(); +} StsCredentialsProviderImpl::StsCredentialsProviderImpl(std::string ram_role_name) - : ram_role_name_(std::move(ram_role_name)) {} + : ram_role_name_(std::move(ram_role_name)) { +} -StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { http_client_->shutdown(); } +StsCredentialsProviderImpl::~StsCredentialsProviderImpl() { + http_client_->shutdown(); +} Credentials StsCredentialsProviderImpl::getCredentials() { if (std::chrono::system_clock::now() >= expiration_) { diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index 2780dca4e..bbf6ad86b 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -16,11 +16,16 @@ ROCKETMQ_NAMESPACE_BEGIN DefaultMQProducer::DefaultMQProducer(const std::string& group_name) - : impl_(std::make_shared(group_name)) {} + : impl_(std::make_shared(group_name)) { +} -void DefaultMQProducer::start() { impl_->start(); } +void DefaultMQProducer::start() { + impl_->start(); +} -void DefaultMQProducer::shutdown() { impl_->shutdown(); } +void DefaultMQProducer::shutdown() { + impl_->shutdown(); +} std::chrono::milliseconds DefaultMQProducer::getSendMsgTimeout() const { return absl::ToChronoMilliseconds(impl_->getIoTimeout()); @@ -50,13 +55,21 @@ void DefaultMQProducer::setNameServerListDiscoveryEndpoint(const std::string& di impl_->withNameServerResolver(name_server_resolver); } -void DefaultMQProducer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } +void DefaultMQProducer::setGroupName(const std::string& group_name) { + impl_->setGroupName(group_name); +} -void DefaultMQProducer::setInstanceName(const std::string& instance_name) { impl_->setInstanceName(instance_name); } +void DefaultMQProducer::setInstanceName(const std::string& instance_name) { + impl_->setInstanceName(instance_name); +} -void DefaultMQProducer::enableTracing(bool enabled) { impl_->enableTracing(enabled); } +void DefaultMQProducer::enableTracing(bool enabled) { + impl_->enableTracing(enabled); +} -bool DefaultMQProducer::isTracingEnabled() { return impl_->isTracingEnabled(); } +bool DefaultMQProducer::isTracingEnabled() { + return impl_->isTracingEnabled(); +} SendResult DefaultMQProducer::send(const MQMessage& message, bool filter_active_broker) { std::error_code ec; @@ -167,9 +180,13 @@ void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateChe impl_->setLocalTransactionStateChecker(std::move(checker)); } -void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { impl_->maxAttemptTimes(max_attempt_times); } +void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { + impl_->maxAttemptTimes(max_attempt_times); +} -int DefaultMQProducer::getMaxAttemptTimes() const { return impl_->maxAttemptTimes(); } +int DefaultMQProducer::getMaxAttemptTimes() const { + return impl_->maxAttemptTimes(); +} std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { std::error_code ec; @@ -180,13 +197,21 @@ std::vector DefaultMQProducer::getTopicMessageQueueInfo(const st return std::move(list); } -void DefaultMQProducer::setUnitName(std::string unit_name) { impl_->setUnitName(std::move(unit_name)); } +void DefaultMQProducer::setUnitName(std::string unit_name) { + impl_->setUnitName(std::move(unit_name)); +} -const std::string& DefaultMQProducer::getUnitName() { return impl_->getUnitName(); } +const std::string& DefaultMQProducer::getUnitName() { + return impl_->getUnitName(); +} -uint32_t DefaultMQProducer::compressBodyThreshold() const { return impl_->compressBodyThreshold(); } +uint32_t DefaultMQProducer::compressBodyThreshold() const { + return impl_->compressBodyThreshold(); +} -void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { impl_->compressBodyThreshold(threshold); } +void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { + impl_->compressBodyThreshold(threshold); +} void DefaultMQProducer::setResourceNamespace(const std::string& resource_namespace) { impl_->resourceNamespace(resource_namespace); @@ -196,7 +221,9 @@ void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credential impl_->setCredentialsProvider(std::move(credentials_provider)); } -void DefaultMQProducer::setRegion(const std::string& region) { impl_->region(region); } +void DefaultMQProducer::setRegion(const std::string& region) { + impl_->region(region); +} TransactionPtr DefaultMQProducer::prepare(MQMessage& message) { std::error_code ec; diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index f46d02ab1..8f636259d 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -12,17 +12,24 @@ ROCKETMQ_NAMESPACE_BEGIN DefaultMQPullConsumer::DefaultMQPullConsumer(const std::string& group_name) - : impl_(std::make_shared(group_name)) {} + : impl_(std::make_shared(group_name)) { +} -void DefaultMQPullConsumer::start() { impl_->start(); } +void DefaultMQPullConsumer::start() { + impl_->start(); +} -void DefaultMQPullConsumer::shutdown() { impl_->shutdown(); } +void DefaultMQPullConsumer::shutdown() { + impl_->shutdown(); +} std::future> DefaultMQPullConsumer::queuesFor(const std::string& topic) { return impl_->queuesFor(topic); } -std::future DefaultMQPullConsumer::queryOffset(const OffsetQuery& query) { return impl_->queryOffset(query); } +std::future DefaultMQPullConsumer::queryOffset(const OffsetQuery& query) { + return impl_->queryOffset(query); +} bool DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullResult& pull_result) { auto callback = absl::make_unique(pull_result); diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index 744a57865..6cb629137 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -12,7 +12,9 @@ DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) { impl_ = std::make_shared(group_name); } -void DefaultMQPushConsumer::start() { impl_->start(); } +void DefaultMQPushConsumer::start() { + impl_->start(); +} void DefaultMQPushConsumer::shutdown() { impl_->shutdown(); @@ -24,7 +26,9 @@ void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::strin impl_->subscribe(topic, expression, expression_type); } -void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { impl_->setConsumeFromWhere(policy); } +void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { + impl_->setConsumeFromWhere(policy); +} void DefaultMQPushConsumer::registerMessageListener(MessageListener* listener) { impl_->registerMessageListener(listener); @@ -44,27 +48,48 @@ void DefaultMQPushConsumer::setNameServerListDiscoveryEndpoint(const std::string impl_->withNameServerResolver(name_server_resolver); } -void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { impl_->setGroupName(group_name); } +void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { + impl_->setGroupName(group_name); +} -void DefaultMQPushConsumer::setConsumeThreadCount(int thread_count) { impl_->consumeThreadPoolSize(thread_count); } +void DefaultMQPushConsumer::setConsumeThreadCount(int thread_count) { + impl_->consumeThreadPoolSize(thread_count); +} -void DefaultMQPushConsumer::setInstanceName(const std::string& instance_name) { impl_->setInstanceName(instance_name); } +void DefaultMQPushConsumer::setInstanceName(const std::string& instance_name) { + impl_->setInstanceName(instance_name); +} -int DefaultMQPushConsumer::getProcessQueueTableSize() { return impl_->getProcessQueueTableSize(); } +int DefaultMQPushConsumer::getProcessQueueTableSize() { + return impl_->getProcessQueueTableSize(); +} -void DefaultMQPushConsumer::setUnitName(std::string unit_name) { impl_->setUnitName(std::move(unit_name)); } +void DefaultMQPushConsumer::setUnitName(std::string unit_name) { + impl_->setUnitName(std::move(unit_name)); +} -const std::string& DefaultMQPushConsumer::getUnitName() const { return impl_->getUnitName(); } +const std::string& DefaultMQPushConsumer::getUnitName() const { + return impl_->getUnitName(); +} -void DefaultMQPushConsumer::enableTracing(bool enabled) { impl_->enableTracing(enabled); } +void DefaultMQPushConsumer::enableTracing(bool enabled) { + impl_->enableTracing(enabled); +} -bool DefaultMQPushConsumer::isTracingEnabled() { return impl_->isTracingEnabled(); } +bool DefaultMQPushConsumer::isTracingEnabled() { + return impl_->isTracingEnabled(); +} -void DefaultMQPushConsumer::setAsyncPull(bool) {} +void DefaultMQPushConsumer::setAsyncPull(bool) { +} -void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { impl_->consumeBatchSize(batch_size); } +void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { + impl_->consumeBatchSize(batch_size); +} -void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { impl_->setCustomExecutor(executor); } +void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { + impl_->setCustomExecutor(executor); +} void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t threshold) { impl_->setThrottle(topic, threshold); @@ -78,8 +103,12 @@ void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr creden impl_->setCredentialsProvider(std::move(credentials_provider)); } -void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { impl_->setMessageModel(message_model); } +void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { + impl_->setMessageModel(message_model); +} -std::string DefaultMQPushConsumer::groupName() const { return impl_->getGroupName(); } +std::string DefaultMQPushConsumer::groupName() const { + return impl_->getGroupName(); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp index df1fb2b8f..0d591c361 100644 --- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -106,7 +106,9 @@ void DynamicNameServerResolver::start() { std::chrono::milliseconds(0), refresh_interval_); } -void DynamicNameServerResolver::shutdown() { scheduler_->shutdown(); } +void DynamicNameServerResolver::shutdown() { + scheduler_->shutdown(); +} std::string DynamicNameServerResolver::current() { absl::MutexLock lk(&name_server_list_mtx_); diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/rocketmq/FilterExpression.cpp index 43207e929..de1f95a65 100644 --- a/src/main/cpp/rocketmq/FilterExpression.cpp +++ b/src/main/cpp/rocketmq/FilterExpression.cpp @@ -5,18 +5,18 @@ ROCKETMQ_NAMESPACE_BEGIN bool FilterExpression::accept(const MQMessageExt& message) const { switch (type_) { - case ExpressionType::TAG: { - if (WILD_CARD_TAG == content_) { - return true; - } else { - return message.getTags() == content_; + case ExpressionType::TAG: { + if (WILD_CARD_TAG == content_) { + return true; + } else { + return message.getTags() == content_; + } } - } - case ExpressionType::SQL92: { - // Server should have strictly filtered. - return true; - } + case ExpressionType::SQL92: { + // Server should have strictly filtered. + return true; + } } return true; } diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp index f7e1c2223..8b97e3139 100644 --- a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -1,12 +1,13 @@ #include "MessageGroupQueueSelector.h" -#include #include +#include ROCKETMQ_NAMESPACE_BEGIN MessageGroupQueueSelector::MessageGroupQueueSelector(std::string message_group) - : message_group_(std::move(message_group)) {} + : message_group_(std::move(message_group)) { +} MQMessageQueue MessageGroupQueueSelector::select(const std::vector& mqs, const MQMessage& msg, void* arg) { diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index adfe10970..f445bbc78 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -11,6 +11,8 @@ #include "Protocol.h" #include "PushConsumer.h" #include "Signature.h" +#include "rocketmq/MessageListener.h" +#include "rocketmq/MessageModel.h" using namespace std::chrono; @@ -76,14 +78,15 @@ void ProcessQueueImpl::receiveMessage() { return; } - auto policy = consumer->receiveMessageAction(); - switch (policy) { - case ReceiveMessageAction::POLLING: + switch (consumer->messageModel()) { + case MessageModel::CLUSTERING: { popMessage(); break; - case ReceiveMessageAction::PULL: + } + case MessageModel::BROADCASTING: { pullMessage(); break; + } } } @@ -121,60 +124,8 @@ void ProcessQueueImpl::pullMessage() { auto timeout = consumer->getLongPollingTimeout(); - auto callback = [this](const InvocationContext* invocation_context) { - auto status = invocation_context->status; - if (status.ok()) { - const auto& common = invocation_context->response.common(); - - switch (common.status().code()) { - case google::rpc::Code::OK: { - ReceiveMessageResult result; - client_manager_->processPullResult(invocation_context->context, invocation_context->response, result, - invocation_context->remote_address); - receive_callback_->onSuccess(result); - } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); - std::error_code ec = ErrorCode::Forbidden; - receive_callback_->onFailure(ec); - } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); - std::error_code ec = ErrorCode::Unauthorized; - receive_callback_->onFailure(ec); - } break; - case google::rpc::Code::DEADLINE_EXCEEDED: { - SPDLOG_WARN("DeadlineExceeded: {}", common.status().message()); - std::error_code ec = ErrorCode::GatewayTimeout; - receive_callback_->onFailure(ec); - } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); - std::error_code ec = ErrorCode::BadRequest; - receive_callback_->onFailure(ec); - } break; - case google::rpc::Code::FAILED_PRECONDITION: { - SPDLOG_WARN("FailedPrecondition: {}", common.status().message()); - std::error_code ec = ErrorCode::PreconditionRequired; - receive_callback_->onFailure(ec); - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); - std::error_code ec = ErrorCode::InternalServerError; - receive_callback_->onFailure(ec); - } break; - default: { - SPDLOG_WARN("Unimplemented: Please upgrade to use latest SDK release"); - std::error_code ec = ErrorCode::NotImplemented; - receive_callback_->onFailure(ec); - } break; - } - } else { - SPDLOG_WARN("Failed to receive valid gRPC response from server. gRPC-status[code={}, message={}]", - status.error_code(), status.error_message()); - std::error_code ec = ErrorCode::RequestTimeout; - receive_callback_->onFailure(ec); - } + auto callback = [this](const std::error_code& ec, const ReceiveMessageResult& result) { + receive_callback_->onCompletion(ec, result); }; client_manager_->pullMessage(message_queue_.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(timeout), @@ -311,11 +262,23 @@ void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_mapset_name(consumer->getGroupName()); request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); wrapFilterExpression(request.mutable_filter_expression()); + switch (consumer->getConsumeMessageService()->messageListenerType()) { + case MessageListenerType::STANDARD: { + request.set_fifo_flag(false); + break; + } + case MessageListenerType::FIFO: { + request.set_fifo_flag(true); + break; + } + } + // Batch size request.set_batch_size(consumer->receiveBatchSize()); @@ -337,6 +300,7 @@ void ProcessQueueImpl::wrapPullMessageRequest(rmq::PullMessageRequest& request) request.mutable_group()->set_name(consumer->getGroupName()); request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); request.mutable_partition()->set_id(message_queue_.getQueueId()); + request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); request.set_offset(next_offset_); diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 85f534433..99a8c232b 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -34,12 +34,17 @@ ProducerImpl::ProducerImpl(absl::string_view group_name) // TODO: initialize client_config_ and fault_strategy_ } -ProducerImpl::~ProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); } +ProducerImpl::~ProducerImpl() { + SPDLOG_INFO("Producer instance is destructed"); +} void ProducerImpl::start() { ClientImpl::start(); - if (State::STARTED != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected producer state: {}", state_.load(std::memory_order_relaxed)); + + State expecting = State::STARTING; + if (!state_.compare_exchange_strong(expecting, State::STARTED)) { + SPDLOG_ERROR("Start with unexpected state. Expecting: {}, Actual: {}", State::STARTING, + state_.load(std::memory_order_relaxed)); return; } @@ -47,15 +52,21 @@ void ProducerImpl::start() { } void ProducerImpl::shutdown() { - ClientImpl::shutdown(); - - State expected = State::STOPPING; - if (state_.compare_exchange_strong(expected, State::STOPPED)) { - SPDLOG_INFO("DefaultMQProducerImpl stopped"); + State expected = State::STARTED; + if (!state_.compare_exchange_strong(expected, State::STOPPING)) { + SPDLOG_ERROR("Shutdown with unexpected state. Expecting: {}, Actual: {}", State::STOPPING, + state_.load(std::memory_order_relaxed)); + return; } + + ClientImpl::shutdown(); + assert(State::STOPPED == state_.load()); + SPDLOG_INFO("Producer instance stopped"); } -bool ProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } +bool ProducerImpl::isRunning() const { + return State::STARTED == state_.load(std::memory_order_relaxed); +} void ProducerImpl::ensureRunning(std::error_code& ec) const noexcept { if (!isRunning()) { @@ -63,7 +74,9 @@ void ProducerImpl::ensureRunning(std::error_code& ec) const noexcept { } } -bool ProducerImpl::validate(const MQMessage& message) { return MixAll::validate(message); } +bool ProducerImpl::validate(const MQMessage& message) { + return MixAll::validate(message); +} std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, const MQMessageQueue& message_queue) { @@ -84,18 +97,18 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM system_attribute->mutable_producer_group()->set_name(group_name_); switch (message.messageType()) { - case MessageType::NORMAL: - system_attribute->set_message_type(rmq::MessageType::NORMAL); - break; - case MessageType::DELAY: - system_attribute->set_message_type(rmq::MessageType::DELAY); - break; - case MessageType::TRANSACTION: - system_attribute->set_message_type(rmq::MessageType::TRANSACTION); - break; - case MessageType::FIFO: - system_attribute->set_message_type(rmq::MessageType::FIFO); - break; + case MessageType::NORMAL: + system_attribute->set_message_type(rmq::MessageType::NORMAL); + break; + case MessageType::DELAY: + system_attribute->set_message_type(rmq::MessageType::DELAY); + break; + case MessageType::TRANSACTION: + system_attribute->set_message_type(rmq::MessageType::TRANSACTION); + break; + case MessageType::FIFO: + system_attribute->set_message_type(rmq::MessageType::FIFO); + break; } if (message.bodyLength() >= compress_body_threshold_) { @@ -223,7 +236,9 @@ void ProducerImpl::send(const MQMessage& message, SendCallback* cb) { asyncPublishInfo(message.getTopic(), callback); } -void ProducerImpl::sendOneway(const MQMessage& message, std::error_code& ec) { send(message, ec); } +void ProducerImpl::sendOneway(const MQMessage& message, std::error_code& ec) { + send(message, ec); +} void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { transaction_state_checker_ = std::move(checker); @@ -317,14 +332,14 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& request.set_transaction_id(transaction_id); std::string action; switch (resolution) { - case TransactionState::COMMIT: - request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); - action = "commit"; - break; - case TransactionState::ROLLBACK: - request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); - action = "rollback"; - break; + case TransactionState::COMMIT: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); + action = "commit"; + break; + case TransactionState::ROLLBACK: + request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); + action = "rollback"; + break; } absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -346,12 +361,12 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message_id); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); switch (resolution) { - case TransactionState::COMMIT: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "commit"); - break; - case TransactionState::ROLLBACK: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "rollback"); - break; + case TransactionState::COMMIT: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "commit"); + break; + case TransactionState::ROLLBACK: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "rollback"); + break; } absl::Mutex mtx; diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index daec94349..7bb747e0a 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -6,6 +6,7 @@ #include "rocketmq/ErrorCode.h" #include "rocketmq/MQClientException.h" #include "rocketmq/MessageModel.h" +#include "rocketmq/PullResult.h" #include #include @@ -69,19 +70,19 @@ std::future> PullConsumerImpl::queuesFor(const std:: std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { QueryOffsetRequest request; switch (query.policy) { - case QueryOffsetPolicy::BEGINNING: - request.set_policy(rmq::QueryOffsetPolicy::BEGINNING); - break; - case QueryOffsetPolicy::END: - request.set_policy(rmq::QueryOffsetPolicy::END); - break; - case QueryOffsetPolicy::TIME_POINT: - request.set_policy(rmq::QueryOffsetPolicy::TIME_POINT); - auto duration = absl::FromChrono(query.time_point.time_since_epoch()); - int64_t seconds = absl::ToInt64Seconds(duration); - request.mutable_time_point()->set_seconds(seconds); - request.mutable_time_point()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); - break; + case QueryOffsetPolicy::BEGINNING: + request.set_policy(rmq::QueryOffsetPolicy::BEGINNING); + break; + case QueryOffsetPolicy::END: + request.set_policy(rmq::QueryOffsetPolicy::END); + break; + case QueryOffsetPolicy::TIME_POINT: + request.set_policy(rmq::QueryOffsetPolicy::TIME_POINT); + auto duration = absl::FromChrono(query.time_point.time_since_epoch()); + int64_t seconds = absl::ToInt64Seconds(duration); + request.mutable_time_point()->set_seconds(seconds); + request.mutable_time_point()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + break; } request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); @@ -127,65 +128,14 @@ void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { std::string target_host = query.message_queue.serviceAddress(); assert(!target_host.empty()); - auto callback = [this, target_host, cb](const InvocationContext* invocation_context) { - if (!invocation_context || !invocation_context->status.ok()) { - std::error_code ec = ErrorCode::RequestTimeout; + auto callback = [target_host, cb](const std::error_code& ec, const ReceiveMessageResult& result) { + if (ec) { cb->onFailure(ec); return; } - auto response = invocation_context->response; - auto biz_status = response.common().status(); - - switch (biz_status.code()) { - case google::rpc::Code::OK: { - std::vector messages; - for (const auto& item : response.messages()) { - MQMessageExt message_ext; - if (client_manager_->wrapMessage(item, message_ext)) { - messages.emplace_back(message_ext); - } - } - PullResult pull_result(response.min_offset(), response.max_offset(), response.next_offset(), std::move(messages)); - cb->onSuccess(pull_result); - } break; - - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", response.common().status().message()); - std::error_code ec = ErrorCode::Forbidden; - cb->onFailure(ec); - } break; - - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", response.common().status().message()); - std::error_code ec = ErrorCode::Unauthorized; - cb->onFailure(ec); - } break; - - case google::rpc::Code::DEADLINE_EXCEEDED: { - SPDLOG_WARN("GatewayTimeout: {}", response.common().status().message()); - std::error_code ec = ErrorCode::GatewayTimeout; - cb->onFailure(ec); - } break; - - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("BadRequest: {}", response.common().status().message()); - std::error_code ec = ErrorCode::BadRequest; - cb->onFailure(ec); - } break; - - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("ServerIntervalError: {}", response.common().status().message()); - std::error_code ec = ErrorCode::InternalServerError; - cb->onFailure(ec); - } break; - - default: { - SPDLOG_WARN("Unsupported response code. Please upgrade to lastest SDK"); - std::error_code ec = ErrorCode::NotImplemented; - cb->onFailure(ec); - } break; - } + PullResult pull_result(result.min_offset, result.max_offset, result.next_offset, result.messages); + cb->onSuccess(pull_result); }; absl::flat_hash_map metadata; @@ -201,12 +151,12 @@ void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); consumer_data->mutable_group()->set_name(group_name_); switch (message_model_) { - case MessageModel::BROADCASTING: - consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); - break; - case MessageModel::CLUSTERING: - consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); - break; + case MessageModel::BROADCASTING: + consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); + break; + case MessageModel::CLUSTERING: + consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); + break; } } diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index c0508c181..9927f923c 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -1,5 +1,6 @@ #include "PushConsumerImpl.h" +#include #include #include #include @@ -9,12 +10,15 @@ #include "AsyncReceiveMessageCallback.h" #include "ClientManagerFactory.h" +#include "ConsumeFifoMessageService.h" +#include "ConsumeStandardMessageService.h" #include "MessageAccessor.h" #include "MixAll.h" #include "ProcessQueueImpl.h" #include "RpcClient.h" #include "Signature.h" #include "rocketmq/MQClientException.h" +#include "rocketmq/MessageListener.h" #include "rocketmq/MessageModel.h" ROCKETMQ_NAMESPACE_BEGIN @@ -29,54 +33,47 @@ PushConsumerImpl::~PushConsumerImpl() { void PushConsumerImpl::start() { ClientImpl::start(); - if (State::STARTED != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected consumer state: {}", state_.load(std::memory_order_relaxed)); + State expecting = State::STARTING; + if (!state_.compare_exchange_strong(expecting, State::STARTED)) { + SPDLOG_ERROR("Unexpected consumer state. Expecting: {}, Actual: {}", State::STARTING, + state_.load(std::memory_order_relaxed)); return; } if (!message_listener_) { - SPDLOG_ERROR("Message listener is nullptr"); + SPDLOG_ERROR("Required message listener is nullptr"); abort(); return; } - if (MessageListenerType::STANDARD == message_listener_->listenerType() && - MessageModel::CLUSTERING == message_model_) { - receive_message_policy_ = ReceiveMessageAction::POLLING; - } else { - receive_message_policy_ = ReceiveMessageAction::PULL; - } - client_manager_->addClientObserver(shared_from_this()); fetchRoutes(); - heartbeat(); - if (message_listener_) { - if (message_listener_->listenerType() == MessageListenerType::FIFO) { - SPDLOG_INFO("start orderly consume service: {}", group_name_); - consume_message_service_ = - std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); - consume_batch_size_ = 1; - } else { - // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. - SPDLOG_INFO("start concurrently consume service: {}", group_name_); - consume_message_service_ = std::make_shared( - shared_from_this(), consume_thread_pool_size_, message_listener_); - } - consume_message_service_->start(); + if (message_listener_->listenerType() == MessageListenerType::FIFO) { + SPDLOG_INFO("start orderly consume service: {}", group_name_); + consume_message_service_ = + std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); + consume_batch_size_ = 1; + } else { + // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. + SPDLOG_INFO("start concurrently consume service: {}", group_name_); + consume_message_service_ = std::make_shared( + shared_from_this(), consume_thread_pool_size_, message_listener_); + } + consume_message_service_->start(); - { - // Set consumer throttling - absl::MutexLock lock(&throttle_table_mtx_); - for (const auto& item : throttle_table_) { - consume_message_service_->throttle(item.first, item.second); - } + { + // Set consumer throttling + absl::MutexLock lock(&throttle_table_mtx_); + for (const auto& item : throttle_table_) { + consume_message_service_->throttle(item.first, item.second); } - } else { - SPDLOG_WARN("Message listener is unexpected nullptr"); } + // Heartbeat depends on initialization of consume-message-service + heartbeat(); + std::weak_ptr consumer_weak_ptr(shared_from_this()); auto scan_assignment_functor = [consumer_weak_ptr]() { std::shared_ptr consumer = consumer_weak_ptr.lock(); @@ -88,33 +85,35 @@ void PushConsumerImpl::start() { scan_assignment_handle_ = client_manager_->getScheduler().schedule( scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); - state_.store(State::STARTED, std::memory_order_relaxed); SPDLOG_INFO("PushConsumer started, groupName={}", group_name_); } const char* PushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; void PushConsumerImpl::shutdown() { + State expecting = State::STARTED; + if (state_.compare_exchange_strong(expecting, State::STOPPING)) { + if (scan_assignment_handle_) { + client_manager_->getScheduler().cancel(scan_assignment_handle_); + SPDLOG_DEBUG("Scan assignment periodic task cancelled"); + } - if (scan_assignment_handle_) { - client_manager_->getScheduler().cancel(scan_assignment_handle_); - SPDLOG_DEBUG("Scan assignment periodic task cancelled"); - } + { + absl::MutexLock lock(&process_queue_table_mtx_); + process_queue_table_.clear(); + } - { - absl::MutexLock lock(&process_queue_table_mtx_); - process_queue_table_.clear(); - } + if (consume_message_service_) { + consume_message_service_->shutdown(); + } - if (consume_message_service_) { - consume_message_service_->shutdown(); - } + // Shutdown services started by parent + ClientImpl::shutdown(); - // Shutdown services started by parent - ClientImpl::shutdown(); - State expected = State::STOPPING; - if (state_.compare_exchange_strong(expected, State::STOPPED)) { SPDLOG_INFO("PushConsumerImpl stopped"); + } else { + SPDLOG_ERROR("Shutdown with unexpected state. Expecting: {}, Actual: {}", State::STARTED, + state_.load(std::memory_order_relaxed)); } } @@ -354,8 +353,8 @@ bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const return false; } - switch (receive_message_policy_) { - case ReceiveMessageAction::PULL: { + switch (message_model_) { + case MessageModel::BROADCASTING: { int64_t offset = -1; if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { // Query latest offset from server. @@ -382,7 +381,7 @@ bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const } break; } - case ReceiveMessageAction::POLLING: + case MessageModel::CLUSTERING: process_queue_ptr->receiveMessage(); break; } @@ -601,6 +600,19 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { subscriptions->AddAllocated(subscription); } } + + assert(consume_message_service_); + switch (consume_message_service_->messageListenerType()) { + case MessageListenerType::FIFO: { + // TODO: Use enumeration in the protocol buffer specification? + request.set_fifo_flag(true); + break; + } + case MessageListenerType::STANDARD: { + request.set_fifo_flag(false); + break; + } + } } ClientResourceBundle PushConsumerImpl::resourceBundle() { diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp index 6e156be1c..095fd5734 100644 --- a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -7,7 +7,8 @@ ROCKETMQ_NAMESPACE_BEGIN StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) - : name_server_list_(absl::StrSplit(name_server_list, ';')) {} + : name_server_list_(absl::StrSplit(name_server_list, ';')) { +} std::string StaticNameServerResolver::current() { std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size(); @@ -19,6 +20,8 @@ std::string StaticNameServerResolver::next() { return current(); } -std::vector StaticNameServerResolver::resolve() { return name_server_list_; } +std::vector StaticNameServerResolver::resolve() { + return name_server_list_; +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 4a4a65acc..75c0c5fa4 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -21,8 +21,12 @@ bool TransactionImpl::rollback() { return producer->rollback(message_id_, transaction_id_, trace_context_, endpoint_); } -std::string TransactionImpl::messageId() const { return message_id_; } +std::string TransactionImpl::messageId() const { + return message_id_; +} -std::string TransactionImpl::transactionId() const { return transaction_id_; } +std::string TransactionImpl::transactionId() const { + return transaction_id_; +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h index 597819a71..006ba11cd 100644 --- a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -1,21 +1,23 @@ #pragma once +#include +#include + #include "ProcessQueue.h" #include "ReceiveMessageCallback.h" -#include ROCKETMQ_NAMESPACE_BEGIN -class AsyncReceiveMessageCallback : public ReceiveMessageCallback, - public std::enable_shared_from_this { +class AsyncReceiveMessageCallback + : public ReceiveMessageCallback, + public std::enable_shared_from_this { public: explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); ~AsyncReceiveMessageCallback() override = default; - void onSuccess(ReceiveMessageResult& result) override; - - void onFailure(const std::error_code& ec) override; + void onCompletion(const std::error_code &ec, + const ReceiveMessageResult &result) override; void receiveMessageLater(); @@ -32,7 +34,7 @@ class AsyncReceiveMessageCallback : public ReceiveMessageCallback, void checkThrottleThenReceive(); - static const char* RECEIVE_LATER_TASK_NAME; + static const char *RECEIVE_LATER_TASK_NAME; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index 971bb6eda..d7596f034 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -10,7 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN class AwaitPullCallback : public PullCallback { public: - explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) {} + explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) { + } void onSuccess(const PullResult& pull_result) noexcept override; @@ -18,11 +19,17 @@ class AwaitPullCallback : public PullCallback { bool await(); - bool hasFailure() const { return ec_.operator bool(); } + bool hasFailure() const { + return ec_.operator bool(); + } - bool isCompleted() const { return completed_; } + bool isCompleted() const { + return completed_; + } - const std::error_code& errorCode() const noexcept { return ec_; } + const std::error_code& errorCode() const noexcept { + return ec_; + } private: PullResult& pull_result_; diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 342bdcc0b..3de7452c5 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -58,6 +59,13 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { name_server_resolver_ = std::move(name_server_resolver); } + /** + * Expose for test purpose only. + */ + void state(State state) { + state_.store(state, std::memory_order_relaxed); + } + protected: ClientManagerPtr client_manager_; std::shared_ptr exporter_; @@ -90,9 +98,12 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; - virtual std::string verifyMessageConsumption(const MQMessageExt& message) { return "Unsupported"; } + virtual std::string verifyMessageConsumption(const MQMessageExt& message) { + return "Unsupported"; + } - virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) {} + virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { + } /** * Concrete publisher/subscriber client is expected to fill other diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h index a4be5d214..ac062ca61 100644 --- a/src/main/cpp/rocketmq/include/ClientResourceBundle.h +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -5,8 +5,9 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class GroupType : int8_t { - PUBLISHER = 0, +enum class GroupType : int8_t +{ + PUBLISHER = 0, SUBSCRIBER = 1, }; diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h new file mode 100644 index 000000000..3ac6b1267 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -0,0 +1,34 @@ +#include "ConsumeMessageServiceBase.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeFifoMessageService : public ConsumeMessageServiceBase, + public std::enable_shared_from_this { +public: + ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + void start() override; + + void shutdown() override; + + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; + + MessageListenerType messageListenerType() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message); + + void submitConsumeTask0(const std::shared_ptr& consumer, const ProcessQueueWeakPtr& process_queue, + const MQMessageExt& message); + + void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, const std::error_code& ec); + + void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + + void onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + + void scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index de4e62fd3..acf6cfae7 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -1,136 +1,34 @@ #pragma once -#include -#include -#include -#include +#include #include "ProcessQueue.h" -#include "RateLimiter.h" -#include "ThreadPool.h" -#include "absl/container/flat_hash_map.h" #include "rocketmq/MessageListener.h" -#include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN -class PushConsumer; - class ConsumeMessageService { public: - ConsumeMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); - virtual ~ConsumeMessageService() = default; - /** - * Make it noncopyable. - */ - ConsumeMessageService(const ConsumeMessageService& other) = delete; - ConsumeMessageService& operator=(const ConsumeMessageService& other) = delete; - /** * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable * functor. */ - virtual void start(); + virtual void start() = 0; /** * Stop the dispatcher thread and then reset the thread pool. */ - virtual void shutdown(); + virtual void shutdown() = 0; virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr) = 0; virtual MessageListenerType messageListenerType() = 0; - /** - * Signal dispatcher thread to check new pending messages. - */ - void signalDispatcher(); - - /** - * Set throttle threshold per topic. - * - * @param topic - * @param threshold - */ - void throttle(const std::string& topic, uint32_t threshold); - - bool hasConsumeRateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); - - std::shared_ptr> rateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); - -protected: - RateLimiterObserver rate_limiter_observer_; - - mutable absl::flat_hash_map>> - rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); - mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ - - std::atomic state_; - - int thread_count_; - std::unique_ptr pool_; - std::weak_ptr consumer_; - - absl::Mutex dispatch_mtx_; - std::thread dispatch_thread_; - absl::CondVar dispatch_cv_; - - MessageListener* message_listener_; - - /** - * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. - */ - void dispatch(); -}; - -class ConsumeStandardMessageService : public ConsumeMessageService { -public: - ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener_ptr); - - ~ConsumeStandardMessageService() override = default; - - void start() override; - - void shutdown() override; - - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - - MessageListenerType messageListenerType() override; - -private: - void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); -}; - -class ConsumeFifoMessageService : public ConsumeMessageService, - public std::enable_shared_from_this { -public: - ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); - void start() override; - - void shutdown() override; - - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - - MessageListenerType messageListenerType() override; - -private: - void consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message); - - void submitConsumeTask0(const std::shared_ptr& consumer, const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message); - - void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); - - void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, const std::error_code& ec); - - void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); - - void onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + virtual void signalDispatcher() = 0; - void scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + virtual void throttle(const std::string& topic, std::uint32_t threshold) = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h new file mode 100644 index 000000000..3b883d6dd --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" + +#include "ConsumeMessageService.h" +#include "RateLimiter.h" +#include "ThreadPool.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumer; + +class ConsumeMessageServiceBase : public ConsumeMessageService { +public: + ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + + ~ConsumeMessageServiceBase() override = default; + + /** + * Make it noncopyable. + */ + ConsumeMessageServiceBase(const ConsumeMessageServiceBase& other) = delete; + ConsumeMessageServiceBase& operator=(const ConsumeMessageServiceBase& other) = delete; + + /** + * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable + * functor. + */ + void start() override; + + /** + * Stop the dispatcher thread and then reset the thread pool. + */ + void shutdown() override; + + /** + * Signal dispatcher thread to check new pending messages. + */ + void signalDispatcher() override; + + /** + * Set throttle threshold per topic. + * + * @param topic + * @param threshold + */ + void throttle(const std::string& topic, std::uint32_t threshold) override; + + bool hasConsumeRateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); + + std::shared_ptr> rateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); + +protected: + RateLimiterObserver rate_limiter_observer_; + + mutable absl::flat_hash_map>> + rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); + mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ + + std::atomic state_; + + int thread_count_; + std::unique_ptr pool_; + std::weak_ptr consumer_; + + absl::Mutex dispatch_mtx_; + std::thread dispatch_thread_; + absl::CondVar dispatch_cv_; + + MessageListener* message_listener_; + + /** + * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. + */ + void dispatch(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h new file mode 100644 index 000000000..d7517ed0a --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h @@ -0,0 +1,26 @@ +#pragma once + +#include "ConsumeMessageServiceBase.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeStandardMessageService : public ConsumeMessageServiceBase { +public: + ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener* message_listener_ptr); + + ~ConsumeStandardMessageService() override = default; + + void start() override; + + void shutdown() override; + + void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; + + MessageListenerType messageListenerType() override; + +private: + void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index 20751b8bf..ee73e510c 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -4,8 +4,8 @@ #include "absl/types/optional.h" #include "Client.h" +#include "ConsumeMessageService.h" #include "FilterExpression.h" -#include "ReceiveMessageAction.h" ROCKETMQ_NAMESPACE_BEGIN @@ -13,8 +13,7 @@ class Consumer : virtual public Client { public: ~Consumer() override = default; - virtual absl::optional - getFilterExpression(const std::string &topic) const = 0; + virtual absl::optional getFilterExpression(const std::string& topic) const = 0; virtual uint32_t maxCachedMessageQuantity() const = 0; @@ -22,7 +21,7 @@ class Consumer : virtual public Client { virtual int32_t receiveBatchSize() const = 0; - virtual ReceiveMessageAction receiveMessageAction() const = 0; + virtual std::shared_ptr getConsumeMessageService() = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h index 3c4e3cca3..7be3b9c69 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -2,8 +2,8 @@ #include -#include "rocketmq/MQMessageExt.h" #include "rocketmq/ExpressionType.h" +#include "rocketmq/MQMessageExt.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index c22acbec5..018f9be4d 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -25,8 +25,10 @@ ROCKETMQ_NAMESPACE_BEGIN struct OffsetRecord { - explicit OffsetRecord(int64_t offset) : offset_(offset), released_(false) {} - OffsetRecord(int64_t offset, bool released) : offset_(offset), released_(released) {} + explicit OffsetRecord(int64_t offset) : offset_(offset), released_(false) { + } + OffsetRecord(int64_t offset, bool released) : offset_(offset), released_(released) { + } int64_t offset_; bool released_; }; @@ -35,7 +37,8 @@ ROCKETMQ_NAMESPACE_END namespace std { -template <> struct less { +template <> +struct less { bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) const { return lhs.offset_ < rhs.offset_; } @@ -76,9 +79,13 @@ class ProcessQueueImpl : virtual public ProcessQueue { void receiveMessage() override; - const std::string& simpleName() const override { return simple_name_; } + const std::string& simpleName() const override { + return simple_name_; + } - std::string topic() const override { return message_queue_.getTopic(); } + std::string topic() const override { + return message_queue_.getTopic(); + } bool hasPendingMessages() const override LOCKS_EXCLUDED(messages_mtx_); @@ -105,14 +112,18 @@ class ProcessQueueImpl : virtual public ProcessQueue { */ bool take(uint32_t batch_size, std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); - void syncIdleState() override { idle_since_ = std::chrono::steady_clock::now(); } + void syncIdleState() override { + idle_since_ = std::chrono::steady_clock::now(); + } void nextOffset(int64_t next_offset) override { assert(next_offset >= 0); next_offset_ = next_offset; } - int64_t nextOffset() const { return next_offset_; } + int64_t nextOffset() const { + return next_offset_; + } bool committedOffset(int64_t& offset) override LOCKS_EXCLUDED(offsets_mtx_); diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index 263b11f5a..8d03e3591 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -65,19 +65,31 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t */ void isolateEndpoint(const std::string& target) LOCKS_EXCLUDED(isolated_endpoints_mtx_); - int maxAttemptTimes() const { return max_attempt_times_; } + int maxAttemptTimes() const { + return max_attempt_times_; + } - void maxAttemptTimes(int times) { max_attempt_times_ = times; } + void maxAttemptTimes(int times) { + max_attempt_times_ = times; + } - int getFailedTimes() const { return failed_times_; } + int getFailedTimes() const { + return failed_times_; + } - void setFailedTimes(int times) { failed_times_ = times; } + void setFailedTimes(int times) { + failed_times_ = times; + } std::vector listMessageQueue(const std::string& topic, std::error_code& ec); - uint32_t compressBodyThreshold() const { return compress_body_threshold_; } + uint32_t compressBodyThreshold() const { + return compress_body_threshold_; + } - void compressBodyThreshold(uint32_t threshold) { compress_body_threshold_ = threshold; } + void compressBodyThreshold(uint32_t threshold) { + compress_body_threshold_ = threshold; + } /** * @brief Send message with tracing. @@ -90,7 +102,9 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void sendImpl(RetrySendCallback* callback); protected: - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { + return shared_from_this(); + } void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index 614534392..058743b69 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -16,7 +16,8 @@ ROCKETMQ_NAMESPACE_BEGIN class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this { public: - explicit PullConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) {} + explicit PullConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) { + } void start() override; @@ -31,7 +32,9 @@ class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this< void prepareHeartbeatData(HeartbeatRequest& request) override; protected: - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { + return shared_from_this(); + } MessageModel message_model_{MessageModel::CLUSTERING}; }; diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index 484272771..dd78cec9e 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -4,7 +4,6 @@ #include #include -#include "ConsumeMessageService.h" #include "Consumer.h" #include "ProcessQueue.h" #include "rocketmq/Executor.h" @@ -35,8 +34,6 @@ class PushConsumer : virtual public Consumer { virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; - virtual std::shared_ptr getConsumeMessageService() = 0; - virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) = 0; virtual MessageListener* messageListener() = 0; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 7499593c1..44e1b5499 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -90,13 +90,17 @@ class PushConsumerImpl : virtual public ClientImpl, void consumeThreadPoolSize(int thread_pool_size); - int32_t maxDeliveryAttempts() const override { return max_delivery_attempts_; } + int32_t maxDeliveryAttempts() const override { + return max_delivery_attempts_; + } uint32_t consumeBatchSize() const override; void consumeBatchSize(uint32_t consume_batch_size); - int32_t receiveBatchSize() const override { return receive_batch_size_; } + int32_t receiveBatchSize() const override { + return receive_batch_size_; + } std::shared_ptr getConsumeMessageService() override; @@ -120,21 +124,27 @@ class PushConsumerImpl : virtual public ClientImpl, // only for test std::size_t getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); - void setCustomExecutor(const Executor& executor) { custom_executor_ = executor; } + void setCustomExecutor(const Executor& executor) { + custom_executor_ = executor; + } - const Executor& customExecutor() const override { return custom_executor_; } + const Executor& customExecutor() const override { + return custom_executor_; + } void setThrottle(const std::string& topic, uint32_t threshold); -#ifdef ENABLE_TRACING - nostd::shared_ptr getTracer(); -#endif - - MessageModel messageModel() const override { return message_model_; } + MessageModel messageModel() const override { + return message_model_; + } - void setMessageModel(MessageModel message_model) { message_model_ = message_model; } + void setMessageModel(MessageModel message_model) { + message_model_ = message_model; + } - void offsetStore(std::unique_ptr offset_store) { offset_store_ = std::move(offset_store); } + void offsetStore(std::unique_ptr offset_store) { + offset_store_ = std::move(offset_store); + } void updateOffset(const MQMessageQueue& message_queue, int64_t offset) override { if (offset_store_) { @@ -147,23 +157,29 @@ class PushConsumerImpl : virtual public ClientImpl, * back-pressure. * @return */ - uint32_t maxCachedMessageQuantity() const override { return MixAll::DEFAULT_CACHED_MESSAGE_COUNT; } + uint32_t maxCachedMessageQuantity() const override { + return MixAll::DEFAULT_CACHED_MESSAGE_COUNT; + } /** * Threshold of total cached message body size by queue before applying * back-pressure. * @return */ - uint64_t maxCachedMessageMemory() const override { return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; } + uint64_t maxCachedMessageMemory() const override { + return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; + } void iterateProcessQueue(const std::function& callback) override; - MessageListener* messageListener() override { return message_listener_; } - - ReceiveMessageAction receiveMessageAction() const override { return receive_message_policy_; } + MessageListener* messageListener() override { + return message_listener_; + } protected: - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { + return shared_from_this(); + } ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; @@ -201,8 +217,6 @@ class PushConsumerImpl : virtual public ClientImpl, MessageModel message_model_{MessageModel::CLUSTERING}; - ReceiveMessageAction receive_message_policy_{ReceiveMessageAction::POLLING}; - mutable std::unique_ptr offset_store_; void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); diff --git a/src/main/cpp/rocketmq/include/ReceiveMessageAction.h b/src/main/cpp/rocketmq/include/ReceiveMessageAction.h deleted file mode 100644 index 6dea6a915..000000000 --- a/src/main/cpp/rocketmq/include/ReceiveMessageAction.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum class ReceiveMessageAction : std::int8_t { - /** - * @brief Use Receive and Ack strategy. Progress of message consumption is - * managed by the server. - */ - POLLING = 0, - - /** - * @brief A consumer client dynamically binds one or multiple message queues - * through user-defined-allocation algorithm. The client then range-scans the - * specified message queue and selectively ack through persisting offset to - * brokers. - */ - PULL = 1, -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index 3063f93de..beff4998c 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -35,11 +35,17 @@ class AwaitSendCallback : public SendCallback { void await(); - explicit operator bool() const { return !ec_.operator bool(); } + explicit operator bool() const { + return !ec_.operator bool(); + } - const SendResult& sendResult() const { return send_result_; } + const SendResult& sendResult() const { + return send_result_; + } - const std::error_code& errorCode() const { return ec_; } + const std::error_code& errorCode() const { + return ec_; + } private: absl::Mutex mtx_; @@ -56,22 +62,29 @@ class RetrySendCallback : public SendCallback { RetrySendCallback(std::weak_ptr producer, MQMessage message, int max_attempt_times, SendCallback* callback, std::vector candidates) : producer_(std::move(producer)), message_(std::move(message)), max_attempt_times_(max_attempt_times), - callback_(callback), candidates_(std::move(candidates)), span_(opencensus::trace::Span::BlankSpan()) {} + callback_(callback), candidates_(std::move(candidates)), span_(opencensus::trace::Span::BlankSpan()) { + } void onSuccess(SendResult& send_result) noexcept override; void onFailure(const std::error_code& ec) noexcept override; - MQMessage& message() { return message_; } + MQMessage& message() { + return message_; + } - int attemptTime() const { return attempt_times_; } + int attemptTime() const { + return attempt_times_; + } const MQMessageQueue& messageQueue() const { int index = attempt_times_ % candidates_.size(); return candidates_[index]; } - opencensus::trace::Span& span() { return span_; } + opencensus::trace::Span& span() { + return span_; + } private: std::weak_ptr producer_; diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h index ed798b427..ab955d295 100644 --- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include #include "absl/strings/string_view.h" @@ -15,9 +15,11 @@ class StaticNameServerResolver : public NameServerResolver { public: explicit StaticNameServerResolver(absl::string_view name_server_list); - void start() override {} + void start() override { + } - void shutdown() override {} + void shutdown() override { + } std::string current() override; diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h index 9429355e5..54c24a1ac 100644 --- a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -16,7 +16,9 @@ class StsCredentialsProviderImpl : public CredentialsProvider { Credentials getCredentials() override; - void withHttpClient(std::unique_ptr http_client) { http_client_ = std::move(http_client); } + void withHttpClient(std::unique_ptr http_client) { + http_client_ = std::move(http_client); + } private: static const char* RAM_ROLE_HOST; diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index be33cee3f..8029605d6 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -14,7 +14,8 @@ class TransactionImpl : public Transaction { TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, std::string trace_context, const std::shared_ptr& producer) : message_id_(std::move(message_id)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), - trace_context_(std::move(trace_context)), producer_(producer) {} + trace_context_(std::move(trace_context)), producer_(producer) { + } ~TransactionImpl() override = default; diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h new file mode 100644 index 000000000..c2645f301 --- /dev/null +++ b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h @@ -0,0 +1,26 @@ +#pragma once + +#include "ConsumeMessageService.h" + +#include "gmock/gmock.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeMessageServiceMock : public ConsumeMessageService { +public: + MOCK_METHOD(void, start, (), (override)); + + MOCK_METHOD(void, shutdown, (), (override)); + + MOCK_METHOD(void, submitConsumeTask, (const ProcessQueueWeakPtr&), (override)); + + MOCK_METHOD(MessageListenerType, messageListenerType, (), (override)); + + MOCK_METHOD(void, signalDispatcher, (), (override)); + + MOCK_METHOD(void, throttle, (const std::string&, std::uint32_t), (override)); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h index df2435805..c818458d6 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -15,7 +15,7 @@ class ConsumerMock : virtual public Consumer, virtual public ClientMock { MOCK_METHOD(int32_t, receiveBatchSize, (), (const override)); - MOCK_METHOD(ReceiveMessageAction, receiveMessageAction, (), (const override)); + MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 059577db3..00cd87270 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -1,8 +1,9 @@ #pragma once +#include + #include "ConsumerMock.h" #include "PushConsumer.h" -#include ROCKETMQ_NAMESPACE_BEGIN @@ -26,8 +27,6 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); - MOCK_METHOD(std::shared_ptr, getConsumeMessageService, (), (override)); - MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&), (override)); MOCK_METHOD(MessageListener*, messageListener, (), (override)); diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index e85557056..89c66a212 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -129,37 +129,37 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } switch (exp->traceMode()) { - case TraceMode::OFF: - return; - case TraceMode::DEBUG: { - { - for (const auto& span : spans) { - SPDLOG_INFO("{} --> {}: {}", absl::FormatTime(span.start_time()), absl::FormatTime(span.end_time()), - span.name().data()); - for (const auto& event : span.annotations().events()) { - for (const auto& attr : event.event().attributes()) { - switch (attr.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.int_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.bool_value()); - break; + case TraceMode::OFF: + return; + case TraceMode::DEBUG: { + { + for (const auto& span : spans) { + SPDLOG_INFO("{} --> {}: {}", absl::FormatTime(span.start_time()), absl::FormatTime(span.end_time()), + span.name().data()); + for (const auto& event : span.annotations().events()) { + for (const auto& attr : event.event().attributes()) { + switch (attr.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, + attr.second.bool_value()); + break; + } } } } } + return; } - return; - } - case TraceMode::GRPC: - break; + case TraceMode::GRPC: + break; } syncExportClients(); @@ -225,15 +225,15 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter kv->set_key(attr.first); auto value = new common::AnyValue(); switch (attr.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attr.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attr.second.int_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attr.second.bool_value()); - break; + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attr.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attr.second.int_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attr.second.bool_value()); + break; } ev->mutable_attributes()->AddAllocated(kv); } @@ -251,15 +251,15 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter kv->set_key(attribute.first); auto value = new common::AnyValue(); switch (attribute.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attribute.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attribute.second.bool_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attribute.second.int_value()); - break; + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; } kv->set_allocated_value(value); span_link->mutable_attributes()->AddAllocated(kv); @@ -274,15 +274,15 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter kv->set_key(attribute.first); auto value = new common::AnyValue(); switch (attribute.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attribute.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attribute.second.bool_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attribute.second.int_value()); - break; + case opencensus::trace::AttributeValueRef::Type::kString: + value->set_string_value(attribute.second.string_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + value->set_bool_value(attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + value->set_int_value(attribute.second.int_value()); + break; } item->mutable_attributes()->AddAllocated(kv); } diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index 536f8d213..dc80c00eb 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -24,7 +24,12 @@ ROCKETMQ_NAMESPACE_BEGIN namespace collector = opentelemetry::proto::collector; namespace collector_trace = collector::trace::v1; -enum class TraceMode : std::uint8_t { OFF = 0, DEBUG = 1, GRPC = 2 }; +enum class TraceMode : std::uint8_t +{ + OFF = 0, + DEBUG = 1, + GRPC = 2 +}; class Samplers { public: @@ -34,7 +39,8 @@ class Samplers { class OtlpExporter : public std::enable_shared_from_this { public: OtlpExporter(std::weak_ptr client_manager, ClientConfig* client_config) - : client_manager_(std::move(client_manager)), client_config_(client_config) {} + : client_manager_(std::move(client_manager)), client_config_(client_config) { + } void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { absl::MutexLock lk(&hosts_mtx_); @@ -50,13 +56,21 @@ class OtlpExporter : public std::enable_shared_from_this { return hosts_; } - std::weak_ptr& clientManager() { return client_manager_; } + std::weak_ptr& clientManager() { + return client_manager_; + } - ClientConfig* clientConfig() { return client_config_; } + ClientConfig* clientConfig() { + return client_config_; + } - void traceMode(TraceMode mode) { mode_ = mode; } + void traceMode(TraceMode mode) { + mode_ = mode; + } - TraceMode traceMode() const { return mode_; } + TraceMode traceMode() const { + return mode_; + } private: std::weak_ptr client_manager_; @@ -71,7 +85,8 @@ class OtlpExporter : public std::enable_shared_from_this { class ExportClient { public: ExportClient(std::shared_ptr completion_queue, std::shared_ptr channel) - : completion_queue_(std::move(completion_queue)), stub_(collector_trace::TraceService::NewStub(channel)) {} + : completion_queue_(std::move(completion_queue)), stub_(collector_trace::TraceService::NewStub(channel)) { + } void asyncExport(const collector_trace::ExportTraceServiceRequest& request, InvocationContext* invocation_context); diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index c9dddabef..1b6d949b6 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -60,9 +60,12 @@ class RpcClientTest : public ::testing::Test { name_server_target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); } - void TearDown() override {} + void TearDown() override { + } - ~RpcClientTest() override { completion_queue_->Shutdown(); } + ~RpcClientTest() override { + completion_queue_->Shutdown(); + } bool brokerEndpoint(const std::string& topic, std::string& endpoint) { QueryRouteRequest route_request; diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index 91873d4ee..ed8731adc 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -20,7 +20,9 @@ class TopAddressingTest : public testing::Test { spdlog::set_level(spdlog::level::debug); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } void SetEnv(const char* key, const char* value) { int overwrite = 1; diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index a7674538f..c7a2d6045 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -53,7 +53,9 @@ class TopicPublishInfoTest : public ::testing::Test { Signature::sign(&client_config_, metadata_); } - void TearDown() override { completion_queue_->Shutdown(); } + void TearDown() override { + completion_queue_->Shutdown(); + } std::string topic_{"yc001"}; std::string group_{"yc001"}; @@ -100,33 +102,33 @@ TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { Topic topic(resource_namespace_, topic_); Permission permission; switch (item.permission()) { - case rmq::Permission::READ: - permission = Permission::READ; - break; - case rmq::Permission::WRITE: - permission = Permission::WRITE; - break; - case rmq::Permission::READ_WRITE: - permission = Permission::READ_WRITE; - break; - default: - permission = Permission::NONE; - break; + case rmq::Permission::READ: + permission = Permission::READ; + break; + case rmq::Permission::WRITE: + permission = Permission::WRITE; + break; + case rmq::Permission::READ_WRITE: + permission = Permission::READ_WRITE; + break; + default: + permission = Permission::NONE; + break; } AddressScheme scheme; switch (item.broker().endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - scheme = AddressScheme::IPv4; - break; - case rmq::AddressScheme::IPv6: - scheme = AddressScheme::IPv6; - break; - case rmq::AddressScheme::DOMAIN_NAME: - scheme = AddressScheme::DOMAIN_NAME; - break; - default: - scheme = AddressScheme::IPv4; + case rmq::AddressScheme::IPv4: + scheme = AddressScheme::IPv4; + break; + case rmq::AddressScheme::IPv6: + scheme = AddressScheme::IPv6; + break; + case rmq::AddressScheme::DOMAIN_NAME: + scheme = AddressScheme::DOMAIN_NAME; + break; + default: + scheme = AddressScheme::IPv4; } std::vector
addresses; diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp index 8dfb408cc..279c74b48 100644 --- a/src/test/cpp/ut/base/HistogramTest.cpp +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -5,7 +5,8 @@ ROCKETMQ_NAMESPACE_BEGIN class HistogramTest : public testing::Test { public: - HistogramTest() : histogram_("Test", 3) {} + HistogramTest() : histogram_("Test", 3) { + } void SetUp() override { histogram_.labels().emplace_back("Foo: "); diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp index a04133f83..fd1b8abbb 100644 --- a/src/test/cpp/ut/base/HostInfoTest.cpp +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -1,16 +1,18 @@ -#include "gtest/gtest.h" #include "HostInfo.h" +#include "fmt/format.h" #include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" #include -#include "fmt/format.h" ROCKETMQ_NAMESPACE_BEGIN class HostInfoTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + } - void TearDown() override {} + void TearDown() override { + } protected: std::string site_{"site"}; @@ -20,15 +22,15 @@ class HostInfoTest : public testing::Test { void SetEnv(const char* key, const char* value) { int overwrite = 1; - #ifdef _WIN32 +#ifdef _WIN32 std::string env; env.append(key); env.push_back('='); env.append(value); _putenv(env.c_str()); - #else +#else setenv(key, value, overwrite); - #endif +#endif } }; diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp index 786bc13a3..ce1290e69 100644 --- a/src/test/cpp/ut/base/HttpClientTest.cpp +++ b/src/test/cpp/ut/base/HttpClientTest.cpp @@ -29,7 +29,8 @@ class HttpClientTest : public testing::Test { HttpClientImpl http_client; }; -TEST_F(HttpClientTest, testBasics) {} +TEST_F(HttpClientTest, testBasics) { +} TEST_F(HttpClientTest, testGet) { auto cb = [](int code, const std::multimap& headers, const std::string& body) { diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp index 9ebd98efd..9f94e1e30 100644 --- a/src/test/cpp/ut/base/InvocationContextTest.cpp +++ b/src/test/cpp/ut/base/InvocationContextTest.cpp @@ -1,6 +1,6 @@ #include "InvocationContext.h" -#include "gtest/gtest.h" #include "RpcClient.h" +#include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp index db99be3ef..aa2d8b1b1 100644 --- a/src/test/cpp/ut/base/MQMessageExtTest.cpp +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -1,6 +1,6 @@ #include "rocketmq/MQMessageExt.h" -#include "rocketmq/MQMessage.h" #include "MessageAccessor.h" +#include "rocketmq/MQMessage.h" #include "gtest/gtest.h" #include @@ -12,7 +12,8 @@ class MQMessageExtTest : public testing::Test { MessageAccessor::setMessageId(message_, msg_id_); } - void TearDown() override {} + void TearDown() override { + } protected: std::string msg_id_{"msg-0"}; @@ -20,14 +21,18 @@ class MQMessageExtTest : public testing::Test { MQMessageExt message_; }; -TEST_F(MQMessageExtTest, testGetQueueId) { EXPECT_EQ(message_.getQueueId(), 0); } +TEST_F(MQMessageExtTest, testGetQueueId) { + EXPECT_EQ(message_.getQueueId(), 0); +} TEST_F(MQMessageExtTest, testBornTimestamp) { auto born_timestamp = message_.bornTimestamp(); EXPECT_TRUE(std::chrono::system_clock::now() - born_timestamp < std::chrono::seconds(1)); } -TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { EXPECT_EQ(message_.getDeliveryAttempt(), 0); } +TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { + EXPECT_EQ(message_.getDeliveryAttempt(), 0); +} TEST_F(MQMessageExtTest, testGetBornTimestamp) { int64_t born_timestamp = message_.getBornTimestamp(); diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp index 77af10e55..1c76a8778 100644 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -13,7 +13,8 @@ class MQMessageTest : public testing::Test { message.setDelayTimeLevel(delay_level_); } - void TearDown() override {} + void TearDown() override { + } protected: std::string topic_{"Test"}; diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp index b072d1f12..bc504b2dc 100644 --- a/src/test/cpp/ut/base/RateLimiterTest.cpp +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -7,7 +7,9 @@ ROCKETMQ_NAMESPACE_BEGIN class RateLimiterTest : public ::testing::Test { public: - void TearDown() override { observer.stop(); } + void TearDown() override { + observer.stop(); + } protected: RateLimiterObserver observer; diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp index ebbddae03..64d9f5e5c 100644 --- a/src/test/cpp/ut/base/ThreadPoolTest.cpp +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -19,7 +19,9 @@ class ThreadPoolTest : public testing::Test { completed = false; } - void TearDown() override { pool_->shutdown(); } + void TearDown() override { + pool_->shutdown(); + } protected: std::unique_ptr pool_; diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp index 836132f09..1e4d86906 100644 --- a/src/test/cpp/ut/base/TopAddressingTest.cpp +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -29,7 +29,9 @@ class TopAddressingTest : public testing::Test { top_addressing_->injectHttpClient(std::move(http_client_)); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } protected: std::unique_ptr top_addressing_; diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp index 449d655b3..58dee2442 100644 --- a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -8,7 +8,7 @@ ROCKETMQ_NAMESPACE_BEGIN TEST(UniqueIdGeneratorTest, testOutputSampleId) { -std::cout << "A sample unique ID: " << UniqueIdGenerator::instance().next() << std::endl; + std::cout << "A sample unique ID: " << UniqueIdGenerator::instance().next() << std::endl; } TEST(UniqueIdGeneratorTest, testNext) { diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index 74da9c54b..5c7354466 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -12,9 +12,11 @@ ROCKETMQ_NAMESPACE_BEGIN class UtilAllTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + } - void TearDown() override {} + void TearDown() override { + } }; TEST_F(UtilAllTest, testCompress) { diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index 74cc5469b..eb1c283f0 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -6,9 +6,11 @@ ROCKETMQ_NAMESPACE_BEGIN class ClientManagerFactoryTest : public testing::Test { public: - void SetUp() override {} + void SetUp() override { + } - void TearDown() override {} + void TearDown() override { + } protected: testing::NiceMock client_config_; @@ -16,7 +18,9 @@ class ClientManagerFactoryTest : public testing::Test { }; TEST_F(ClientManagerFactoryTest, testGetClientManager) { - EXPECT_CALL(client_config_, resourceNamespace).Times(testing::AtLeast(1)).WillRepeatedly(testing::ReturnRef(resource_namespace_)); + EXPECT_CALL(client_config_, resourceNamespace) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::ReturnRef(resource_namespace_)); ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); EXPECT_TRUE(client_manager); } diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index e14ed0249..f1c1e2ab4 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -133,7 +133,7 @@ TEST_F(ClientManagerTest, testReceiveMessage) { .WillRepeatedly(testing::Invoke(mock_async_receive)); ReceiveMessageRequest request; - EXPECT_CALL(*receive_message_callback_, onSuccess).Times(testing::AtLeast(1)); + EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), receive_message_callback_); @@ -167,7 +167,7 @@ TEST_F(ClientManagerTest, testReceiveMessage_Failure) { .WillRepeatedly(testing::Invoke(mock_async_receive)); ReceiveMessageRequest request; - EXPECT_CALL(*receive_message_callback_, onFailure).Times(testing::AtLeast(1)); + EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), receive_message_callback_); @@ -305,29 +305,6 @@ TEST_F(ClientManagerTest, testEndTransaction) { EXPECT_TRUE(callback_invoked); } -TEST_F(ClientManagerTest, processPullResult) { - grpc::ClientContext client_context; - PullMessageResponse response; - auto message = new rmq::Message; - message->mutable_topic()->set_resource_namespace(resource_namespace_); - message->mutable_topic()->set_name(topic_); - message->set_body(message_body_); - message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); - std::string checksum; - EXPECT_TRUE(MixAll::md5(message_body_, checksum)); - message->mutable_system_attribute()->mutable_body_digest()->set_checksum(checksum); - message->mutable_system_attribute()->set_tag(tag_); - message->mutable_system_attribute()->mutable_keys()->Add(key_.c_str()); - response.mutable_messages()->AddAllocated(message); - ReceiveMessageResult result; - client_manager_->processPullResult(client_context, response, result, target_host_); - EXPECT_EQ(1, result.messages_.size()); - const auto& msg = *result.messages_.begin(); - EXPECT_EQ(msg.getTopic(), topic_); - EXPECT_EQ(msg.getTags(), tag_); - EXPECT_EQ(msg.getBody(), message_body_); -} - TEST_F(ClientManagerTest, testHealthCheck) { bool completed = false; absl::Mutex mtx; diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/test/cpp/ut/client/ClientTest.cpp index 2596f4901..098789f4d 100644 --- a/src/test/cpp/ut/client/ClientTest.cpp +++ b/src/test/cpp/ut/client/ClientTest.cpp @@ -16,12 +16,15 @@ class ClientTest : public testing::Test { })); } - void TearDown() override {} + void TearDown() override { + } protected: std::shared_ptr> client_; }; -TEST_F(ClientTest, testActive) { EXPECT_TRUE(client_->active()); } +TEST_F(ClientTest, testActive) { + EXPECT_TRUE(client_->active()); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp index 854dcecb3..7f447df7d 100644 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -9,7 +9,9 @@ namespace ut { class RpcClientTest : public testing::Test { public: - void SetUp() override { grpc::internal::GrpcLibraryInitializer initializer; } + void SetUp() override { + grpc::internal::GrpcLibraryInitializer initializer; + } static void mockQueryRouteInfo(const QueryRouteRequest& request, InvocationContext* invocation_context) { diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp index ec1ec2d9a..de46b79b9 100644 --- a/src/test/cpp/ut/client/TracingUtilityTest.cpp +++ b/src/test/cpp/ut/client/TracingUtilityTest.cpp @@ -6,7 +6,8 @@ using namespace opentelemetry::trace; ROCKETMQ_NAMESPACE_BEGIN -template static std::string hex(const T& id_item) { +template +static std::string hex(const T& id_item) { char buf[T::kSize * 2]; id_item.ToLowerBase16(buf); return std::string(buf, sizeof(buf)); diff --git a/src/test/cpp/ut/grpc/GrpcTimer.cpp b/src/test/cpp/ut/grpc/GrpcTimer.cpp index 993d86aae..ebb9ca01b 100644 --- a/src/test/cpp/ut/grpc/GrpcTimer.cpp +++ b/src/test/cpp/ut/grpc/GrpcTimer.cpp @@ -29,8 +29,12 @@ struct PeriodicTimer { class GrpcTimerTest : public testing::Test { public: - void SetUp() override { grpc_init(); } - void TearDown() override { grpc_shutdown(); } + void SetUp() override { + grpc_init(); + } + void TearDown() override { + grpc_shutdown(); + } static void schedule(void* arg, grpc_error_handle error) { auto timer = static_cast(arg); @@ -54,7 +58,8 @@ class GrpcTimerTest : public testing::Test { } }; -TEST_F(GrpcTimerTest, testSetUp) {} +TEST_F(GrpcTimerTest, testSetUp) { +} TEST_F(GrpcTimerTest, testSingleShot) { grpc_core::ExecCtx exec_ctx; diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 424b4d6ae..6ac3a010f 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -18,11 +18,15 @@ ROCKETMQ_NAMESPACE_BEGIN class TestClientImpl : public ClientImpl, public std::enable_shared_from_this { public: - TestClientImpl(std::string group) : ClientImpl(std::move(group)) {} + TestClientImpl(std::string group) : ClientImpl(std::move(group)) { + } - std::shared_ptr self() override { return shared_from_this(); } + std::shared_ptr self() override { + return shared_from_this(); + } - void prepareHeartbeatData(HeartbeatRequest& request) override {} + void prepareHeartbeatData(HeartbeatRequest& request) override { + } }; class ClientImplTest : public testing::Test { @@ -93,6 +97,8 @@ TEST_F(ClientImplTest, testBasic) { } EXPECT_TRUE(completed); + // Now that the derivative class has closed its resources, state of ClientImpl should be STOPPING. + client_->state(State::STOPPING); client_->shutdown(); } diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp index 77a5f9709..5215ddb79 100644 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -1,4 +1,4 @@ -#include "ConsumeMessageService.h" +#include "ConsumeStandardMessageService.h" #include "MessageListenerMock.h" #include "ProcessQueueMock.h" #include "PushConsumerMock.h" @@ -13,7 +13,8 @@ ROCKETMQ_NAMESPACE_BEGIN class ConsumeStandardMessageServiceTest : public testing::Test { public: ConsumeStandardMessageServiceTest() - : credentials_provider_(std::make_shared("access_key", "access_secret")) {} + : credentials_provider_(std::make_shared("access_key", "access_secret")) { + } void SetUp() override { grpc_init(); @@ -41,7 +42,9 @@ class ConsumeStandardMessageServiceTest : public testing::Test { ON_CALL(*consumer_, getGroupName).WillByDefault(testing::ReturnRef(group_name_)); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } protected: int thread_count_{2}; diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp index 2924a6e9f..c2d5b727b 100644 --- a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp @@ -1,6 +1,6 @@ -#include "gtest/gtest.h" -#include "gmock/gmock.h" #include "ConsumerMock.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 26600a7d6..859d6f54b 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -4,14 +4,13 @@ #include "google/protobuf/struct.pb.h" #include "google/protobuf/util/json_util.h" #include "gtest/gtest.h" +#include #include #include -#include ROCKETMQ_NAMESPACE_BEGIN -class CredentialsProviderTest : public testing::Test { -}; +class CredentialsProviderTest : public testing::Test {}; TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { std::string access_key("abc"); diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index e7b879941..81b07b1dd 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -112,7 +112,8 @@ TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { class UnitTestSendCallback : public SendCallback { public: UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) - : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) {} + : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) { + } void onSuccess(SendResult& send_result) noexcept override { absl::MutexLock lk(&mtx_); diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 845966f3b..32f6cef19 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -6,13 +6,16 @@ ROCKETMQ_NAMESPACE_BEGIN class DefaultMQPushConsumerTest : public testing::Test { public: - DefaultMQPushConsumerTest() : group_name_("group-0"), consumer_(group_name_) {} + DefaultMQPushConsumerTest() : group_name_("group-0"), consumer_(group_name_) { + } protected: std::string group_name_; DefaultMQPushConsumer consumer_; }; -TEST_F(DefaultMQPushConsumerTest, testGroupName) { EXPECT_EQ(group_name_, consumer_.groupName()); } +TEST_F(DefaultMQPushConsumerTest, testGroupName) { + EXPECT_EQ(group_name_, consumer_.groupName()); +} ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp index 2435b8139..3aef9dfdd 100644 --- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -17,7 +17,8 @@ ROCKETMQ_NAMESPACE_BEGIN class DynamicNameServerResolverTest : public testing::Test { public: DynamicNameServerResolverTest() - : resolver_(std::make_shared(endpoint_, std::chrono::seconds(1))) {} + : resolver_(std::make_shared(endpoint_, std::chrono::seconds(1))) { + } void SetUp() override { auto http_client = absl::make_unique>(); @@ -37,7 +38,9 @@ class DynamicNameServerResolverTest : public testing::Test { resolver_->start(); } - void TearDown() override { resolver_->shutdown(); } + void TearDown() override { + resolver_->shutdown(); + } protected: std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp index c1eee2cb3..b64eaa9c1 100644 --- a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -20,20 +20,20 @@ class ExecutorImpl { virtual ~ExecutorImpl() { switch (state_.load(std::memory_order_relaxed)) { - case CREATED: - case STOPPING: - case STOPPED: - break; - - case STARTING: - case STARTED: - state_.store(State::STOPPED); - for (auto& worker : workers_) { - if (worker.joinable()) { - worker.join(); + case CREATED: + case STOPPING: + case STOPPED: + break; + + case STARTING: + case STARTED: + state_.store(State::STOPPED); + for (auto& worker : workers_) { + if (worker.joinable()) { + worker.join(); + } } - } - break; + break; } } @@ -103,15 +103,20 @@ class ExecutorImpl { class ExecutorTest : public ::testing::Test { public: - void SetUp() override { executor_.start(); } + void SetUp() override { + executor_.start(); + } - void TearDown() override { executor_.stop(); } + void TearDown() override { + executor_.stop(); + } protected: ExecutorImpl executor_{0}; }; -TEST_F(ExecutorTest, testBasic) {} +TEST_F(ExecutorTest, testBasic) { +} std::mutex submit_mtx_; std::condition_variable submit_cv_; diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp index 465d41fd8..5685c3eca 100644 --- a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp +++ b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp @@ -5,11 +5,14 @@ ROCKETMQ_NAMESPACE_BEGIN class FilterExpressionTest : public testing::Test { public: - FilterExpressionTest() : filter_expression_("TagA") {} + FilterExpressionTest() : filter_expression_("TagA") { + } - void SetUp() override {} + void SetUp() override { + } - void TearDown() override {} + void TearDown() override { + } protected: FilterExpression filter_expression_; diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp index 67fc5ab9d..102992465 100644 --- a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp @@ -1,7 +1,7 @@ -#include "gtest/gtest.h" #include "MessageGroupQueueSelector.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" +#include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp index 60b09091f..4fc598cb3 100644 --- a/src/test/cpp/ut/rocketmq/MessageTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageTest.cpp @@ -1,7 +1,7 @@ +#include "rocketmq/MQMessageExt.h" #include #include #include -#include "rocketmq/MQMessageExt.h" TEST(MessageTest, testExpire) { auto expire_time_point = std::chrono::steady_clock::now() + std::chrono::milliseconds(300); diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 725b8d6b9..98c9707b0 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -1,5 +1,15 @@ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + #include "Assignment.h" #include "ClientManagerMock.h" +#include "ConsumeMessageServiceMock.h" #include "ConsumeMessageType.h" #include "InvocationContext.h" #include "MessageAccessor.h" @@ -10,12 +20,6 @@ #include "RpcClientMock.h" #include "rocketmq/CredentialsProvider.h" #include "rocketmq/MQMessageExt.h" -#include "gtest/gtest.h" -#include -#include -#include -#include -#include ROCKETMQ_NAMESPACE_BEGIN @@ -31,12 +35,20 @@ class ProcessQueueTest : public testing::Test { credentials_provider_ = std::make_shared(access_key_, access_secret_); consumer_ = std::make_shared>(); auto consumer = std::dynamic_pointer_cast(consumer_); + + consume_message_service_ = std::make_shared>(); + ON_CALL(*consume_message_service_, messageListenerType) + .WillByDefault(testing::Return(MessageListenerType::STANDARD)); + + ON_CALL(*consumer_, getConsumeMessageService).WillByDefault(testing::Return(consume_message_service_)); + process_queue_ = std::make_shared(message_queue_, filter_expression_, consumer, client_manager_); receive_message_callback_ = std::make_shared>(); process_queue_->callback(receive_message_callback_); } - void TearDown() override {} + void TearDown() override { + } protected: std::string tenant_id_{"tenant-0"}; @@ -57,6 +69,7 @@ class ProcessQueueTest : public testing::Test { std::shared_ptr> rpc_client_; std::shared_ptr> client_manager_; std::shared_ptr> consumer_; + std::shared_ptr> consume_message_service_; std::shared_ptr process_queue_; std::shared_ptr> receive_message_callback_; std::string resource_namespace_{"mq://test"}; @@ -138,7 +151,9 @@ TEST_F(ProcessQueueTest, testShouldThrottle_ByMemory) { EXPECT_TRUE(process_queue_->shouldThrottle()); } -TEST_F(ProcessQueueTest, testHasPendingMessages) { EXPECT_FALSE(process_queue_->hasPendingMessages()); } +TEST_F(ProcessQueueTest, testHasPendingMessages) { + EXPECT_FALSE(process_queue_->hasPendingMessages()); +} TEST_F(ProcessQueueTest, testHasPendingMessages2) { std::vector messages; @@ -271,19 +286,18 @@ TEST_F(ProcessQueueTest, testReceiveMessage_POP) { EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - EXPECT_CALL(*consumer_, receiveMessageAction).WillRepeatedly(testing::Return(ReceiveMessageAction::POLLING)); auto optional = absl::make_optional(filter_expression_); EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); + EXPECT_CALL(*consumer_, messageModel).WillRepeatedly(testing::Return(MessageModel::CLUSTERING)); auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, const std::shared_ptr& cb) { - ReceiveMessageResult receive_message_result; - receive_message_result.status_ = ReceiveMessageStatus::OK; - + std::error_code ec; + ReceiveMessageResult result; for (size_t i = 0; i < threshold_quantity_; i++) { MQMessageExt message; message.setTopic(topic_); @@ -291,9 +305,9 @@ TEST_F(ProcessQueueTest, testReceiveMessage_POP) { message.setBody(message_body_); MessageAccessor::setQueueId(message, queue_id_); MessageAccessor::setQueueOffset(message, i); - receive_message_result.messages_.emplace_back(message); + result.messages.emplace_back(message); } - cb->onSuccess(receive_message_result); + cb->onCompletion(ec, result); }; EXPECT_CALL(*client_manager_, receiveMessage) @@ -311,24 +325,24 @@ TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - EXPECT_CALL(*consumer_, receiveMessageAction).WillRepeatedly(testing::Return(ReceiveMessageAction::PULL)); auto optional = absl::make_optional(filter_expression_); EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); - auto invocation_context = new InvocationContext(); + std::error_code ec; + ReceiveMessageResult result; + auto pull_message_mock = [&](const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - cb(invocation_context); + const std::function& cb) { + cb(ec, result); }; EXPECT_CALL(*client_manager_, pullMessage) .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Invoke(pull_message_mock)); - EXPECT_CALL(*client_manager_, processPullResult); process_queue_->receiveMessage(); } diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index b8375b3ba..eb775148c 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -17,8 +17,8 @@ ROCKETMQ_NAMESPACE_BEGIN class ProducerImplTest : public testing::Test { public: - ProducerImplTest() - : credentials_provider_(std::make_shared(access_key_, access_secret_)) {} + ProducerImplTest() : credentials_provider_(std::make_shared(access_key_, access_secret_)) { + } void SetUp() override { grpc_init(); @@ -43,7 +43,9 @@ class ProducerImplTest : public testing::Test { } } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } protected: std::shared_ptr> client_manager_; @@ -198,7 +200,8 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { class TestSendCallback : public SendCallback { public: - TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) {} + TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) { + } void onSuccess(SendResult& send_result) noexcept override { absl::MutexLock lk(&mtx_); completed_ = true; diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 26d3de855..6c5ae4172 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -12,6 +12,8 @@ #include "apache/rocketmq/v1/definition.pb.h" #include "rocketmq/AsyncCallback.h" #include "rocketmq/ConsumeType.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/MQMessageExt.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" @@ -102,7 +104,8 @@ TEST_F(PullConsumerImplTest, testQueuesFor) { class TestPullCallback : public PullCallback { public: - TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) {} + TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) { + } void onSuccess(const PullResult& pull_result) noexcept override { success_ = true; failure_ = false; @@ -132,30 +135,21 @@ TEST_F(PullConsumerImplTest, testPull) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Invoke(mock_resolve_route)); - auto invocation_context = new InvocationContext(); - - auto messages = invocation_context->response.mutable_messages(); + std::error_code ec; + ReceiveMessageResult result; for (int i = 0; i < batch_size_; i++) { - auto message = new rmq::Message(); - message->set_body(message_body_); - std::string md5; - EXPECT_TRUE(MixAll::md5(message_body_, md5)); - message->mutable_system_attribute()->mutable_body_digest()->set_type(rmq::DigestType::MD5); - message->mutable_system_attribute()->mutable_body_digest()->set_checksum(md5); - - message->mutable_topic()->set_resource_namespace(resource_namespace_); - message->mutable_topic()->set_name(topic_); - - message->mutable_system_attribute()->set_tag(tag_); - - messages->AddAllocated(message); + MQMessageExt message; + message.setBody(message_body_); + message.setTopic(topic_); + message.setTags(tag_); + result.messages.emplace_back(message); } auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - cb(invocation_context); + const std::function& cb) { + cb(ec, result); }; EXPECT_CALL(*client_manager_, pullMessage) @@ -196,13 +190,13 @@ TEST_F(PullConsumerImplTest, testPull_gRPC_error) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Invoke(mock_resolve_route)); - auto invocation_context = new InvocationContext(); - invocation_context->status = grpc::Status::CANCELLED; - + std::error_code ec; + ReceiveMessageResult result; auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - cb(invocation_context); + const std::function& cb) { + ec = ErrorCode::BadRequest; + cb(ec, result); }; EXPECT_CALL(*client_manager_, pullMessage) @@ -243,13 +237,14 @@ TEST_F(PullConsumerImplTest, testPull_biz_error) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Invoke(mock_resolve_route)); - auto invocation_context = new InvocationContext(); - invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::NOT_FOUND); + std::error_code ec; + ReceiveMessageResult result; auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - cb(invocation_context); + const std::function& cb) { + ec = ErrorCode::BadRequest; + cb(ec, result); }; EXPECT_CALL(*client_manager_, pullMessage) diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp index dab7a0297..6f7ce3448 100644 --- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -9,11 +9,16 @@ ROCKETMQ_NAMESPACE_BEGIN class StaticNameServerResolverTest : public testing::Test { public: - StaticNameServerResolverTest() : resolver_(name_server_list_) {} + StaticNameServerResolverTest() : resolver_(name_server_list_) { + } - void SetUp() override { resolver_.start(); } + void SetUp() override { + resolver_.start(); + } - void TearDown() override { resolver_.shutdown(); } + void TearDown() override { + resolver_.shutdown(); + } protected: std::string name_server_list_{"10.0.0.1:9876;10.0.0.2:9876"}; diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp index 19ccf8f60..0daec1e6f 100644 --- a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -37,7 +37,9 @@ class StsCredentialsProviderImplTest : public testing::Test { sts_credentials_provider->withHttpClient(std::move(http_client_)); } - void TearDown() override { grpc_shutdown(); } + void TearDown() override { + grpc_shutdown(); + } protected: std::shared_ptr sts_credentials_provider; diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp index c1909ac8e..add9524fe 100644 --- a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -13,9 +13,12 @@ ROCKETMQ_NAMESPACE_BEGIN class OtlpExporterTest : public testing::Test { public: - void SetUp() override { client_manager_ = std::make_shared>(); } + void SetUp() override { + client_manager_ = std::make_shared>(); + } - void TearDown() override {} + void TearDown() override { + } protected: std::shared_ptr> client_manager_; diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp index ffc58a651..0b248248f 100644 --- a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp @@ -7,4 +7,3 @@ TEST(SpanContextTest, testSpanContext) { EXPECT_NO_THROW(span_context = opencensus::trace::propagation::FromTraceParentHeader(empty_context)); EXPECT_FALSE(span_context.IsValid()); } - diff --git a/tools/format.sh b/tools/format.sh new file mode 100644 index 000000000..21d995625 --- /dev/null +++ b/tools/format.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +TOOLS_DIR=$(dirname "$0") +WORKSPACE_DIR=$(dirname "$TOOLS_DIR") +cd $WORKSPACE_DIR + +find . -iname *.h -o -iname *.cpp | xargs clang-format -i \ No newline at end of file From a89a937c2e7f2f5580036617d44963477d3a7195 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 28 Sep 2021 13:06:16 +0000 Subject: [PATCH 080/133] Polish code --- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index f445bbc78..e9c1db059 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -92,14 +92,12 @@ void ProcessQueueImpl::receiveMessage() { void ProcessQueueImpl::popMessage() { rmq::ReceiveMessageRequest request; - absl::flat_hash_map metadata; auto consumer_client = consumer_.lock(); if (!consumer_client) { return; } Signature::sign(consumer_client.get(), metadata); - wrapPopMessageRequest(metadata, request); syncIdleState(); SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); @@ -145,7 +143,6 @@ void ProcessQueueImpl::cacheMessages(const std::vector& messages) { absl::MutexLock messages_lock_guard(&messages_mtx_); - // TODO: use lock-when semantics absl::MutexLock offsets_lock_guard(&offsets_mtx_); for (const auto& message : messages) { const std::string& msg_id = message.getMsgId(); From 4baf8659f660a73cd1eb2b3d211000c827150281 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 28 Sep 2021 21:36:32 +0800 Subject: [PATCH 081/133] Broadcasting (#25) * Complete heartbeat data * Add broadcasting example --- example/rocketmq/ExampleBroadcastPushConsumer.cpp | 8 +++----- src/main/cpp/client/ClientManagerImpl.cpp | 5 +++++ src/main/cpp/rocketmq/PushConsumerImpl.cpp | 5 ++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 2d59b0e7c..64b527aa3 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -1,12 +1,10 @@ #include "rocketmq/DefaultMQPushConsumer.h" -#ifndef SPDLOG_ACTIVE_LEVEL -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG -#endif - +#include "rocketmq/Logger.h" #include "spdlog/spdlog.h" #include +#include #include #include @@ -17,6 +15,7 @@ class SampleMQMessageListener : public StandardMessageListener { ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); + std::cout << "Received a message. MessageId: " << msg.getMsgId() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } return ConsumeMessageResult::SUCCESS; @@ -24,7 +23,6 @@ class SampleMQMessageListener : public StandardMessageListener { }; int main(int argc, char* argv[]) { - Logger& logger = getLogger(); logger.setLevel(Level::Debug); logger.init(); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 6a4068b23..b662e2655 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1348,6 +1348,11 @@ void ClientManagerImpl::pullMessage( SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; } break; + case google::rpc::Code::NOT_FOUND: { + SPDLOG_WARN("NotFound: {}, host={}", common.status().message(), invocation_context->remote_address); + ec = ErrorCode::NotFound; + break; + } case google::rpc::Code::DEADLINE_EXCEEDED: { SPDLOG_WARN("DeadlineExceeded: {}, host={}", common.status().message(), invocation_context->remote_address); ec = ErrorCode::GatewayTimeout; diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 9927f923c..4386b84b6 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -581,7 +581,6 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { } auto subscriptions = consumer_data->mutable_subscriptions(); - { absl::MutexLock lk(&topic_filter_expression_table_mtx_); for (const auto& entry : topic_filter_expression_table_) { @@ -613,6 +612,10 @@ void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { break; } } + + consumer_data->set_consume_policy(rmq::ConsumePolicy::RESUME); + consumer_data->mutable_dead_letter_policy()->set_max_delivery_attempts(maxDeliveryAttempts()); + consumer_data->set_consume_type(rmq::ConsumeMessageType::PASSIVE); } ClientResourceBundle PushConsumerImpl::resourceBundle() { From ee9120e5e653d3efaf4ba0aa041b68f16cdfb567 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 30 Sep 2021 03:43:24 +0000 Subject: [PATCH 082/133] Set gRPC max message size --- example/rocketmq/ExampleAsyncProducer.cpp | 2 +- example/rocketmq/ExampleProducer.cpp | 2 +- example/rocketmq/ExamplePushConsumer.cpp | 7 +++--- src/main/cpp/base/include/InvocationContext.h | 6 +++-- src/main/cpp/client/ClientManagerImpl.cpp | 22 +++++++++++++++++++ 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 8cc77db4b..3bf3801ac 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -174,7 +174,7 @@ int main(int argc, char* argv[]) { } Logger& logger = getLogger(); - logger.setLevel(Level::Debug); + logger.setLevel(Level::Info); logger.init(); const char* topic = "cpp_sdk_standard"; diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index a65fc44ad..3551bf50d 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -65,7 +65,7 @@ int main(int argc, char* argv[]) { std::thread stats_thread(stats_lambda); - std::string body = randomString(1024); + std::string body = randomString(1024 * 1024 * 4); std::cout << "Message body: " << body << std::endl; message.setBody(body); diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 697954b5e..c792c9ce2 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -16,8 +16,9 @@ class SampleMQMessageListener : public StandardMessageListener { ConsumeMessageResult consumeMessage(const std::vector& msgs) override { for (const MQMessageExt& msg : msgs) { SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); - std::cout << "Consume Message[MsgId=" << msg.getMsgId() << "] OK" << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "Consume Message[MsgId=" << msg.getMsgId() << "] OK. Body Size: " << msg.getBody().size() + << std::endl; + // std::this_thread::sleep_for(std::chrono::seconds(1)); } return ConsumeMessageResult::SUCCESS; } @@ -44,7 +45,7 @@ int main(int argc, char* argv[]) { push_consumer.setConsumeThreadCount(4); push_consumer.start(); - std::this_thread::sleep_for(std::chrono::seconds(300)); + std::this_thread::sleep_for(std::chrono::minutes(30)); push_consumer.shutdown(); return EXIT_SUCCESS; diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 3178d8c4d..bf6788c27 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -34,6 +34,7 @@ struct BaseInvocationContext { std::string remote_address; grpc::ClientContext context; grpc::Status status; + std::string task_name; absl::Time created_time{absl::Now()}; std::chrono::steady_clock::time_point start_time{std::chrono::steady_clock::now()}; }; @@ -42,6 +43,9 @@ template struct InvocationContext : public BaseInvocationContext { void onCompletion(bool ok) override { + auto elapsed = + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); + SPDLOG_DEBUG("RPC[{}] costs {}ms", task_name, elapsed); /// Client-side Read, Server-side Read, Client-side /// RecvInitialMetadata (which is typically included in Read if not /// done explicitly): ok indicates whether there is a valid message @@ -60,8 +64,6 @@ struct InvocationContext : public BaseInvocationContext { } if (!status.ok() && grpc::StatusCode::DEADLINE_EXCEEDED == status.error_code()) { - auto elapsed = - std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); auto diff = std::chrono::duration_cast(std::chrono::system_clock::now() - context.deadline()) .count(); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index b662e2655..4fe97437f 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -54,6 +54,10 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) tls_channel_credential_options_.watch_root_certs(); tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); + + int max_message_size = 1024 * 1024 * 16; + channel_arguments_.SetMaxReceiveMessageSize(max_message_size); + channel_arguments_.SetMaxSendMessageSize(max_message_size); SPDLOG_INFO("ClientManager[ResourceNamespace={}] created", resource_namespace_); } @@ -172,6 +176,7 @@ void ClientManagerImpl::healthCheck( SPDLOG_DEBUG("Prepare to send health-check to {}. Request: {}", target_host, request.DebugString()); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("HealthCheck to {}", target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -289,6 +294,7 @@ void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata SPDLOG_DEBUG("Prepare to send heartbeat to {}. Request: {}", target_host, request.DebugString()); auto client = getRpcClient(target_host, true); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Heartbeat to {}", target_host); invocation_context->remote_address = target_host; for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); @@ -390,6 +396,8 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met RpcClientSharedPtr client = getRpcClient(target_host); // Invocation context will be deleted in its onComplete() method. auto invocation_context = new InvocationContext(); + invocation_context->task_name = + fmt::format("Send message[] to {}", request.message().system_attribute().message_id(), target_host); invocation_context->remote_address = target_host; for (const auto& entry : metadata) { invocation_context->context.AddMetadata(entry.first, entry.second); @@ -551,6 +559,7 @@ void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metad } auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Query route of topic={} from {}", request.topic().name(), target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { @@ -703,6 +712,7 @@ void ClientManagerImpl::queryAssignment( }; auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("QueryAssignment from {}", target); invocation_context->remote_address = target; for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); @@ -719,6 +729,9 @@ void ClientManagerImpl::receiveMessage(const std::string& target_host, const Met RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("ReceiveMessage from queue[{}-{}-{}-{}], host={}", request.group().name(), + request.partition().topic().name(), request.partition().broker().name(), + request.partition().id(), target_host); invocation_context->remote_address = target_host; if (!metadata.empty()) { for (const auto& item : metadata) { @@ -1024,6 +1037,7 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Ack message[{}] against {}", request.message_id(), target); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -1078,6 +1092,7 @@ void ClientManagerImpl::nack(const std::string& target_host, const Metadata& met RpcClientSharedPtr client = getRpcClient(target_host); assert(client); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("Nack Message[{}] against {}", request.message_id(), target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -1144,6 +1159,8 @@ void ClientManagerImpl::endTransaction( SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("End transaction[{}] of message[] against {}", request.transaction_id(), + request.message_id(), target_host); invocation_context->remote_address = target_host; for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); @@ -1306,6 +1323,9 @@ void ClientManagerImpl::pullMessage( SPDLOG_DEBUG("PullMessage Request: {}, target_host={}", request.DebugString(), target_host); auto client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("PullMessage for queue[{}-{}-{}-{}] from {}", request.group().name(), + request.partition().topic().name(), request.partition().broker().name(), + request.partition().id(), target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); for (const auto& item : metadata) { @@ -1389,6 +1409,8 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( SPDLOG_DEBUG("ForwardMessageToDeadLetterQueue Request: {}", request.DebugString()); auto client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); + invocation_context->task_name = + fmt::format("Forward message[{}] to DLQ against {}", request.message_id(), target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); From 7e764b6e961e133df068d89e5629d7d8a82a5579 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 30 Sep 2021 04:36:57 +0000 Subject: [PATCH 083/133] Log RPC latency --- src/main/cpp/client/ClientManagerImpl.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 4fe97437f..0b810e7b0 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -866,9 +866,10 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess if (success) { body_digest_match = (digest.checksum() == checksum); if (body_digest_match) { - SPDLOG_DEBUG("MD5 checksum validation passed."); + SPDLOG_DEBUG("Body of message[{}] MD5 checksum validation passed.", message_ext.getMsgId()); } else { - SPDLOG_WARN("Body MD5 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + SPDLOG_WARN("Body of message[{}] MD5 checksum validation failed. Expect: {}, Actual: {}", + message_ext.getMsgId(), digest.checksum(), checksum); } } else { SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); @@ -882,12 +883,13 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess if (success) { body_digest_match = (checksum == digest.checksum()); if (body_digest_match) { - SPDLOG_DEBUG("SHA1 checksum validation passed"); + SPDLOG_DEBUG("Body of message[{}] SHA1 checksum validation passed", message_ext.getMsgId()); } else { - SPDLOG_WARN("Body SHA1 checksum validation failed. Expect: {}, Actual: {}", digest.checksum(), checksum); + SPDLOG_WARN("Body of message[{}] SHA1 checksum validation failed. Expect: {}, Actual: {}", + message_ext.getMsgId(), digest.checksum(), checksum); } } else { - SPDLOG_WARN("Failed to calculate SHA1 digest. Skip."); + SPDLOG_WARN("Failed to calculate SHA1 digest for message[{}]. Skip.", message_ext.getMsgId()); } break; } From 1c7474b4804d1bbd5c74aa386185c9ebb3f9b023 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 30 Sep 2021 05:05:16 +0000 Subject: [PATCH 084/133] Change ReceiveMessageResponse to trace --- src/main/cpp/client/ClientManagerImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 0b810e7b0..9a00c01ff 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1352,7 +1352,7 @@ void ClientManagerImpl::pullMessage( result.max_offset = invocation_context->response.max_offset(); switch (common.status().code()) { case google::rpc::Code::OK: { - SPDLOG_DEBUG("Received PullMessage Response: {}, host={}", invocation_context->response.DebugString(), + SPDLOG_TRACE("Received PullMessage Response: {}, host={}", invocation_context->response.DebugString(), invocation_context->remote_address); for (const auto& item : invocation_context->response.messages()) { MQMessageExt message; From ee61a086f21ef91a62e4183986818376ac47861c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 30 Sep 2021 05:09:45 +0000 Subject: [PATCH 085/133] Fix log level --- src/main/cpp/client/ClientManagerImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 9a00c01ff..18fdadfa0 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -759,7 +759,7 @@ void ClientManagerImpl::receiveMessage(const std::string& target_host, const Met const auto& common = invocation_context->response.common(); switch (common.status().code()) { case google::rpc::Code::OK: { - SPDLOG_DEBUG("ReceivedMessage Resonse: {}, host={}", invocation_context->response.DebugString(), + SPDLOG_TRACE("ReceivedMessage Resonse: {}, host={}", invocation_context->response.DebugString(), invocation_context->remote_address); for (auto& item : invocation_context->response.messages()) { MQMessageExt message_ext; From ffdab2988411ab48cf489f9c5555bf918f608878 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 30 Sep 2021 05:24:26 +0000 Subject: [PATCH 086/133] Trim response-text in log interceptor --- src/main/cpp/client/LogInterceptor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 869fc9508..3f5834508 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -47,7 +47,13 @@ void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* meth void* message = methods->GetRecvMessage(); if (message) { auto* response = reinterpret_cast(message); - SPDLOG_DEBUG("[Inbound] {}\n{}", client_rpc_info_->method(), response->DebugString()); + std::string&& response_text = response->DebugString(); + int limit = 1024; + if (response_text.size() <= limit) { + SPDLOG_DEBUG("[Inbound] {}\n{}", client_rpc_info_->method(), response_text); + } else { + SPDLOG_DEBUG("[Inbound] {}\n{}...", client_rpc_info_->method(), response_text.substr(0, limit)); + } } } } From f19a2372e4a2f3c8add9cf325ada5cbdead5a9f5 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Fri, 1 Oct 2021 20:46:44 +0800 Subject: [PATCH 087/133] Use latest protocol definition (#26) * Use latest protocol definition --- bazel/rocketmq_deps.bzl | 10 + proto/BUILD.bazel | 66 +---- proto/apache/rocketmq/v1/admin.proto | 113 -------- proto/apache/rocketmq/v1/definition.proto | 58 +++- proto/apache/rocketmq/v1/service.proto | 122 ++++++++- proto/google/protobuf/any.proto | 158 ----------- proto/google/protobuf/duration.proto | 116 -------- proto/google/protobuf/empty.proto | 52 ---- proto/google/protobuf/field_mask.proto | 245 ----------------- proto/google/protobuf/timestamp.proto | 147 ----------- proto/google/rpc/code.proto | 186 ------------- proto/google/rpc/error_details.proto | 249 ------------------ proto/google/rpc/status.proto | 47 ---- src/main/cpp/client/TopicAssignmentInfo.cpp | 1 + src/main/cpp/client/include/RpcClient.h | 2 +- src/main/cpp/remoting/BUILD.bazel | 13 + .../remoting/include/CommandCustomHeader.h | 12 + src/main/cpp/remoting/include/LanguageCode.h | 25 ++ .../include/QueryRouteRequestHeader.h | 28 ++ .../cpp/remoting/include/RemotingCommand.h | 8 + src/main/cpp/remoting/include/RequestCode.h | 16 ++ src/main/cpp/rocketmq/ClientImpl.cpp | 7 +- src/test/cpp/it/RpcClientTest.cpp | 17 +- src/test/cpp/ut/client/RpcClientTest.cpp | 6 +- .../cpp/ut/rocketmq/include/MQClientTest.h | 1 + 25 files changed, 305 insertions(+), 1400 deletions(-) delete mode 100644 proto/google/protobuf/any.proto delete mode 100644 proto/google/protobuf/duration.proto delete mode 100644 proto/google/protobuf/empty.proto delete mode 100644 proto/google/protobuf/field_mask.proto delete mode 100644 proto/google/protobuf/timestamp.proto delete mode 100644 proto/google/rpc/code.proto delete mode 100644 proto/google/rpc/error_details.proto delete mode 100644 proto/google/rpc/status.proto create mode 100644 src/main/cpp/remoting/BUILD.bazel create mode 100644 src/main/cpp/remoting/include/CommandCustomHeader.h create mode 100644 src/main/cpp/remoting/include/LanguageCode.h create mode 100644 src/main/cpp/remoting/include/QueryRouteRequestHeader.h create mode 100644 src/main/cpp/remoting/include/RemotingCommand.h create mode 100644 src/main/cpp/remoting/include/RequestCode.h diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index acee2ac11..1e2b907da 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -125,4 +125,14 @@ def rocketmq_deps(): build_file = "@org_apache_rocketmq//third_party:cpp_httplib.BUILD", strip_prefix = "cpp-httplib-0.9.4", urls = ["https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.9.4.tar.gz"], + ) + + maybe( + http_archive, + name = "com_google_googleapis", + sha256 = "e89f15d54b0ddab0cd41d18cb2299e5447db704e2b05ff141cb1769170671466", + urls = [ + "https://github.com/googleapis/googleapis/archive/af7fb72df59a814221b123a4d1acb3f6c3e6cc95.zip" + ], + strip_prefix = "googleapis-af7fb72df59a814221b123a4d1acb3f6c3e6cc95", ) \ No newline at end of file diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel index b99691133..0863bd830 100644 --- a/proto/BUILD.bazel +++ b/proto/BUILD.bazel @@ -2,37 +2,6 @@ load("@rules_proto//proto:defs.bzl", "proto_library") load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library", "cpp_grpc_compile") -proto_library( - name = "google_rpc_code", - srcs = [ - "google/rpc/code.proto", - ], - strip_import_prefix = "/proto", -) - -proto_library( - name = "google_rpc_error_details", - srcs = [ - "google/rpc/error_details.proto", - ], - deps = [ - "@com_google_protobuf//:duration_proto", - "@com_google_protobuf//:timestamp_proto", - ], - strip_import_prefix = "/proto", -) - -proto_library( - name = "google_rpc_status", - srcs = [ - "google/rpc/status.proto", - ], - deps = [ - "@com_google_protobuf//:any_proto", - ], - strip_import_prefix = "/proto", -) - proto_library( name = "apache_rocketmq_definition", srcs = [ @@ -43,6 +12,9 @@ proto_library( "@com_google_protobuf//:field_mask_proto", "@com_google_protobuf//:duration_proto", "@com_google_protobuf//:timestamp_proto", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", ], strip_import_prefix = "/proto", ) @@ -67,45 +39,25 @@ proto_library( ], deps = [ ":apache_rocketmq_definition", - ":google_rpc_code", - ":google_rpc_error_details", - ":google_rpc_status", "@com_google_protobuf//:any_proto", "@com_google_protobuf//:duration_proto", "@com_google_protobuf//:timestamp_proto", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", ], strip_import_prefix = "/proto", ) -cc_proto_library( - name = "rocketmq_proto_cc", - deps = [ - ":apache_rocketmq_service", - ], - visibility = ["//visibility:public"] -) - -cpp_grpc_compile( - name = "rocketmq_grpc_compile", - protos = [ - ":google_rpc_code", - ":google_rpc_error_details", - ":google_rpc_status", - ":apache_rocketmq_definition", - ":apache_rocketmq_admin", - ":apache_rocketmq_service", - ], -) - cpp_grpc_library( name = "rocketmq_grpc_library", protos = [ - ":google_rpc_code", - ":google_rpc_error_details", - ":google_rpc_status", ":apache_rocketmq_definition", ":apache_rocketmq_admin", ":apache_rocketmq_service", + "@com_google_googleapis//google/rpc:code_proto", + "@com_google_googleapis//google/rpc:error_details_proto", + "@com_google_googleapis//google/rpc:status_proto", ], visibility = ["//visibility:public"] ) \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v1/admin.proto index bd7b203d4..8ed203762 100644 --- a/proto/apache/rocketmq/v1/admin.proto +++ b/proto/apache/rocketmq/v1/admin.proto @@ -1,9 +1,5 @@ syntax = "proto3"; -import "apache/rocketmq/v1/definition.proto"; -import "google/protobuf/field_mask.proto"; -import "google/protobuf/empty.proto"; - package apache.rocketmq.v1; option cc_enable_arenas = true; @@ -30,116 +26,7 @@ message ChangeLogLevelResponse { string remark = 1; } -// Request for the UpdateTopic method. -message UpdateTopicRequest { - // Instance identifier - string instance_id = 1; - - // Required. The updated topic object. - Resource topic = 2; - - // Required. Indicates which fields in the provided topic to update. Must be - // specified and non-empty. Note that if `update_mask` contains - // "message_storage_policy" but the `message_storage_policy` is not set in - // the `topic` provided above, then the updated value is determined by the - // policy configured at the project or organization level. - google.protobuf.FieldMask update_mask = 3; -} - -// Request for the `ListTopics` method. -message ListTopicsRequest { - - string instance_id = 1; - - // Maximum number of topics to return. - int32 page_size = 2; - - // The value returned by the last `ListTopicsResponse`; indicates that this is - // a continuation of a prior `ListTopics` call, and that the system should - // return the next page of data. - string page_token = 3; -} - -// Response for the `ListTopics` method. -message ListTopicsResponse { - // The resulting topics. - repeated Resource topics = 1; - - // If not empty, indicates that there may be more topics that match the - // request; this value should be passed in a new `ListTopicsRequest`. - string next_page_token = 2; -} - -// Request for the `DeleteTopic` method. -message DeleteTopicRequest { - // Instance identifier - string instance_id = 1; - - // Required. Name of the topic to delete. - string topic = 2; -} - -// Request for GetTopic method -message GetTopicRequest { - Resource topic = 1; -} - -message DeleteConsumerGroupRequest { - string instance_id = 1; - string group_id = 2; -} - -message UpdateConsumerGroupRequest { - ConsumerData consumer_data = 1; - google.protobuf.FieldMask update_mask = 2; -} - -// Request for the `ListConsumerGroups` method. -message ListConsumerGroupsRequest { - - string instance_id = 1; - - // Maximum number of topics to return. - int32 page_size = 2; - - // The value returned by the last `ListTopicsResponse`; indicates that this is - // a continuation of a prior `ListTopics` call, and that the system should - // return the next page of data. - string page_token = 3; -} - -// Response for the `ListConsumerGroups` method. -message ListConsumerGroupsResponse { - // The resulting consumer groups. - repeated Resource consumer_group = 1; - - // If not empty, indicates that there may be more topics that match the - // request; this value should be passed in a new `ListTopicsRequest`. - string next_page_token = 2; -} - service Admin { rpc ChangeLogLevel(ChangeLogLevelRequest) returns (ChangeLogLevelResponse) { } - - rpc GetTopic(GetTopicRequest) returns (Resource) { - } - - rpc CreateTopic(Resource) returns (Resource) { - } - - rpc UpdateTopic(UpdateTopicRequest) returns (Resource) { - } - - rpc DeleteTopic(DeleteTopicRequest) returns (google.protobuf.Empty) { - } - - rpc CreateConsumerGroup(Resource) returns (Resource) { - } - - rpc DeleteConsumerGroup(DeleteConsumerGroupRequest) returns (google.protobuf.Empty) { - } - - rpc UpdateConsumerGroup(UpdateConsumerGroupRequest) returns (Resource) { - } } \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index c7e79278e..9ecd46a11 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -18,16 +18,22 @@ enum Permission { READ = 1; WRITE = 2; READ_WRITE = 3; + + reserved 4 to 64; } enum FilterType { TAG = 0; SQL = 1; + + reserved 2 to 64; } message FilterExpression { FilterType type = 1; string expression = 2; + + reserved 3 to 64; } // Dead lettering is done on a best effort basis. The same message might be @@ -42,6 +48,8 @@ message DeadLetterPolicy { // // If this parameter is 0, a default value of 5 is used. int32 max_delivery_attempts = 1; + + reserved 2 to 64; } message Resource { @@ -50,25 +58,37 @@ message Resource { // Resource name identifier, which remains unique within the abstract resource // namespace. string name = 2; + + reserved 3 to 64; } enum ConsumeModel { CLUSTERING = 0; BROADCASTING = 1; + + reserved 2 to 64; } -message ProducerData { Resource group = 1; } +message ProducerData { + Resource group = 1; + + reserved 2 to 64; +} enum ConsumePolicy { RESUME = 0; PLAYBACK = 1; DISCARD = 2; TARGET_TIMESTAMP = 3; + + reserved 4 to 64; } enum ConsumeMessageType { ACTIVE = 0; PASSIVE = 1; + + reserved 2 to 64; } message ConsumerData { @@ -83,27 +103,37 @@ message ConsumerData { DeadLetterPolicy dead_letter_policy = 5; ConsumeMessageType consume_type = 6; + + reserved 7 to 64; } message SubscriptionEntry { Resource topic = 1; FilterExpression expression = 2; + + reserved 3 to 64; } enum AddressScheme { IPv4 = 0; IPv6 = 1; DOMAIN_NAME = 2; + + reserved 3 to 64; } message Address { string host = 1; int32 port = 2; + + reserved 3 to 64; } message Endpoints { AddressScheme scheme = 1; repeated Address addresses = 2; + + reserved 3 to 64; } message Broker { @@ -119,6 +149,8 @@ message Broker { // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses Endpoints endpoints = 3; + + reserved 4 to 64; } message Partition { @@ -126,6 +158,8 @@ message Partition { int32 id = 2; Permission permission = 3; Broker broker = 4; + + reserved 5 to 64; } enum MessageType { @@ -140,6 +174,8 @@ enum MessageType { // Messages that are transactional. Only committed messages are delivered to // subscribers. TRANSACTION = 3; + + reserved 4 to 64; } enum DigestType { @@ -154,16 +190,22 @@ enum DigestType { // SHA-family has substantially fewer collision with fair amount of // computation. SHA1 = 2; + + reserved 3 to 64; } message Digest { DigestType type = 1; string checksum = 2; + + reserved 3 to 64; } enum Encoding { IDENTITY = 0; GZIP = 1; + + reserved 2 to 64; } message SystemAttribute { @@ -233,8 +275,10 @@ message SystemAttribute { // Trace context. string trace_context = 20; - // Delay time of first resolve orphaned transaction request from server. + // Delay time of first recover orphaned transaction request from server. google.protobuf.Duration orphaned_transaction_recovery_period = 21; + + reserved 22 to 64; } message Message { @@ -243,9 +287,15 @@ message Message { map user_attribute = 2; SystemAttribute system_attribute = 3; bytes body = 4; + + reserved 5 to 64; } -message Assignment { Partition Partition = 1; } +message Assignment { + Partition Partition = 1; + + reserved 2 to 64; +} enum QueryOffsetPolicy { // Use this option if client wishes to playback all existing messages. @@ -256,4 +306,6 @@ enum QueryOffsetPolicy { // Use this option if time-based seek is targeted. TIME_POINT = 2; + + reserved 3 to 64; } \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index ac7af1ca9..5deb63cd5 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -1,10 +1,8 @@ syntax = "proto3"; -import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/rpc/error_details.proto"; -import "google/rpc/code.proto"; import "google/rpc/status.proto"; import "apache/rocketmq/v1/definition.proto"; @@ -26,6 +24,8 @@ message ResponseCommon { google.rpc.RetryInfo retry_info = 4; google.rpc.DebugInfo debug_info = 5; google.rpc.ErrorInfo error_info = 6; + + reserved 7 to 64; } message QueryRouteRequest { @@ -33,22 +33,30 @@ message QueryRouteRequest { // Service access point Endpoints endpoints = 2; + + reserved 3 to 64; } message QueryRouteResponse { ResponseCommon common = 1; repeated Partition partitions = 2; + + reserved 3 to 64; } message SendMessageRequest { Message message = 1; Partition partition = 2; + + reserved 3 to 64; } message SendMessageResponse { ResponseCommon common = 1; string message_id = 2; string transaction_id = 3; + + reserved 4 to 64; } message QueryAssignmentRequest { @@ -58,11 +66,15 @@ message QueryAssignmentRequest { // Service access point Endpoints endpoints = 4; + + reserved 5 to 64; } message QueryAssignmentResponse { ResponseCommon common = 1; repeated Assignment assignments = 2; + + reserved 3 to 64; } message ReceiveMessageRequest { @@ -76,6 +88,8 @@ message ReceiveMessageRequest { google.protobuf.Duration invisible_duration = 8; google.protobuf.Duration await_time = 9; bool fifo_flag = 10; + + reserved 11 to 64; } message ReceiveMessageResponse { @@ -83,6 +97,8 @@ message ReceiveMessageResponse { repeated Message messages = 2; google.protobuf.Timestamp delivery_timestamp = 3; google.protobuf.Duration invisible_duration = 4; + + reserved 5 to 64; } message AckMessageRequest { @@ -94,9 +110,15 @@ message AckMessageRequest { int64 offset = 5; } string message_id = 6; + + reserved 7 to 64; } -message AckMessageResponse { ResponseCommon common = 1; } +message AckMessageResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message NackMessageRequest { Resource group = 1; @@ -106,9 +128,15 @@ message NackMessageRequest { string message_id = 5; int32 delivery_attempt = 6; int32 max_delivery_attempts = 7; + + reserved 8 to 64; } -message NackMessageResponse { ResponseCommon common = 1; } +message NackMessageResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message ForwardMessageToDeadLetterQueueRequest { Resource group = 1; @@ -118,9 +146,15 @@ message ForwardMessageToDeadLetterQueueRequest { string message_id = 5; int32 delivery_attempt = 6; int32 max_delivery_attempts = 7; + + reserved 8 to 64; } -message ForwardMessageToDeadLetterQueueResponse { ResponseCommon common = 1; } +message ForwardMessageToDeadLetterQueueResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message HeartbeatRequest { string client_id = 1; @@ -129,16 +163,28 @@ message HeartbeatRequest { ConsumerData consumer_data = 3; } bool fifo_flag = 4; + + reserved 5 to 64; } -message HeartbeatResponse { ResponseCommon common = 1; } +message HeartbeatResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message HealthCheckRequest { Resource group = 1; string client_host = 2; + + reserved 3 to 64; } -message HealthCheckResponse { ResponseCommon common = 1; } +message HealthCheckResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message EndTransactionRequest { Resource group = 1; @@ -155,19 +201,29 @@ message EndTransactionRequest { } Source source = 5; string trace_context = 6; + + reserved 7 to 64; } -message EndTransactionResponse { ResponseCommon common = 1; } +message EndTransactionResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message QueryOffsetRequest { Partition partition = 1; QueryOffsetPolicy policy = 2; google.protobuf.Timestamp time_point = 3; + + reserved 4 to 64; } message QueryOffsetResponse { ResponseCommon common = 1; int64 offset = 2; + + reserved 3 to 64; } message PullMessageRequest { @@ -178,6 +234,8 @@ message PullMessageRequest { google.protobuf.Duration await_time = 5; FilterExpression filter_expression = 6; string client_id = 7; + + reserved 8 to 64; } message PullMessageResponse { @@ -186,15 +244,23 @@ message PullMessageResponse { int64 next_offset = 3; int64 max_offset = 4; repeated Message messages = 5; + + reserved 6 to 64; } message UpdateOffsetRequest { Resource group = 1; Partition partition = 2; int64 partition_offset = 3; + + reserved 4 to 64; } -message UpdateOffsetResponse { ResponseCommon common = 1; } +message UpdateOffsetResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} message GenericPollingRequest { string client_id = 1; @@ -203,31 +269,49 @@ message GenericPollingRequest { Resource producer_group = 3; Resource consumer_group = 4; } + + reserved 5 to 64; } -message GenericPollingResponse { ResponseCommon common = 1; } +message GenericPollingResponse { + ResponseCommon common = 1; -message PrintThreadStackRequest { string mid = 1; } + reserved 2 to 64; +} + +message PrintThreadStackRequest { + string mid = 1; + + reserved 2 to 64; +} message PrintThreadStackResponse { ResponseCommon common = 1; string mid = 2; string stack_trace = 3; + + reserved 4 to 64; } message VerifyMessageConsumptionRequest { string mid = 1; Message message = 2; + + reserved 3 to 64; } message VerifyMessageConsumptionResponse { string mid = 1; ResponseCommon common = 2; + + reserved 3 to 64; } -message ResolveOrphanedTransactionRequest { +message RecoverOrphanedTransactionRequest { Message orphaned_transactional_message = 1; string transaction_id = 2; + + reserved 3 to 64; } message MultiplexingRequest { @@ -236,6 +320,8 @@ message MultiplexingRequest { PrintThreadStackResponse print_thread_stack_response = 2; VerifyMessageConsumptionResponse verify_message_consumption_response = 3; } + + reserved 4 to 64; } message MultiplexingResponse { @@ -243,16 +329,24 @@ message MultiplexingResponse { GenericPollingResponse polling_response = 1; PrintThreadStackRequest print_thread_stack_request = 2; VerifyMessageConsumptionRequest verify_message_consumption_request = 3; - ResolveOrphanedTransactionRequest resolve_orphaned_transaction_request = 4; + RecoverOrphanedTransactionRequest recover_orphaned_transaction_request = 4; } + + reserved 5 to 64; } message NotifyClientTerminationRequest { Resource group = 1; string client_id = 2; + + reserved 3 to 64; } -message NotifyClientTerminationResponse { ResponseCommon common = 1; } +message NotifyClientTerminationResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} service MessagingService { rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) {} diff --git a/proto/google/protobuf/any.proto b/proto/google/protobuf/any.proto deleted file mode 100644 index 6ed8a23cf..000000000 --- a/proto/google/protobuf/any.proto +++ /dev/null @@ -1,158 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option go_package = "google.golang.org/protobuf/types/known/anypb"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "AnyProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// `Any` contains an arbitrary serialized protocol buffer message along with a -// URL that describes the type of the serialized message. -// -// Protobuf library provides support to pack/unpack Any values in the form -// of utility functions or additional generated methods of the Any type. -// -// Example 1: Pack and unpack a message in C++. -// -// Foo foo = ...; -// Any any; -// any.PackFrom(foo); -// ... -// if (any.UnpackTo(&foo)) { -// ... -// } -// -// Example 2: Pack and unpack a message in Java. -// -// Foo foo = ...; -// Any any = Any.pack(foo); -// ... -// if (any.is(Foo.class)) { -// foo = any.unpack(Foo.class); -// } -// -// Example 3: Pack and unpack a message in Python. -// -// foo = Foo(...) -// any = Any() -// any.Pack(foo) -// ... -// if any.Is(Foo.DESCRIPTOR): -// any.Unpack(foo) -// ... -// -// Example 4: Pack and unpack a message in Go -// -// foo := &pb.Foo{...} -// any, err := anypb.New(foo) -// if err != nil { -// ... -// } -// ... -// foo := &pb.Foo{} -// if err := any.UnmarshalTo(foo); err != nil { -// ... -// } -// -// The pack methods provided by protobuf library will by default use -// 'type.googleapis.com/full.type.name' as the type URL and the unpack -// methods only use the fully qualified type name after the last '/' -// in the type URL, for example "foo.bar.com/x/y.z" will yield type -// name "y.z". -// -// -// JSON -// ==== -// The JSON representation of an `Any` value uses the regular -// representation of the deserialized, embedded message, with an -// additional field `@type` which contains the type URL. Example: -// -// package google.profile; -// message Person { -// string first_name = 1; -// string last_name = 2; -// } -// -// { -// "@type": "type.googleapis.com/google.profile.Person", -// "firstName": , -// "lastName": -// } -// -// If the embedded message type is well-known and has a custom JSON -// representation, that representation will be embedded adding a field -// `value` which holds the custom JSON in addition to the `@type` -// field. Example (for message [google.protobuf.Duration][]): -// -// { -// "@type": "type.googleapis.com/google.protobuf.Duration", -// "value": "1.212s" -// } -// -message Any { - // A URL/resource name that uniquely identifies the type of the serialized - // protocol buffer message. This string must contain at least - // one "/" character. The last segment of the URL's path must represent - // the fully qualified name of the type (as in - // `path/google.protobuf.Duration`). The name should be in a canonical form - // (e.g., leading "." is not accepted). - // - // In practice, teams usually precompile into the binary all types that they - // expect it to use in the context of Any. However, for URLs which use the - // scheme `http`, `https`, or no scheme, one can optionally set up a type - // server that maps type URLs to message definitions as follows: - // - // * If no scheme is provided, `https` is assumed. - // * An HTTP GET on the URL must yield a [google.protobuf.Type][] - // value in binary format, or produce an error. - // * Applications are allowed to cache lookup results based on the - // URL, or have them precompiled into a binary to avoid any - // lookup. Therefore, binary compatibility needs to be preserved - // on changes to types. (Use versioned type names to manage - // breaking changes.) - // - // Note: this functionality is not currently available in the official - // protobuf release, and it is not used for type URLs beginning with - // type.googleapis.com. - // - // Schemes other than `http`, `https` (or the empty scheme) might be - // used with implementation specific semantics. - // - string type_url = 1; - - // Must be a valid serialized protocol buffer of the above specified type. - bytes value = 2; -} diff --git a/proto/google/protobuf/duration.proto b/proto/google/protobuf/duration.proto deleted file mode 100644 index 81c3e369f..000000000 --- a/proto/google/protobuf/duration.proto +++ /dev/null @@ -1,116 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option cc_enable_arenas = true; -option go_package = "google.golang.org/protobuf/types/known/durationpb"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "DurationProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// A Duration represents a signed, fixed-length span of time represented -// as a count of seconds and fractions of seconds at nanosecond -// resolution. It is independent of any calendar and concepts like "day" -// or "month". It is related to Timestamp in that the difference between -// two Timestamp values is a Duration and it can be added or subtracted -// from a Timestamp. Range is approximately +-10,000 years. -// -// # Examples -// -// Example 1: Compute Duration from two Timestamps in pseudo code. -// -// Timestamp start = ...; -// Timestamp end = ...; -// Duration duration = ...; -// -// duration.seconds = end.seconds - start.seconds; -// duration.nanos = end.nanos - start.nanos; -// -// if (duration.seconds < 0 && duration.nanos > 0) { -// duration.seconds += 1; -// duration.nanos -= 1000000000; -// } else if (duration.seconds > 0 && duration.nanos < 0) { -// duration.seconds -= 1; -// duration.nanos += 1000000000; -// } -// -// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. -// -// Timestamp start = ...; -// Duration duration = ...; -// Timestamp end = ...; -// -// end.seconds = start.seconds + duration.seconds; -// end.nanos = start.nanos + duration.nanos; -// -// if (end.nanos < 0) { -// end.seconds -= 1; -// end.nanos += 1000000000; -// } else if (end.nanos >= 1000000000) { -// end.seconds += 1; -// end.nanos -= 1000000000; -// } -// -// Example 3: Compute Duration from datetime.timedelta in Python. -// -// td = datetime.timedelta(days=3, minutes=10) -// duration = Duration() -// duration.FromTimedelta(td) -// -// # JSON Mapping -// -// In JSON format, the Duration type is encoded as a string rather than an -// object, where the string ends in the suffix "s" (indicating seconds) and -// is preceded by the number of seconds, with nanoseconds expressed as -// fractional seconds. For example, 3 seconds with 0 nanoseconds should be -// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should -// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 -// microsecond should be expressed in JSON format as "3.000001s". -// -// -message Duration { - // Signed seconds of the span of time. Must be from -315,576,000,000 - // to +315,576,000,000 inclusive. Note: these bounds are computed from: - // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years - int64 seconds = 1; - - // Signed fractions of a second at nanosecond resolution of the span - // of time. Durations less than one second are represented with a 0 - // `seconds` field and a positive or negative `nanos` field. For durations - // of one second or more, a non-zero value for the `nanos` field must be - // of the same sign as the `seconds` field. Must be from -999,999,999 - // to +999,999,999 inclusive. - int32 nanos = 2; -} diff --git a/proto/google/protobuf/empty.proto b/proto/google/protobuf/empty.proto deleted file mode 100644 index 5f992de94..000000000 --- a/proto/google/protobuf/empty.proto +++ /dev/null @@ -1,52 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option go_package = "google.golang.org/protobuf/types/known/emptypb"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "EmptyProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; -option cc_enable_arenas = true; - -// A generic empty message that you can re-use to avoid defining duplicated -// empty messages in your APIs. A typical example is to use it as the request -// or the response type of an API method. For instance: -// -// service Foo { -// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); -// } -// -// The JSON representation for `Empty` is empty JSON object `{}`. -message Empty {} diff --git a/proto/google/protobuf/field_mask.proto b/proto/google/protobuf/field_mask.proto deleted file mode 100644 index 6b5104f18..000000000 --- a/proto/google/protobuf/field_mask.proto +++ /dev/null @@ -1,245 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "FieldMaskProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; -option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb"; -option cc_enable_arenas = true; - -// `FieldMask` represents a set of symbolic field paths, for example: -// -// paths: "f.a" -// paths: "f.b.d" -// -// Here `f` represents a field in some root message, `a` and `b` -// fields in the message found in `f`, and `d` a field found in the -// message in `f.b`. -// -// Field masks are used to specify a subset of fields that should be -// returned by a get operation or modified by an update operation. -// Field masks also have a custom JSON encoding (see below). -// -// # Field Masks in Projections -// -// When used in the context of a projection, a response message or -// sub-message is filtered by the API to only contain those fields as -// specified in the mask. For example, if the mask in the previous -// example is applied to a response message as follows: -// -// f { -// a : 22 -// b { -// d : 1 -// x : 2 -// } -// y : 13 -// } -// z: 8 -// -// The result will not contain specific values for fields x,y and z -// (their value will be set to the default, and omitted in proto text -// output): -// -// -// f { -// a : 22 -// b { -// d : 1 -// } -// } -// -// A repeated field is not allowed except at the last position of a -// paths string. -// -// If a FieldMask object is not present in a get operation, the -// operation applies to all fields (as if a FieldMask of all fields -// had been specified). -// -// Note that a field mask does not necessarily apply to the -// top-level response message. In case of a REST get operation, the -// field mask applies directly to the response, but in case of a REST -// list operation, the mask instead applies to each individual message -// in the returned resource list. In case of a REST custom method, -// other definitions may be used. Where the mask applies will be -// clearly documented together with its declaration in the API. In -// any case, the effect on the returned resource/resources is required -// behavior for APIs. -// -// # Field Masks in Update Operations -// -// A field mask in update operations specifies which fields of the -// targeted resource are going to be updated. The API is required -// to only change the values of the fields as specified in the mask -// and leave the others untouched. If a resource is passed in to -// describe the updated values, the API ignores the values of all -// fields not covered by the mask. -// -// If a repeated field is specified for an update operation, new values will -// be appended to the existing repeated field in the target resource. Note that -// a repeated field is only allowed in the last position of a `paths` string. -// -// If a sub-message is specified in the last position of the field mask for an -// update operation, then new value will be merged into the existing sub-message -// in the target resource. -// -// For example, given the target message: -// -// f { -// b { -// d: 1 -// x: 2 -// } -// c: [1] -// } -// -// And an update message: -// -// f { -// b { -// d: 10 -// } -// c: [2] -// } -// -// then if the field mask is: -// -// paths: ["f.b", "f.c"] -// -// then the result will be: -// -// f { -// b { -// d: 10 -// x: 2 -// } -// c: [1, 2] -// } -// -// An implementation may provide options to override this default behavior for -// repeated and message fields. -// -// In order to reset a field's value to the default, the field must -// be in the mask and set to the default value in the provided resource. -// Hence, in order to reset all fields of a resource, provide a default -// instance of the resource and set all fields in the mask, or do -// not provide a mask as described below. -// -// If a field mask is not present on update, the operation applies to -// all fields (as if a field mask of all fields has been specified). -// Note that in the presence of schema evolution, this may mean that -// fields the client does not know and has therefore not filled into -// the request will be reset to their default. If this is unwanted -// behavior, a specific service may require a client to always specify -// a field mask, producing an error if not. -// -// As with get operations, the location of the resource which -// describes the updated values in the request message depends on the -// operation kind. In any case, the effect of the field mask is -// required to be honored by the API. -// -// ## Considerations for HTTP REST -// -// The HTTP kind of an update operation which uses a field mask must -// be set to PATCH instead of PUT in order to satisfy HTTP semantics -// (PUT must only be used for full updates). -// -// # JSON Encoding of Field Masks -// -// In JSON, a field mask is encoded as a single string where paths are -// separated by a comma. Fields name in each path are converted -// to/from lower-camel naming conventions. -// -// As an example, consider the following message declarations: -// -// message Profile { -// User user = 1; -// Photo photo = 2; -// } -// message User { -// string display_name = 1; -// string address = 2; -// } -// -// In proto a field mask for `Profile` may look as such: -// -// mask { -// paths: "user.display_name" -// paths: "photo" -// } -// -// In JSON, the same mask is represented as below: -// -// { -// mask: "user.displayName,photo" -// } -// -// # Field Masks and Oneof Fields -// -// Field masks treat fields in oneofs just as regular fields. Consider the -// following message: -// -// message SampleMessage { -// oneof test_oneof { -// string name = 4; -// SubMessage sub_message = 9; -// } -// } -// -// The field mask can be: -// -// mask { -// paths: "name" -// } -// -// Or: -// -// mask { -// paths: "sub_message" -// } -// -// Note that oneof type names ("test_oneof" in this case) cannot be used in -// paths. -// -// ## Field Mask Verification -// -// The implementation of any API method which has a FieldMask type field in the -// request should verify the included field paths, and return an -// `INVALID_ARGUMENT` error if any path is unmappable. -message FieldMask { - // The set of field mask paths. - repeated string paths = 1; -} diff --git a/proto/google/protobuf/timestamp.proto b/proto/google/protobuf/timestamp.proto deleted file mode 100644 index 3b2df6d91..000000000 --- a/proto/google/protobuf/timestamp.proto +++ /dev/null @@ -1,147 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option cc_enable_arenas = true; -option go_package = "google.golang.org/protobuf/types/known/timestamppb"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "TimestampProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// A Timestamp represents a point in time independent of any time zone or local -// calendar, encoded as a count of seconds and fractions of seconds at -// nanosecond resolution. The count is relative to an epoch at UTC midnight on -// January 1, 1970, in the proleptic Gregorian calendar which extends the -// Gregorian calendar backwards to year one. -// -// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap -// second table is needed for interpretation, using a [24-hour linear -// smear](https://developers.google.com/time/smear). -// -// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By -// restricting to that range, we ensure that we can convert to and from [RFC -// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. -// -// # Examples -// -// Example 1: Compute Timestamp from POSIX `time()`. -// -// Timestamp timestamp; -// timestamp.set_seconds(time(NULL)); -// timestamp.set_nanos(0); -// -// Example 2: Compute Timestamp from POSIX `gettimeofday()`. -// -// struct timeval tv; -// gettimeofday(&tv, NULL); -// -// Timestamp timestamp; -// timestamp.set_seconds(tv.tv_sec); -// timestamp.set_nanos(tv.tv_usec * 1000); -// -// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. -// -// FILETIME ft; -// GetSystemTimeAsFileTime(&ft); -// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; -// -// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z -// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. -// Timestamp timestamp; -// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); -// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); -// -// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. -// -// long millis = System.currentTimeMillis(); -// -// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) -// .setNanos((int) ((millis % 1000) * 1000000)).build(); -// -// -// Example 5: Compute Timestamp from Java `Instant.now()`. -// -// Instant now = Instant.now(); -// -// Timestamp timestamp = -// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) -// .setNanos(now.getNano()).build(); -// -// -// Example 6: Compute Timestamp from current time in Python. -// -// timestamp = Timestamp() -// timestamp.GetCurrentTime() -// -// # JSON Mapping -// -// In JSON format, the Timestamp type is encoded as a string in the -// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the -// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" -// where {year} is always expressed using four digits while {month}, {day}, -// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional -// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), -// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required. A proto3 JSON serializer should always use UTC (as indicated by -// "Z") when printing the Timestamp type and a proto3 JSON parser should be -// able to accept both UTC and other timezones (as indicated by an offset). -// -// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past -// 01:30 UTC on January 15, 2017. -// -// In JavaScript, one can convert a Date object to this format using the -// standard -// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) -// method. In Python, a standard `datetime.datetime` object can be converted -// to this format using -// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with -// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use -// the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D -// ) to obtain a formatter capable of generating timestamps in this format. -// -// -message Timestamp { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} diff --git a/proto/google/rpc/code.proto b/proto/google/rpc/code.proto deleted file mode 100644 index 98ae0ac18..000000000 --- a/proto/google/rpc/code.proto +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; -option java_multiple_files = true; -option java_outer_classname = "CodeProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - -// The canonical error codes for gRPC APIs. -// -// -// Sometimes multiple error codes may apply. Services should return -// the most specific error code that applies. For example, prefer -// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. -// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. -enum Code { - // Not an error; returned on success - // - // HTTP Mapping: 200 OK - OK = 0; - - // The operation was cancelled, typically by the caller. - // - // HTTP Mapping: 499 Client Closed Request - CANCELLED = 1; - - // Unknown error. For example, this error may be returned when - // a `Status` value received from another address space belongs to - // an error space that is not known in this address space. Also - // errors raised by APIs that do not return enough error information - // may be converted to this error. - // - // HTTP Mapping: 500 Internal Server Error - UNKNOWN = 2; - - // The client specified an invalid argument. Note that this differs - // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments - // that are problematic regardless of the state of the system - // (e.g., a malformed file name). - // - // HTTP Mapping: 400 Bad Request - INVALID_ARGUMENT = 3; - - // The deadline expired before the operation could complete. For operations - // that change the state of the system, this error may be returned - // even if the operation has completed successfully. For example, a - // successful response from a server could have been delayed long - // enough for the deadline to expire. - // - // HTTP Mapping: 504 Gateway Timeout - DEADLINE_EXCEEDED = 4; - - // Some requested entity (e.g., file or directory) was not found. - // - // Note to server developers: if a request is denied for an entire class - // of users, such as gradual feature rollout or undocumented whitelist, - // `NOT_FOUND` may be used. If a request is denied for some users within - // a class of users, such as user-based access control, `PERMISSION_DENIED` - // must be used. - // - // HTTP Mapping: 404 Not Found - NOT_FOUND = 5; - - // The entity that a client attempted to create (e.g., file or directory) - // already exists. - // - // HTTP Mapping: 409 Conflict - ALREADY_EXISTS = 6; - - // The caller does not have permission to execute the specified - // operation. `PERMISSION_DENIED` must not be used for rejections - // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` - // instead for those errors). `PERMISSION_DENIED` must not be - // used if the caller can not be identified (use `UNAUTHENTICATED` - // instead for those errors). This error code does not imply the - // request is valid or the requested entity exists or satisfies - // other pre-conditions. - // - // HTTP Mapping: 403 Forbidden - PERMISSION_DENIED = 7; - - // The request does not have valid authentication credentials for the - // operation. - // - // HTTP Mapping: 401 Unauthorized - UNAUTHENTICATED = 16; - - // Some resource has been exhausted, perhaps a per-user quota, or - // perhaps the entire file system is out of space. - // - // HTTP Mapping: 429 Too Many Requests - RESOURCE_EXHAUSTED = 8; - - // The operation was rejected because the system is not in a state - // required for the operation's execution. For example, the directory - // to be deleted is non-empty, an rmdir operation is applied to - // a non-directory, etc. - // - // Service implementors can use the following guidelines to decide - // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: - // (a) Use `UNAVAILABLE` if the client can retry just the failing call. - // (b) Use `ABORTED` if the client should retry at a higher level - // (e.g., when a client-specified test-and-set fails, indicating the - // client should restart a read-modify-write sequence). - // (c) Use `FAILED_PRECONDITION` if the client should not retry until - // the system state has been explicitly fixed. E.g., if an "rmdir" - // fails because the directory is non-empty, `FAILED_PRECONDITION` - // should be returned since the client should not retry unless - // the files are deleted from the directory. - // - // HTTP Mapping: 400 Bad Request - FAILED_PRECONDITION = 9; - - // The operation was aborted, typically due to a concurrency issue such as - // a sequencer check failure or transaction abort. - // - // See the guidelines above for deciding between `FAILED_PRECONDITION`, - // `ABORTED`, and `UNAVAILABLE`. - // - // HTTP Mapping: 409 Conflict - ABORTED = 10; - - // The operation was attempted past the valid range. E.g., seeking or - // reading past end-of-file. - // - // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may - // be fixed if the system state changes. For example, a 32-bit file - // system will generate `INVALID_ARGUMENT` if asked to read at an - // offset that is not in the range [0,2^32-1], but it will generate - // `OUT_OF_RANGE` if asked to read from an offset past the current - // file size. - // - // There is a fair bit of overlap between `FAILED_PRECONDITION` and - // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific - // error) when it applies so that callers who are iterating through - // a space can easily look for an `OUT_OF_RANGE` error to detect when - // they are done. - // - // HTTP Mapping: 400 Bad Request - OUT_OF_RANGE = 11; - - // The operation is not implemented or is not supported/enabled in this - // service. - // - // HTTP Mapping: 501 Not Implemented - UNIMPLEMENTED = 12; - - // Internal errors. This means that some invariants expected by the - // underlying system have been broken. This error code is reserved - // for serious errors. - // - // HTTP Mapping: 500 Internal Server Error - INTERNAL = 13; - - // The service is currently unavailable. This is most likely a - // transient condition, which can be corrected by retrying with - // a backoff. Note that it is not always safe to retry - // non-idempotent operations. - // - // See the guidelines above for deciding between `FAILED_PRECONDITION`, - // `ABORTED`, and `UNAVAILABLE`. - // - // HTTP Mapping: 503 Service Unavailable - UNAVAILABLE = 14; - - // Unrecoverable data loss or corruption. - // - // HTTP Mapping: 500 Internal Server Error - DATA_LOSS = 15; -} diff --git a/proto/google/rpc/error_details.proto b/proto/google/rpc/error_details.proto deleted file mode 100644 index c4d6c4b78..000000000 --- a/proto/google/rpc/error_details.proto +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -import "google/protobuf/duration.proto"; - -option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; -option java_multiple_files = true; -option java_outer_classname = "ErrorDetailsProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - -// Describes when the clients can retry a failed request. Clients could ignore -// the recommendation here or retry when this information is missing from error -// responses. -// -// It's always recommended that clients should use exponential backoff when -// retrying. -// -// Clients should wait until `retry_delay` amount of time has passed since -// receiving the error response before retrying. If retrying requests also -// fail, clients should use an exponential backoff scheme to gradually increase -// the delay between retries based on `retry_delay`, until either a maximum -// number of retries have been reached or a maximum retry delay cap has been -// reached. -message RetryInfo { - // Clients should wait at least this long between retrying the same request. - google.protobuf.Duration retry_delay = 1; -} - -// Describes additional debugging info. -message DebugInfo { - // The stack trace entries indicating where the error occurred. - repeated string stack_entries = 1; - - // Additional debugging information provided by the server. - string detail = 2; -} - -// Describes how a quota check failed. -// -// For example if a daily limit was exceeded for the calling project, -// a service could respond with a QuotaFailure detail containing the project -// id and the description of the quota limit that was exceeded. If the -// calling project hasn't enabled the service in the developer console, then -// a service could respond with the project id and set `service_disabled` -// to true. -// -// Also see RetryInfo and Help types for other details about handling a -// quota failure. -message QuotaFailure { - // A message type used to describe a single quota violation. For example, a - // daily quota or a custom quota that was exceeded. - message Violation { - // The subject on which the quota check failed. - // For example, "clientip:" or "project:". - string subject = 1; - - // A description of how the quota check failed. Clients can use this - // description to find more about the quota configuration in the service's - // public documentation, or find the relevant quota limit to adjust through - // developer console. - // - // For example: "Service disabled" or "Daily Limit for read operations - // exceeded". - string description = 2; - } - - // Describes all quota violations. - repeated Violation violations = 1; -} - -// Describes the cause of the error with structured details. -// -// Example of an error when contacting the "pubsub.googleapis.com" API when it -// is not enabled: -// -// { "reason": "API_DISABLED" -// "domain": "googleapis.com" -// "metadata": { -// "resource": "projects/123", -// "service": "pubsub.googleapis.com" -// } -// } -// -// This response indicates that the pubsub.googleapis.com API is not enabled. -// -// Example of an error that is returned when attempting to create a Spanner -// instance in a region that is out of stock: -// -// { "reason": "STOCKOUT" -// "domain": "spanner.googleapis.com", -// "metadata": { -// "availableRegions": "us-central1,us-east2" -// } -// } -message ErrorInfo { - // The reason of the error. This is a constant value that identifies the - // proximate cause of the error. Error reasons are unique within a particular - // domain of errors. This should be at most 63 characters and match - // /[A-Z0-9_]+/. - string reason = 1; - - // The logical grouping to which the "reason" belongs. The error domain - // is typically the registered service name of the tool or product that - // generates the error. Example: "pubsub.googleapis.com". If the error is - // generated by some common infrastructure, the error domain must be a - // globally unique value that identifies the infrastructure. For Google API - // infrastructure, the error domain is "googleapis.com". - string domain = 2; - - // Additional structured details about this error. - // - // Keys should match /[a-zA-Z0-9-_]/ and be limited to 64 characters in - // length. When identifying the current value of an exceeded limit, the units - // should be contained in the key, not the value. For example, rather than - // {"instanceLimit": "100/request"}, should be returned as, - // {"instanceLimitPerRequest": "100"}, if the client exceeds the number of - // instances that can be created in a single (batch) request. - map metadata = 3; -} - -// Describes what preconditions have failed. -// -// For example, if an RPC failed because it required the Terms of Service to be -// acknowledged, it could list the terms of service violation in the -// PreconditionFailure message. -message PreconditionFailure { - // A message type used to describe a single precondition failure. - message Violation { - // The type of PreconditionFailure. We recommend using a service-specific - // enum type to define the supported precondition violation subjects. For - // example, "TOS" for "Terms of Service violation". - string type = 1; - - // The subject, relative to the type, that failed. - // For example, "google.com/cloud" relative to the "TOS" type would indicate - // which terms of service is being referenced. - string subject = 2; - - // A description of how the precondition failed. Developers can use this - // description to understand how to fix the failure. - // - // For example: "Terms of service not accepted". - string description = 3; - } - - // Describes all precondition violations. - repeated Violation violations = 1; -} - -// Describes violations in a client request. This error type focuses on the -// syntactic aspects of the request. -message BadRequest { - // A message type used to describe a single bad request field. - message FieldViolation { - // A path leading to a field in the request body. The value will be a - // sequence of dot-separated identifiers that identify a protocol buffer - // field. E.g., "field_violations.field" would identify this field. - string field = 1; - - // A description of why the request element is bad. - string description = 2; - } - - // Describes all violations in a client request. - repeated FieldViolation field_violations = 1; -} - -// Contains metadata about the request that clients can attach when filing a bug -// or providing other forms of feedback. -message RequestInfo { - // An opaque string that should only be interpreted by the service generating - // it. For example, it can be used to identify requests in the service's logs. - string request_id = 1; - - // Any data that was used to serve this request. For example, an encrypted - // stack trace that can be sent back to the service provider for debugging. - string serving_data = 2; -} - -// Describes the resource that is being accessed. -message ResourceInfo { - // A name for the type of resource being accessed, e.g. "sql table", - // "cloud storage bucket", "file", "Google calendar"; or the type URL - // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". - string resource_type = 1; - - // The name of the resource being accessed. For example, a shared calendar - // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current - // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. - string resource_name = 2; - - // The owner of the resource (optional). - // For example, "user:" or "project:". - string owner = 3; - - // Describes what error is encountered when accessing this resource. - // For example, updating a cloud project may require the `writer` permission - // on the developer console project. - string description = 4; -} - -// Provides links to documentation or for performing an out of band action. -// -// For example, if a quota check failed with an error indicating the calling -// project hasn't enabled the accessed service, this can contain a URL pointing -// directly to the right place in the developer console to flip the bit. -message Help { - // Describes a URL link. - message Link { - // Describes what the link offers. - string description = 1; - - // The URL of the link. - string url = 2; - } - - // URL(s) pointing to additional information on handling the current error. - repeated Link links = 1; -} - -// Provides a localized error message that is safe to return to the user -// which can be attached to an RPC error. -message LocalizedMessage { - // The locale used following the specification defined at - // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. - // Examples are: "en-US", "fr-CH", "es-MX" - string locale = 1; - - // The localized error message in the above locale. - string message = 2; -} diff --git a/proto/google/rpc/status.proto b/proto/google/rpc/status.proto deleted file mode 100644 index 3b1f7a932..000000000 --- a/proto/google/rpc/status.proto +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.rpc; - -import "google/protobuf/any.proto"; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; -option java_multiple_files = true; -option java_outer_classname = "StatusProto"; -option java_package = "com.google.rpc"; -option objc_class_prefix = "RPC"; - -// The `Status` type defines a logical error model that is suitable for -// different programming environments, including REST APIs and RPC APIs. It is -// used by [gRPC](https://github.com/grpc). Each `Status` message contains -// three pieces of data: error code, error message, and error details. -// -// You can find out more about this error model and how to work with it in the -// [API Design Guide](https://cloud.google.com/apis/design/errors). -message Status { - // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - int32 code = 1; - - // A developer-facing error message, which should be in English. Any - // user-facing error message should be localized and sent in the - // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - string message = 2; - - // A list of messages that carry the error details. There is a common set of - // message types for APIs to use. - repeated google.protobuf.Any details = 3; -} diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index f35f98612..3df672457 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -1,6 +1,7 @@ #include "TopicAssignmentInfo.h" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" +#include "google/rpc/code.pb.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index 416001e94..86522bdb6 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -48,7 +48,7 @@ using PrintThreadStackRequest = rmq::PrintThreadStackRequest; using PrintThreadStackResponse = rmq::PrintThreadStackResponse; using VerifyMessageConsumptionRequest = rmq::VerifyMessageConsumptionRequest; using VerifyMessageConsumptionResponse = rmq::VerifyMessageConsumptionResponse; -using ResolveOrphanedTransactionRequest = rmq::ResolveOrphanedTransactionRequest; +using RecoverOrphanedTransactionRequest = rmq::RecoverOrphanedTransactionRequest; using QueryOffsetRequest = rmq::QueryOffsetRequest; using QueryOffsetResponse = rmq::QueryOffsetResponse; using PullMessageRequest = rmq::PullMessageRequest; diff --git a/src/main/cpp/remoting/BUILD.bazel b/src/main/cpp/remoting/BUILD.bazel new file mode 100644 index 000000000..59b8fe5d1 --- /dev/null +++ b/src/main/cpp/remoting/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "remoting", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/remoting/include", + deps = [ + "//api:rocketmq_interface", + "@asio//:asio", + ] +) \ No newline at end of file diff --git a/src/main/cpp/remoting/include/CommandCustomHeader.h b/src/main/cpp/remoting/include/CommandCustomHeader.h new file mode 100644 index 000000000..de1269cca --- /dev/null +++ b/src/main/cpp/remoting/include/CommandCustomHeader.h @@ -0,0 +1,12 @@ +#pragma once + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class CommandCustomHeader { +public: + virtual ~CommandCustomHeader() = default; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/LanguageCode.h b/src/main/cpp/remoting/include/LanguageCode.h new file mode 100644 index 000000000..7ff6d4729 --- /dev/null +++ b/src/main/cpp/remoting/include/LanguageCode.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class LanguageCode : std::uint8_t +{ + JAVA = 0, + CPP = 1, + DOTNET = 2, + PYTHON = 3, + DELPHI = 4, + ERLANG = 5, + RUBY = 6, + OTHER = 7, + HTTP = 8, + Go = 9, + PHP = 10, + OMS = 11, +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h new file mode 100644 index 000000000..7b9468dd5 --- /dev/null +++ b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "absl/strings/string_view.h" + +#include "CommandCustomHeader.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class QueryRouteRequestHeader : public CommandCustomHeader { +public: + ~QueryRouteRequestHeader() override = default; + + void topic(absl::string_view topic) { + topic_ = std::string(topic.data(), topic.length()); + } + + const std::string& topic() const { + return topic_; + } + +private: + std::string topic_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RemotingCommand.h b/src/main/cpp/remoting/include/RemotingCommand.h new file mode 100644 index 000000000..fbf361673 --- /dev/null +++ b/src/main/cpp/remoting/include/RemotingCommand.h @@ -0,0 +1,8 @@ +#pragma once + +#include "LanguageCode.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RequestCode.h b/src/main/cpp/remoting/include/RequestCode.h new file mode 100644 index 000000000..d0a933dc6 --- /dev/null +++ b/src/main/cpp/remoting/include/RequestCode.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class RequestCode : std::int32_t +{ + SendMessage = 10, + PullMessage = 11, + QueryRoute = 105, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index b4a247673..e868c4c99 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -13,6 +13,7 @@ #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "apache/rocketmq/v1/definition.pb.h" +#include "google/rpc/code.pb.h" #include "ClientImpl.h" #include "ClientManagerFactory.h" @@ -358,12 +359,12 @@ void ClientImpl::onMultiplexingResponse(const InvocationContextresponse.resolve_orphaned_transaction_request().orphaned_transactional_message(); + case MultiplexingResponse::TypeCase::kRecoverOrphanedTransactionRequest: { + auto orphan = ctx->response.recover_orphaned_transaction_request().orphaned_transactional_message(); MQMessageExt message; if (client_manager_->wrapMessage(orphan, message)) { MessageAccessor::setTargetEndpoint(message, ctx->remote_address); - const std::string& transaction_id = ctx->response.resolve_orphaned_transaction_request().transaction_id(); + const std::string& transaction_id = ctx->response.recover_orphaned_transaction_request().transaction_id(); resolveOrphanedTransactionalMessage(transaction_id, message); } else { SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 1b6d949b6..8b61c2453 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -1,3 +1,13 @@ +#include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "apache/rocketmq/v1/service.pb.h" +#include "google/rpc/code.pb.h" +#include "spdlog/spdlog.h" +#include "gtest/gtest.h" + #include "ClientConfigImpl.h" #include "InvocationContext.h" #include "LogInterceptorFactory.h" @@ -7,15 +17,8 @@ #include "TlsHelper.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" -#include "absl/container/flat_hash_set.h" -#include "apache/rocketmq/v1/service.pb.h" #include "rocketmq/CredentialsProvider.h" #include "rocketmq/Logger.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include -#include -#include using namespace testing; diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp index 7f447df7d..b4d841d4f 100644 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -1,6 +1,8 @@ #include "RpcClientMock.h" -#include -#include + +#include "google/rpc/code.pb.h" +#include "grpcpp/impl/grpc_library.h" +#include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 6c13f20c9..05e554ded 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -3,6 +3,7 @@ #include #include +#include "google/rpc/code.pb.h" #include "grpc/grpc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" From 8eeb23255421380a100202a661d807a9ce075814 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Sat, 2 Oct 2021 16:04:06 +0800 Subject: [PATCH 088/133] Configure Keep-alive ping and disable client retries (#27) * Configure Keep-alive ping and disable client retries --- example/rocketmq/ExampleProducer.cpp | 2 +- src/main/cpp/client/ClientManagerImpl.cpp | 20 ++++ src/main/cpp/remoting/BUILD.bazel | 2 + .../cpp/remoting/QueryRouteRequestHeader.cpp | 13 +++ src/main/cpp/remoting/RemotingCommand.cpp | 97 +++++++++++++++++++ .../remoting/include/CommandCustomHeader.h | 5 + src/main/cpp/remoting/include/LanguageCode.h | 2 +- .../include/QueryRouteRequestHeader.h | 2 + .../cpp/remoting/include/RemotingCommand.h | 84 +++++++++++++++- .../remoting/include/RemotingCommandType.h | 15 +++ src/main/cpp/remoting/include/ResponseCode.h | 16 +++ src/main/cpp/remoting/include/Version.h | 15 +++ src/test/cpp/ut/remoting/BUILD.bazel | 23 +++++ .../remoting/QueryRouteRequestHeaderTest.cpp | 20 ++++ .../cpp/ut/remoting/RemotingCommandTest.cpp | 46 +++++++++ 15 files changed, 359 insertions(+), 3 deletions(-) create mode 100644 src/main/cpp/remoting/QueryRouteRequestHeader.cpp create mode 100644 src/main/cpp/remoting/RemotingCommand.cpp create mode 100644 src/main/cpp/remoting/include/RemotingCommandType.h create mode 100644 src/main/cpp/remoting/include/ResponseCode.h create mode 100644 src/main/cpp/remoting/include/Version.h create mode 100644 src/test/cpp/ut/remoting/BUILD.bazel create mode 100644 src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp create mode 100644 src/test/cpp/ut/remoting/RemotingCommandTest.cpp diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 3551bf50d..8dee43623 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -65,7 +65,7 @@ int main(int argc, char* argv[]) { std::thread stats_thread(stats_lambda); - std::string body = randomString(1024 * 1024 * 4); + std::string body = randomString(1024 * 4); std::cout << "Message body: " << body << std::endl; message.setBody(body); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 18fdadfa0..0dfc00b32 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -58,6 +58,26 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) int max_message_size = 1024 * 1024 * 16; channel_arguments_.SetMaxReceiveMessageSize(max_message_size); channel_arguments_.SetMaxSendMessageSize(max_message_size); + + /* + * Keep-alive settings: + * https://github.com/grpc/grpc/blob/master/doc/keepalive.md + * Keep-alive ping timeout duration: 3s + * Keep-alive ping interval, 30s + */ + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 60000); + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 3000); + channel_arguments_.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); + channel_arguments_.SetInt(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA, 0); + + /* + * If set to zero, disables retry behavior. Otherwise, transparent retries + * are enabled for all RPCs, and configurable retries are enabled when they + * are configured via the service config. For details, see: + * https://github.com/grpc/proposal/blob/master/A6-client-retries.md + */ + channel_arguments_.SetInt(GRPC_ARG_ENABLE_RETRIES, 0); + SPDLOG_INFO("ClientManager[ResourceNamespace={}] created", resource_namespace_); } diff --git a/src/main/cpp/remoting/BUILD.bazel b/src/main/cpp/remoting/BUILD.bazel index 59b8fe5d1..108f8ce3b 100644 --- a/src/main/cpp/remoting/BUILD.bazel +++ b/src/main/cpp/remoting/BUILD.bazel @@ -9,5 +9,7 @@ cc_library( deps = [ "//api:rocketmq_interface", "@asio//:asio", + "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/memory", ] ) \ No newline at end of file diff --git a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp new file mode 100644 index 000000000..3a6f03285 --- /dev/null +++ b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp @@ -0,0 +1,13 @@ +#include "QueryRouteRequestHeader.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void QueryRouteRequestHeader::encode(google::protobuf::Value& root) const { + auto fields = root.mutable_struct_value()->mutable_fields(); + google::protobuf::Value topic; + topic.set_string_value(topic_); + fields->insert({"topic", topic}); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/RemotingCommand.cpp b/src/main/cpp/remoting/RemotingCommand.cpp new file mode 100644 index 000000000..cbcdf67cf --- /dev/null +++ b/src/main/cpp/remoting/RemotingCommand.cpp @@ -0,0 +1,97 @@ +#include "RemotingCommand.h" + +#include +#include +#include + +#include "LanguageCode.h" +#include "absl/memory/memory.h" + +#include "QueryRouteRequestHeader.h" + +ROCKETMQ_NAMESPACE_BEGIN + +std::int32_t RemotingCommand::nextRequestId() { + static std::atomic_int32_t request_id{0}; + return request_id.fetch_add(1, std::memory_order_relaxed); +} + +RemotingCommand RemotingCommand::createRequest(RequestCode code, CommandCustomHeader* ext_fields) { + RemotingCommand command; + command.code_ = static_cast(code); + command.ext_fields_ = ext_fields; + return command; +} + +RemotingCommand RemotingCommand::createResponse(ResponseCode code, CommandCustomHeader* ext_fields) { + RemotingCommand response; + response.code_ = static_cast(code); + response.ext_fields_ = ext_fields; + response.flag_ |= (1 << RPC_TYPE_RESPONSE); + return response; +} + +void RemotingCommand::encodeHeader(google::protobuf::Value& root) { + auto fields = root.mutable_struct_value()->mutable_fields(); + + google::protobuf::Value code; + code.set_number_value(code_); + fields->insert({"code", code}); + + google::protobuf::Value language; + switch (language_) { + case LanguageCode::CPP: { + language.set_string_value("CPP"); + break; + } + case LanguageCode::JAVA: { + language.set_string_value("JAVA"); + break; + } + case LanguageCode::GO: { + language.set_string_value("GO"); + break; + } + case LanguageCode::DOTNET: { + language.set_string_value("DOTNET"); + break; + } + default: { + language.set_string_value("OTHER"); + break; + } + } + fields->insert({"language", language}); + + if (version_) { + google::protobuf::Value version; + version.set_number_value(version_); + fields->insert({"version", version}); + } + + google::protobuf::Value opaque; + opaque.set_number_value(opaque_); + fields->insert({"opaque", opaque}); + + google::protobuf::Value flag; + flag.set_number_value(flag_); + fields->insert({"flag", flag}); + + if (!remark_.empty()) { + google::protobuf::Value remark; + remark.set_string_value(remark_); + fields->insert({"remark", remark}); + } + + if (ext_fields_) { + google::protobuf::Value ext_fields; + ext_fields_->encode(ext_fields); + fields->insert({"extFields", ext_fields}); + } +} + +const std::uint8_t RemotingCommand::RPC_TYPE_RESPONSE = 0; + +const std::uint8_t RemotingCommand::RPC_TYPE_ONE_WAY = 1; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/CommandCustomHeader.h b/src/main/cpp/remoting/include/CommandCustomHeader.h index de1269cca..aeabf06f0 100644 --- a/src/main/cpp/remoting/include/CommandCustomHeader.h +++ b/src/main/cpp/remoting/include/CommandCustomHeader.h @@ -1,5 +1,8 @@ #pragma once +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -7,6 +10,8 @@ ROCKETMQ_NAMESPACE_BEGIN class CommandCustomHeader { public: virtual ~CommandCustomHeader() = default; + + virtual void encode(google::protobuf::Value& root) const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/LanguageCode.h b/src/main/cpp/remoting/include/LanguageCode.h index 7ff6d4729..cdf8efeac 100644 --- a/src/main/cpp/remoting/include/LanguageCode.h +++ b/src/main/cpp/remoting/include/LanguageCode.h @@ -17,7 +17,7 @@ enum class LanguageCode : std::uint8_t RUBY = 6, OTHER = 7, HTTP = 8, - Go = 9, + GO = 9, PHP = 10, OMS = 11, }; diff --git a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h index 7b9468dd5..86fc8917d 100644 --- a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h +++ b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h @@ -21,6 +21,8 @@ class QueryRouteRequestHeader : public CommandCustomHeader { return topic_; } + void encode(google::protobuf::Value& root) const override; + private: std::string topic_; }; diff --git a/src/main/cpp/remoting/include/RemotingCommand.h b/src/main/cpp/remoting/include/RemotingCommand.h index fbf361673..b7e4cec2b 100644 --- a/src/main/cpp/remoting/include/RemotingCommand.h +++ b/src/main/cpp/remoting/include/RemotingCommand.h @@ -1,8 +1,90 @@ #pragma once +#include +#include +#include +#include + +#include "CommandCustomHeader.h" #include "LanguageCode.h" -#include "rocketmq/RocketMQ.h" +#include "RequestCode.h" +#include "ResponseCode.h" +#include "Version.h" ROCKETMQ_NAMESPACE_BEGIN +/** + * RemotingCommand is non-copyable. It is movable. + */ +class RemotingCommand { +public: + RemotingCommand(const RemotingCommand&) = delete; + + RemotingCommand(RemotingCommand&& rhs) noexcept { + code_ = rhs.code_; + language_ = rhs.language_; + version_ = rhs.version_; + opaque_ = rhs.opaque_; + remark_ = std::move(rhs.remark_); + body_ = std::move(rhs.body_); + ext_fields_ = rhs.ext_fields_; + rhs.ext_fields_ = nullptr; + } + + RemotingCommand& operator=(const RemotingCommand&) = delete; + + RemotingCommand& operator=(RemotingCommand&& rhs) noexcept { + if (this == &rhs) { + return *this; + } + + code_ = rhs.code_; + language_ = rhs.language_; + version_ = rhs.version_; + opaque_ = rhs.opaque_; + remark_ = std::move(rhs.remark_); + body_ = std::move(rhs.body_); + ext_fields_ = rhs.ext_fields_; + rhs.ext_fields_ = nullptr; + return *this; + } + + virtual ~RemotingCommand() { + delete ext_fields_; + } + + static std::int32_t nextRequestId(); + + static RemotingCommand createRequest(RequestCode, CommandCustomHeader*); + + static RemotingCommand createResponse(ResponseCode, CommandCustomHeader*); + + virtual void encodeHeader(google::protobuf::Value& root); + +private: + RemotingCommand() = default; + + std::int32_t code_{static_cast(RequestCode::QueryRoute)}; + LanguageCode language_{LanguageCode::CPP}; + std::int32_t version_{static_cast(Version::V4_9_1)}; + std::int32_t opaque_{nextRequestId()}; + std::uint32_t flag_{0}; + std::string remark_; + + CommandCustomHeader* ext_fields_{nullptr}; + + std::vector body_; + + /** + * Bit-field shift amount for flag_ field, indicating the RPC is a response from broker or name-server. + */ + const static std::uint8_t RPC_TYPE_RESPONSE; + + /** + * Bit-field shift amount for flag_ field. Request marked one-way should NOT expect a respose from broker or + * name-server. + */ + const static std::uint8_t RPC_TYPE_ONE_WAY; +}; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/RemotingCommandType.h b/src/main/cpp/remoting/include/RemotingCommandType.h new file mode 100644 index 000000000..5bd341a90 --- /dev/null +++ b/src/main/cpp/remoting/include/RemotingCommandType.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class RemotingCommandType : std::uint8_t +{ + REQUEST = 0, + RESPONSE = 1, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/ResponseCode.h b/src/main/cpp/remoting/include/ResponseCode.h new file mode 100644 index 000000000..080f9b3fd --- /dev/null +++ b/src/main/cpp/remoting/include/ResponseCode.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class ResponseCode : std::uint16_t +{ + Success = 0, + InternalSystemError = 1, + TooManyRequests = 2, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/Version.h b/src/main/cpp/remoting/include/Version.h new file mode 100644 index 000000000..5a64a4573 --- /dev/null +++ b/src/main/cpp/remoting/include/Version.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class Version : std::int32_t +{ + V4_9_1 = 396, + V4_9_2 = 398, +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BUILD.bazel b/src/test/cpp/ut/remoting/BUILD.bazel new file mode 100644 index 000000000..394a8f844 --- /dev/null +++ b/src/test/cpp/ut/remoting/BUILD.bazel @@ -0,0 +1,23 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "query_route_request_header_test", + srcs = [ + "QueryRouteRequestHeaderTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "remoting_command_test", + srcs = [ + "RemotingCommandTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ], +) \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp new file mode 100644 index 000000000..b80a77aef --- /dev/null +++ b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp @@ -0,0 +1,20 @@ +#include "QueryRouteRequestHeader.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(QueryRouteRequestHeaderTest, testEncode) { + google::protobuf::Value root; + QueryRouteRequestHeader header; + header.topic("abc"); + header.encode(root); + + std::string json; + auto status = google::protobuf::util::MessageToJsonString(root, &json); + EXPECT_TRUE(status.ok()); + EXPECT_FALSE(json.empty()); + std::cout << json << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp new file mode 100644 index 000000000..088fa8774 --- /dev/null +++ b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp @@ -0,0 +1,46 @@ +#include "RemotingCommand.h" + +#include +#include +#include + +#include "QueryRouteRequestHeader.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class RemotingCommandTest : public testing::Test { +public: +}; + +TEST_F(RemotingCommandTest, testNextRequestId) { + + std::unordered_set request_ids; + + for (int i = 0; i < 50; i++) { + std::int32_t id = RemotingCommand::nextRequestId(); + EXPECT_TRUE(request_ids.find(id) == request_ids.end()); + request_ids.insert(id); + } +} + +TEST_F(RemotingCommandTest, testCreateRequest) { + auto header = new QueryRouteRequestHeader; + header->topic("abc"); + + auto command = RemotingCommand::createRequest(RequestCode::QueryRoute, header); + google::protobuf::Value root; + command.encodeHeader(root); + + const auto& fields = root.struct_value().fields(); + EXPECT_TRUE(fields.contains("extFields")); + EXPECT_TRUE(fields.contains("code")); + EXPECT_EQ(static_cast(RequestCode::QueryRoute), fields.at("code").number_value()); + + std::string json; + auto status = google::protobuf::util::MessageToJsonString(root, &json); + EXPECT_TRUE(status.ok()); + std::cout << json << std::endl; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 9bd65be7f2b39e34ff45b376abf71762e791fe66 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sat, 2 Oct 2021 09:41:09 +0000 Subject: [PATCH 089/133] BugFix: fix DEBUG enum item and macro collision --- src/main/cpp/tracing/exporters/OtlpExporter.cpp | 6 +++--- src/main/cpp/tracing/exporters/include/OtlpExporter.h | 8 ++++---- src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 89c66a212..40f93d20f 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -129,9 +129,9 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } switch (exp->traceMode()) { - case TraceMode::OFF: + case TraceMode::Off: return; - case TraceMode::DEBUG: { + case TraceMode::Develop: { { for (const auto& span : spans) { SPDLOG_INFO("{} --> {}: {}", absl::FormatTime(span.start_time()), absl::FormatTime(span.end_time()), @@ -158,7 +158,7 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } return; } - case TraceMode::GRPC: + case TraceMode::Grpc: break; } diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index dc80c00eb..6a39f96a3 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -26,9 +26,9 @@ namespace collector_trace = collector::trace::v1; enum class TraceMode : std::uint8_t { - OFF = 0, - DEBUG = 1, - GRPC = 2 + Off = 0, + Develop = 1, + Grpc = 2 }; class Samplers { @@ -79,7 +79,7 @@ class OtlpExporter : public std::enable_shared_from_this { std::vector hosts_; absl::Mutex hosts_mtx_; - TraceMode mode_{TraceMode::OFF}; + TraceMode mode_{TraceMode::Off}; }; class ExportClient { diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp index add9524fe..7cc649d00 100644 --- a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -27,7 +27,7 @@ class OtlpExporterTest : public testing::Test { TEST_F(OtlpExporterTest, testExport) { auto exporter = std::make_shared(client_manager_, &client_config_); - exporter->traceMode(TraceMode::DEBUG); + exporter->traceMode(TraceMode::Develop); exporter->start(); auto& sampler = Samplers::always(); From 51ff3c1143695bb7b25abc999c663193b36c91c3 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sat, 2 Oct 2021 12:08:50 +0000 Subject: [PATCH 090/133] Set default logger level --- src/main/cpp/client/LogInterceptor.cpp | 19 ++++++++++++++++++ src/main/cpp/log/LoggerImpl.cpp | 27 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 3f5834508..88d14176b 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -11,6 +11,25 @@ ROCKETMQ_NAMESPACE_BEGIN void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* methods) { InterceptorContinuation continuation(methods); + auto level = spdlog::default_logger()->level(); + switch (level) { + case spdlog::level::trace: + // fall-through on purpose. + case spdlog::level::debug: { + break; + } + case spdlog::level::info: + // fall-through on purpose. + case spdlog::level::warn: + // fall-through on purpose. + case spdlog::level::err: { + return; + } + default: { + return; + } + } + if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { std::multimap* metadata = methods->GetSendInitialMetadata(); if (metadata) { diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp index 20643f811..a8140f40a 100644 --- a/src/main/cpp/log/LoggerImpl.cpp +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -1,4 +1,6 @@ #include "LoggerImpl.h" +#include +#include #include #include @@ -77,7 +79,30 @@ void LoggerImpl::init0() { auto default_logger = std::make_shared(LOGGER_NAME, spdlog::sinks_init_list{console_sink_, file_sink_}); default_logger->flush_on(spdlog::level::warn); - default_logger->set_level(spdlog::level::trace); + Level logger_level = + static_cast(std::min(static_cast(level_), static_cast(console_level_))); + switch (logger_level) { + case Level::Trace: { + default_logger->set_level(spdlog::level::trace); + break; + } + case Level::Debug: { + default_logger->set_level(spdlog::level::debug); + break; + } + case Level::Info: { + default_logger->set_level(spdlog::level::info); + break; + } + case Level::Warn: { + default_logger->set_level(spdlog::level::warn); + break; + } + default: { + default_logger->set_level(spdlog::level::info); + break; + } + } spdlog::flush_every(std::chrono::seconds(1)); spdlog::set_default_logger(default_logger); } From 7354d7a4f5163075e72ba48f2e332ae187b8bdb5 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Sun, 3 Oct 2021 02:58:20 +0000 Subject: [PATCH 091/133] Add sha256 for asio and httplib --- bazel/rocketmq_deps.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index 1e2b907da..d593bee28 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -112,6 +112,7 @@ def rocketmq_deps(): maybe( http_archive, name = "asio", + sha256 = "c864363205f78768c795ba14a9989200075e732f877ddef01a19237c2eccf44b", build_file = "@org_apache_rocketmq//third_party:asio.BUILD", strip_prefix = "asio-1.18.2", urls = [ @@ -122,6 +123,7 @@ def rocketmq_deps(): maybe( http_archive, name = "com_github_yhirose_cpp_httplib", + sha256 = "0ff62e28eb0f6e563178d44b77c94dddb8702141d83dd34b83cb046399c2b1d5", build_file = "@org_apache_rocketmq//third_party:cpp_httplib.BUILD", strip_prefix = "cpp-httplib-0.9.4", urls = ["https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.9.4.tar.gz"], From 8ecf035ffd7b6f295af5541c58ef7180b256c653 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 8 Oct 2021 03:07:05 +0000 Subject: [PATCH 092/133] Use latest protocol specification --- example/rocketmq/ExampleProducer.cpp | 4 +- proto/apache/rocketmq/v1/admin.proto | 19 ++- proto/apache/rocketmq/v1/definition.proto | 23 ++- proto/apache/rocketmq/v1/service.proto | 147 +++++++++++++----- src/main/cpp/client/ClientManagerImpl.cpp | 9 +- src/main/cpp/client/include/Client.h | 16 +- .../cpp/client/mocks/include/ClientMock.h | 2 + src/main/cpp/rocketmq/ClientImpl.cpp | 11 +- src/main/cpp/rocketmq/ProducerImpl.cpp | 11 ++ src/main/cpp/rocketmq/PullConsumerImpl.cpp | 10 ++ src/main/cpp/rocketmq/PushConsumerImpl.cpp | 8 + src/main/cpp/rocketmq/include/ClientImpl.h | 6 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 2 + .../cpp/rocketmq/include/PullConsumerImpl.h | 2 + .../cpp/rocketmq/include/PushConsumerImpl.h | 2 + 15 files changed, 211 insertions(+), 61 deletions(-) diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 8dee43623..6452509ff 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -66,12 +66,12 @@ int main(int argc, char* argv[]) { std::thread stats_thread(stats_lambda); std::string body = randomString(1024 * 4); - std::cout << "Message body: " << body << std::endl; + std::cout << "Message body size: " << body.length() << std::endl; message.setBody(body); try { producer.start(); - for (int i = 0; i < 102400; ++i) { + for (int i = 0; i < 16; ++i) { SendResult sendResult = producer.send(message); std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; count++; diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v1/admin.proto index 8ed203762..554207b90 100644 --- a/proto/apache/rocketmq/v1/admin.proto +++ b/proto/apache/rocketmq/v1/admin.proto @@ -1,3 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; package apache.rocketmq.v1; @@ -7,9 +22,7 @@ option java_multiple_files = true; option java_package = "apache.rocketmq.v1"; option java_generate_equals_and_hash = true; option java_string_check_utf8 = true; - -// Ali Cloud Service -option java_outer_classname = "ACS"; +option java_outer_classname = "MQAdmin"; message ChangeLogLevelRequest { enum Level { diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index 9ecd46a11..c92aad3ad 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -1,3 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; import "google/protobuf/timestamp.proto"; @@ -9,8 +24,6 @@ option java_multiple_files = true; option java_package = "apache.rocketmq.v1"; option java_generate_equals_and_hash = true; option java_string_check_utf8 = true; - -// Ali Cloud Service option java_outer_classname = "MQDomain"; enum Permission { @@ -46,7 +59,7 @@ message DeadLetterPolicy { // // This field will be honored on a best effort basis. // - // If this parameter is 0, a default value of 5 is used. + // If this parameter is 0, a default value of 16 is used. int32 max_delivery_attempts = 1; reserved 2 to 64; @@ -216,6 +229,7 @@ message SystemAttribute { repeated string keys = 2; // Message identifier, client-side generated, remains unique. + // if message_id is empty, the send message request will be aborted with status `INVALID_ARGUMENT` string message_id = 3; // Message body digest @@ -284,6 +298,9 @@ message SystemAttribute { message Message { Resource topic = 1; // User defined key-value pairs. + // If user_attribute contains the reserved keys by RocketMQ, + // the send message request will be aborted with status `INVALID_ARGUMENT`. See below links for the reserved keys + // https://github.com/apache/rocketmq/blob/master/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java#L58 map user_attribute = 2; SystemAttribute system_attribute = 3; bytes body = 4; diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 5deb63cd5..6220f515e 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -1,3 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + syntax = "proto3"; import "google/protobuf/duration.proto"; @@ -13,8 +28,6 @@ option java_multiple_files = true; option java_package = "apache.rocketmq.v1"; option java_generate_equals_and_hash = true; option java_string_check_utf8 = true; - -// Ali Cloud Service option java_outer_classname = "MQService"; message ResponseCommon { @@ -28,10 +41,13 @@ message ResponseCommon { reserved 7 to 64; } +// A QueryRouteRequest requests a set of Partitions of the specific topic with +// necessary route infos. message QueryRouteRequest { Resource topic = 1; - // Service access point + // The service access points used to issue QueryRouteRequest + // The QueryRouteResponse will indicate the adress of subsequent RPCs. Endpoints endpoints = 2; reserved 3 to 64; @@ -248,20 +264,6 @@ message PullMessageResponse { reserved 6 to 64; } -message UpdateOffsetRequest { - Resource group = 1; - Partition partition = 2; - int64 partition_offset = 3; - - reserved 4 to 64; -} - -message UpdateOffsetResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - message GenericPollingRequest { string client_id = 1; repeated Resource topics = 2; @@ -336,10 +338,13 @@ message MultiplexingResponse { } message NotifyClientTerminationRequest { - Resource group = 1; - string client_id = 2; + oneof group { + Resource producer_group = 1; + Resource consumer_group = 2; + } + string client_id = 3; - reserved 3 to 64; + reserved 4 to 64; } message NotifyClientTerminationResponse { @@ -348,37 +353,103 @@ message NotifyClientTerminationResponse { reserved 2 to 64; } +// For all the rpcs in MessagingService may return below erros: +// +// If the request doesn't have a valid authentication credentials, returns `UNAUTHENTICATED`. +// If the caller doesn't permission to execute the specified operation, returns `PERMISSION_DENIED`. +// If the per-user rate quota has been exceeded, returns `RESOURCE_EXHAUSTED`. +// If any unexpected server-side exception occurs, returns `INTERNAL`. service MessagingService { - rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) {} - rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) {} + // Querys the route info of a topic from specific endpoints, the server returns a set of partition if success. + // + // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. + rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { + } - rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) {} + // Producer or consumer sends HeartbeatRequest to server in order to report necessary + // client-side information, like subscription data of consumer. Returns `OK` if success. + // + // If the client language info is invalid, returns `INVALID_ARGUMENT` + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { + } - rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) {} + // Checks the health status of message server, returns `OK` if no network issues. + // Clients could use this RPC to detect the availability of server, and adpot necessary isolation measures. + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { + } - rpc QueryAssignment(QueryAssignmentRequest) - returns (QueryAssignmentResponse) {} + // Sends one message to the specific partition of a topic, returns message id or transaction id with status `OK`. + // + // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { + } + + // Querys the assigned partition route info of a topic for current consumer, + // the returned assignment result is descided by server-side load balacner. + // + // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. + rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { + } - rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) {} + // Receives messages from the server in batch manner, returns a set of messages if success. + // The received messages should be acked or uacked after processed. + // + // If the pending concurrent receive requests exceed the quota of the given consumer group, returns `UNAVAILABLE`. + // If the upstream store server hangs, return `DEADLINE_EXCEEDED` in a timely manner. + // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. + // If there is no new message in the specific topic, returns `OK` with an empty message set. Please note that client + // may suffer from false empty responses. + rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { + } - rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) {} + // Acknowledges the message associated with the `receipt_handle` or `offset` in the + // `AckMessageRequest`, it means the message has been successfully processed. + // Returns `OK` if the message server remove the relevant message successfully. + // + // If the given receipt_handle is illegal or out of date, returns `INVALID_ARGUMENT`. + rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { + } - rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) {} + // Signals that the message has not been successfully processed. The message server should resend the message + // follow the retry policy defined at server-side. + // + // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. + rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { + } + // Forwards one message to dead letter queue if the DeadLetterPolicy is triggered by this message at client-side, + // return `OK` if success. rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) - returns (ForwardMessageToDeadLetterQueueResponse) {} - - rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) {} + returns (ForwardMessageToDeadLetterQueueResponse) { + } - rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) {} + // Commits or rollback one transactional message. + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { + } - rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) {} + // Querys the offset of the specific partition, returns the offset with `OK` if success. + // The message server should maintain a numerical offset for each message in a parition. + rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { + } - rpc UpdateOffset(UpdateOffsetRequest) returns (UpdateOffsetResponse) {} + // Pulls messages from the specific partition, returns a set of messages with next pull offset. + // The pulled messages can't be acked or nacked, while the client is responsible for manage offesets for consumer, + // typically update consume offset to local memory or a third-party storage service. + // + // If the pending concurrent receive requests exceed the quota of the given consumer group, returns `UNAVAILABLE`. + // If the upstream store server hangs, return `DEADLINE_EXCEEDED` in a timely manner. + // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. + // If there is no new message in the specific topic, returns `OK` with an empty message set. Please note that client + // may suffer from false empty responses. + rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { + } - rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) {} + rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) { + } - rpc NotifyClientTermination(NotifyClientTerminationRequest) - returns (NotifyClientTerminationResponse) {} + rpc NotifyClientTermination(NotifyClientTerminationRequest) returns (NotifyClientTerminationResponse) { + } } \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 0dfc00b32..24d56be9e 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -55,9 +55,12 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - int max_message_size = 1024 * 1024 * 16; - channel_arguments_.SetMaxReceiveMessageSize(max_message_size); - channel_arguments_.SetMaxSendMessageSize(max_message_size); + + // Use unlimited receive message size. + channel_arguments_.SetMaxReceiveMessageSize(-1); + + int max_send_message_size = 1024 * 1024 * 16; + channel_arguments_.SetMaxSendMessageSize(max_send_message_size); /* * Keep-alive settings: diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index 9a14e8cbc..917115d35 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -12,23 +12,27 @@ class Client : virtual public ClientConfig { public: ~Client() override = default; - virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; + virtual void endpointsInUse(absl::flat_hash_set &endpoints) = 0; virtual void heartbeat() = 0; virtual bool active() = 0; - virtual void onRemoteEndpointRemoval(const std::vector&) = 0; + virtual void onRemoteEndpointRemoval(const std::vector &) = 0; /** - * For endpoints that are marked as inactive due to one or multiple business operation failure, this function is to - * initiate health-check RPCs; Once the health-check passes, they are conceptually add back to serve further business - * workload. + * For endpoints that are marked as inactive due to one or multiple business + * operation failure, this function is to initiate health-check RPCs; Once the + * health-check passes, they are conceptually add back to serve further + * business workload. */ virtual void healthCheck() = 0; - virtual void schedule(const std::string& task_name, const std::function& task, + virtual void schedule(const std::string &task_name, + const std::function &task, std::chrono::milliseconds delay) = 0; + + virtual void notifyClientTermination() = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h index dfd7a6615..176599207 100644 --- a/src/main/cpp/client/mocks/include/ClientMock.h +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -19,6 +19,8 @@ class ClientMock : virtual public Client, virtual public ClientConfigMock { MOCK_METHOD(void, schedule, (const std::string&, const std::function&, std::chrono::milliseconds), (override)); + + MOCK_METHOD(void, notifyClientTermination, (), (override)); }; ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index e868c4c99..294de2729 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -10,6 +10,7 @@ #include #include +#include "RpcClient.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "apache/rocketmq/v1/definition.pb.h" @@ -69,7 +70,6 @@ void ClientImpl::shutdown() { if (route_update_handle_) { client_manager_->getScheduler().cancel(route_update_handle_); } - notifyClientTermination(); client_manager_.reset(); } else { SPDLOG_ERROR("Try to shutdown ClientImpl, but its state is not as expected. Expecting: {}, Actual: {}", @@ -476,15 +476,16 @@ void ClientImpl::fillGenericPollingRequest(MultiplexingRequest& request) { } void ClientImpl::notifyClientTermination() { + SPDLOG_WARN("Should NOT reach here. Subclass should have overridden this function."); + std::abort(); +} + +void ClientImpl::notifyClientTermination(const NotifyClientTerminationRequest& request) { absl::flat_hash_set endpoints; endpointsInUse(endpoints); Metadata metadata; Signature::sign(this, metadata); - NotifyClientTerminationRequest request; - request.mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_name(group_name_); - request.set_client_id(clientId()); for (const auto& endpoint : endpoints) { client_manager_->notifyClientTermination(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 99a8c232b..8a6ae27fd 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -5,6 +5,7 @@ #include #include +#include "Client.h" #include "absl/strings/str_join.h" #include "opencensus/trace/propagation/trace_context.h" #include "opencensus/trace/span.h" @@ -59,11 +60,21 @@ void ProducerImpl::shutdown() { return; } + notifyClientTermination(); + ClientImpl::shutdown(); assert(State::STOPPED == state_.load()); SPDLOG_INFO("Producer instance stopped"); } +void ProducerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_producer_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + bool ProducerImpl::isRunning() const { return State::STARTED == state_.load(std::memory_order_relaxed); } diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index 7bb747e0a..43f820374 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -24,6 +24,8 @@ void PullConsumerImpl::start() { void PullConsumerImpl::shutdown() { // Shutdown services started by current tier + notifyClientTermination(); + // Shutdown services that are started by the parent ClientImpl::shutdown(); State expected = State::STOPPING; @@ -160,4 +162,12 @@ void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { } } +void PullConsumerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 4386b84b6..a8cbc7bfa 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -630,4 +630,12 @@ ClientResourceBundle PushConsumerImpl::resourceBundle() { return std::move(resource_bundle); } +void PushConsumerImpl::notifyClientTermination() { + NotifyClientTerminationRequest request; + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + request.set_client_id(clientId()); + ClientImpl::notifyClientTermination(request); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 3de7452c5..998d357d4 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -3,8 +3,10 @@ #include #include #include +#include #include +#include "RpcClient.h" #include "absl/strings/string_view.h" #include "apache/rocketmq/v1/definition.pb.h" @@ -118,7 +120,9 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void setAccessPoint(rmq::Endpoints* endpoints); - virtual void notifyClientTermination(); + void notifyClientTermination() override; + + void notifyClientTermination(const NotifyClientTerminationRequest& request); private: /** diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index 8d03e3591..dea27a288 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -108,6 +108,8 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; + void notifyClientTermination() override; + private: absl::flat_hash_map topic_publish_info_table_ GUARDED_BY(topic_publish_info_mtx_); absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index 058743b69..c0091137d 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -36,6 +36,8 @@ class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this< return shared_from_this(); } + void notifyClientTermination() override; + MessageModel message_model_{MessageModel::CLUSTERING}; }; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 44e1b5499..e6958106a 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -183,6 +183,8 @@ class PushConsumerImpl : virtual public ClientImpl, ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; + void notifyClientTermination() override; + private: absl::flat_hash_map topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); From fdac606d5881f0f278f69c908473cffa9d549a4c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 8 Oct 2021 03:44:19 +0000 Subject: [PATCH 093/133] Fix unit tests --- src/test/cpp/ut/client/ClientManagerFactoryTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index eb1c283f0..5e936360f 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -23,6 +23,7 @@ TEST_F(ClientManagerFactoryTest, testGetClientManager) { .WillRepeatedly(testing::ReturnRef(resource_namespace_)); ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); EXPECT_TRUE(client_manager); + client_manager->start(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file From a03788ceca0ec96d1a86adb064a2e7fffa8c209d Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Fri, 8 Oct 2021 13:37:10 +0000 Subject: [PATCH 094/133] Add unit test for remoting protocol --- src/main/cpp/remoting/BUILD.bazel | 1 + src/main/cpp/remoting/BrokerData.cpp | 27 +++++++++++++++ src/main/cpp/remoting/QueueData.cpp | 33 ++++++++++++++++++ src/main/cpp/remoting/TopicRouteData.cpp | 30 ++++++++++++++++ src/main/cpp/remoting/include/BrokerData.h | 22 ++++++++++++ src/main/cpp/remoting/include/QueueData.h | 28 +++++++++++++++ .../cpp/remoting/include/TopicRouteData.h | 29 ++++++++++++++++ src/test/cpp/ut/remoting/BUILD.bazel | 34 +++++++++++++++++++ src/test/cpp/ut/remoting/BrokerDataTest.cpp | 28 +++++++++++++++ src/test/cpp/ut/remoting/QueueDataTest.cpp | 22 ++++++++++++ .../cpp/ut/remoting/TopicRouteDataTest.cpp | 20 +++++++++++ 11 files changed, 274 insertions(+) create mode 100644 src/main/cpp/remoting/BrokerData.cpp create mode 100644 src/main/cpp/remoting/QueueData.cpp create mode 100644 src/main/cpp/remoting/TopicRouteData.cpp create mode 100644 src/main/cpp/remoting/include/BrokerData.h create mode 100644 src/main/cpp/remoting/include/QueueData.h create mode 100644 src/main/cpp/remoting/include/TopicRouteData.h create mode 100644 src/test/cpp/ut/remoting/BrokerDataTest.cpp create mode 100644 src/test/cpp/ut/remoting/QueueDataTest.cpp create mode 100644 src/test/cpp/ut/remoting/TopicRouteDataTest.cpp diff --git a/src/main/cpp/remoting/BUILD.bazel b/src/main/cpp/remoting/BUILD.bazel index 108f8ce3b..ae84d43b4 100644 --- a/src/main/cpp/remoting/BUILD.bazel +++ b/src/main/cpp/remoting/BUILD.bazel @@ -11,5 +11,6 @@ cc_library( "@asio//:asio", "@com_google_protobuf//:protobuf", "@com_google_absl//absl/memory", + "@com_google_absl//absl/container:flat_hash_map", ] ) \ No newline at end of file diff --git a/src/main/cpp/remoting/BrokerData.cpp b/src/main/cpp/remoting/BrokerData.cpp new file mode 100644 index 000000000..cadba72b7 --- /dev/null +++ b/src/main/cpp/remoting/BrokerData.cpp @@ -0,0 +1,27 @@ +#include "BrokerData.h" +#include + +ROCKETMQ_NAMESPACE_BEGIN + +BrokerData BrokerData::decode(const google::protobuf::Struct& root) { + BrokerData broker_data; + auto fields = root.fields(); + if (fields.contains("cluster")) { + broker_data.cluster_ = fields["cluster"].string_value(); + } + + if (fields.contains("brokerName")) { + broker_data.broker_name_ = fields["brokerName"].string_value(); + } + + if (fields.contains("brokerAddrs")) { + auto items = fields["brokerAddrs"].struct_value().fields(); + for (const auto& item : items) { + auto k = std::stoll(item.first); + broker_data.broker_addresses_.insert({k, item.second.string_value()}); + } + } + return broker_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/QueueData.cpp b/src/main/cpp/remoting/QueueData.cpp new file mode 100644 index 000000000..31a570269 --- /dev/null +++ b/src/main/cpp/remoting/QueueData.cpp @@ -0,0 +1,33 @@ +#include "QueueData.h" + +ROCKETMQ_NAMESPACE_BEGIN + +QueueData QueueData::decode(const google::protobuf::Struct& root) { + auto fields = root.fields(); + + QueueData queue_data; + + if (fields.contains("brokerName")) { + queue_data.broker_name_ = fields["brokerName"].string_value(); + } + + if (fields.contains("readQueueNums")) { + queue_data.read_queue_number_ = fields["readQueueNums"].number_value(); + } + + if (fields.contains("writeQueueNums")) { + queue_data.write_queue_number_ = fields["writeQueueNums"].number_value(); + } + + if (fields.contains("perm")) { + queue_data.perm_ = fields["perm"].number_value(); + } + + if (fields.contains("topicSynFlag")) { + queue_data.topic_system_flag_ = fields["topicSynFlag"].number_value(); + } + + return queue_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/TopicRouteData.cpp b/src/main/cpp/remoting/TopicRouteData.cpp new file mode 100644 index 000000000..44141356d --- /dev/null +++ b/src/main/cpp/remoting/TopicRouteData.cpp @@ -0,0 +1,30 @@ +#include "TopicRouteData.h" +#include "BrokerData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TopicRouteData TopicRouteData::decode(const google::protobuf::Struct& root) { + auto fields = root.fields(); + + TopicRouteData topic_route_data; + + if (fields.contains("queueDatas")) { + auto queue_data_list = fields.at("queueDatas"); + + for (auto& item : queue_data_list.list_value().values()) { + topic_route_data.queue_data_.push_back(QueueData::decode(item.struct_value())); + } + } + + if (fields.contains("brokerDatas")) { + auto broker_data_list = fields.at("brokerDatas"); + for (auto& item : broker_data_list.list_value().values()) { + topic_route_data.broker_data_.push_back(BrokerData::decode(item.struct_value())); + } + } + + return topic_route_data; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/BrokerData.h b/src/main/cpp/remoting/include/BrokerData.h new file mode 100644 index 000000000..d8d43d078 --- /dev/null +++ b/src/main/cpp/remoting/include/BrokerData.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct BrokerData { + std::string cluster_; + std::string broker_name_; + absl::flat_hash_map broker_addresses_; + + static BrokerData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/QueueData.h b/src/main/cpp/remoting/include/QueueData.h new file mode 100644 index 000000000..6fb5c7f72 --- /dev/null +++ b/src/main/cpp/remoting/include/QueueData.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "google/protobuf/struct.pb.h" +#include "google/protobuf/util/json_util.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct QueueData { + std::string broker_name_; + std::int32_t read_queue_number_{0}; + std::int32_t write_queue_number_{0}; + std::uint32_t perm_{0}; + + /** + * @brief in Java, it's named "topicSynFlag" + * + */ + std::uint32_t topic_system_flag_{0}; + + static QueueData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/TopicRouteData.h b/src/main/cpp/remoting/include/TopicRouteData.h new file mode 100644 index 000000000..9e45e3a4e --- /dev/null +++ b/src/main/cpp/remoting/include/TopicRouteData.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "BrokerData.h" +#include "QueueData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct TopicRouteData { + + /** + * @brief In Java, it's named "queueDatas" + * + */ + std::vector queue_data_; + + /** + * @brief In Java, it's named "brokerDatas" + * + */ + std::vector broker_data_; + + + static TopicRouteData decode(const google::protobuf::Struct& root); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BUILD.bazel b/src/test/cpp/ut/remoting/BUILD.bazel index 394a8f844..c0dacd1af 100644 --- a/src/test/cpp/ut/remoting/BUILD.bazel +++ b/src/test/cpp/ut/remoting/BUILD.bazel @@ -20,4 +20,38 @@ cc_test( "//src/main/cpp/remoting", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "broker_data_test", + srcs = [ + "BrokerDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] +) + + +cc_test( + name = "queue_data_test", + srcs = [ + "QueueDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] +) + +cc_test( + name = "topic_route_data_test", + srcs = [ + "TopicRouteDataTest.cpp", + ], + deps = [ + "//src/main/cpp/remoting", + "@com_google_googletest//:gtest_main", + ] ) \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BrokerDataTest.cpp b/src/test/cpp/ut/remoting/BrokerDataTest.cpp new file mode 100644 index 000000000..168203c3e --- /dev/null +++ b/src/test/cpp/ut/remoting/BrokerDataTest.cpp @@ -0,0 +1,28 @@ +#include "BrokerData.h" + +#include + +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +#include "google/protobuf/struct.pb.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(BrokerDataTest, testDecode) { + std::string json = + R"({"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false})"; + google::protobuf::Struct root; + + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + ASSERT_TRUE(status.ok()); + + BrokerData&& broker_data = BrokerData::decode(root); + EXPECT_EQ("b1", broker_data.broker_name_); + EXPECT_EQ("cluster", broker_data.cluster_); + EXPECT_EQ(2, broker_data.broker_addresses_.size()); + EXPECT_EQ("abc", broker_data.broker_addresses_.at(1)); + EXPECT_EQ("def", broker_data.broker_addresses_.at(2)); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueueDataTest.cpp b/src/test/cpp/ut/remoting/QueueDataTest.cpp new file mode 100644 index 000000000..936a68896 --- /dev/null +++ b/src/test/cpp/ut/remoting/QueueDataTest.cpp @@ -0,0 +1,22 @@ +#include "gtest/gtest.h" + +#include "QueueData.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(QueueDataTest, testDecode) { + std::string json = R"({"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8})"; + google::protobuf::Struct root; + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + EXPECT_TRUE(status.ok()); + + QueueData queue_data = QueueData::decode(root); + EXPECT_EQ("broker1", queue_data.broker_name_); + EXPECT_EQ(6, queue_data.perm_); + EXPECT_EQ(4, queue_data.read_queue_number_); + EXPECT_EQ(8, queue_data.write_queue_number_); + EXPECT_EQ(3, queue_data.topic_system_flag_); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp new file mode 100644 index 000000000..3a11d87c2 --- /dev/null +++ b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp @@ -0,0 +1,20 @@ +#include "TopicRouteData.h" +#include "rocketmq/RocketMQ.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(TopicRouteDataTest, testDecode) { + std::string json = + R"({"brokerDatas":[{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false}],"filterServerTable":{},"queueDatas":[{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8}]})"; + google::protobuf::Struct root; + auto status = google::protobuf::util::JsonStringToMessage(json, &root); + EXPECT_TRUE(status.ok()); + + auto topic_route_data = TopicRouteData::decode(root); + + EXPECT_FALSE(topic_route_data.broker_data_.empty()); + EXPECT_FALSE(topic_route_data.queue_data_.empty()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file From 42762d05d6359568f5f817c5d05ca53ba9637239 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 12 Oct 2021 09:34:58 +0000 Subject: [PATCH 095/133] Fix compile warning --- .bazelrc | 6 ++++-- example/rocketmq/ExamplePullConsumer.cpp | 5 ++++- src/main/cpp/client/LogInterceptor.cpp | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.bazelrc b/.bazelrc index e92ef362c..0f8f862f0 100644 --- a/.bazelrc +++ b/.bazelrc @@ -115,8 +115,10 @@ build:ubsan --linkopt -fsanitize=undefined build:ubsan --linkopt -lubsan # Use remote cache, --config=remote_cache See https://docs.bazel.build/versions/main/guide.html#--config -build:remote_cache --remote_cache=http://11.122.50.85:9090 -test:remote_cache --remote_cache=http://11.122.50.85:9090 +build:remote_cache --remote_cache=grpc://11.122.50.85:9092 +build:remote_cache --experimental_remote_downloader=grpc://11.122.50.85:9092 +test:remote_cache --remote_cache=http://11.122.50.85:9090 +test:remote_cache --remote_upload_local_results=true # Coverage options coverage --config=coverage diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp index b758e5454..cd745ffdd 100644 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -1,7 +1,9 @@ +#include +#include + #include "rocketmq/CredentialsProvider.h" #include "rocketmq/DefaultMQPullConsumer.h" #include "rocketmq/Logger.h" -#include int main(int argc, char* argv[]) { const char* group = "GID_group003"; @@ -28,6 +30,7 @@ int main(int argc, char* argv[]) { offset_query.policy = rocketmq::QueryOffsetPolicy::BEGINNING; auto offset_future = pull_consumer.queryOffset(offset_query); int64_t offset = offset_future.get(); + std::cout << "offset: " << offset << std::endl; } pull_consumer.shutdown(); diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 88d14176b..306a8df41 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -5,6 +5,7 @@ #include "google/protobuf/message.h" #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" +#include ROCKETMQ_NAMESPACE_BEGIN @@ -67,7 +68,7 @@ void LogInterceptor::Intercept(grpc::experimental::InterceptorBatchMethods* meth if (message) { auto* response = reinterpret_cast(message); std::string&& response_text = response->DebugString(); - int limit = 1024; + std::size_t limit = 1024; if (response_text.size() <= limit) { SPDLOG_DEBUG("[Inbound] {}\n{}", client_rpc_info_->method(), response_text); } else { From 332f2e83b179316f4c2eaeaff621bf12c1c01552 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Wed, 13 Oct 2021 14:42:56 +0800 Subject: [PATCH 096/133] Adapt to protocol v1.0-alpha1 (#29) Adapt to protocol v1.0-alpha1, including the following changes * Import latest protocol files * Replace multiplex RPC with 3 separate RPCs, PollCommand, ReportThreadStackTrace, ReportMessageConsumptionVerificationResult --- .bazelrc | 2 +- proto/apache/rocketmq/v1/definition.proto | 25 +- proto/apache/rocketmq/v1/service.proto | 236 ++++++++++++------ src/main/cpp/client/ClientManagerImpl.cpp | 127 +++++++--- src/main/cpp/client/RpcClientImpl.cpp | 19 +- src/main/cpp/client/include/ClientManager.h | 21 +- .../cpp/client/include/ClientManagerImpl.h | 16 +- src/main/cpp/client/include/RpcClient.h | 31 ++- src/main/cpp/client/include/RpcClientImpl.h | 11 +- .../client/mocks/include/ClientManagerMock.h | 17 +- .../cpp/client/mocks/include/RpcClientMock.h | 14 +- src/main/cpp/rocketmq/ClientImpl.cpp | 196 +++++++++------ src/main/cpp/rocketmq/include/ClientImpl.h | 30 ++- 13 files changed, 525 insertions(+), 220 deletions(-) diff --git a/.bazelrc b/.bazelrc index 0f8f862f0..1c53b01e2 100644 --- a/.bazelrc +++ b/.bazelrc @@ -117,7 +117,7 @@ build:ubsan --linkopt -lubsan # Use remote cache, --config=remote_cache See https://docs.bazel.build/versions/main/guide.html#--config build:remote_cache --remote_cache=grpc://11.122.50.85:9092 build:remote_cache --experimental_remote_downloader=grpc://11.122.50.85:9092 -test:remote_cache --remote_cache=http://11.122.50.85:9090 +test:remote_cache --remote_cache=grpc://11.122.50.85:9090 test:remote_cache --remote_upload_local_results=true # Coverage options diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto index c92aad3ad..b6cb24beb 100644 --- a/proto/apache/rocketmq/v1/definition.proto +++ b/proto/apache/rocketmq/v1/definition.proto @@ -207,6 +207,21 @@ enum DigestType { reserved 3 to 64; } +// When publishing messages to or subscribing messages from brokers, clients +// shall include or validate digests of message body to ensure data integrity. +// +// For message publishment, when an invalid digest were detected, brokers need +// respond client with BAD_REQUEST. +// +// For messags subscription, when an invalid digest were detected, consumers +// need to handle this case according to message type: +// 1) Standard messages should be negatively acknowledged instantly, causing +// immediate re-delivery; 2) FIFO messages require special RPC, to re-fetch +// previously acquired messages batch; +// +// Message consumption model also affects how invalid digest are handled. When +// messages are consumed in broadcasting way, +// TODO: define semantics of invalid-digest-when-broadcasting. message Digest { DigestType type = 1; string checksum = 2; @@ -229,7 +244,8 @@ message SystemAttribute { repeated string keys = 2; // Message identifier, client-side generated, remains unique. - // if message_id is empty, the send message request will be aborted with status `INVALID_ARGUMENT` + // if message_id is empty, the send message request will be aborted with + // status `INVALID_ARGUMENT` string message_id = 3; // Message body digest @@ -296,13 +312,18 @@ message SystemAttribute { } message Message { + Resource topic = 1; + // User defined key-value pairs. // If user_attribute contains the reserved keys by RocketMQ, - // the send message request will be aborted with status `INVALID_ARGUMENT`. See below links for the reserved keys + // the send message request will be aborted with status `INVALID_ARGUMENT`. + // See below links for the reserved keys // https://github.com/apache/rocketmq/blob/master/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java#L58 map user_attribute = 2; + SystemAttribute system_attribute = 3; + bytes body = 4; reserved 5 to 64; diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto index 6220f515e..9d8e3b090 100644 --- a/proto/apache/rocketmq/v1/service.proto +++ b/proto/apache/rocketmq/v1/service.proto @@ -41,13 +41,26 @@ message ResponseCommon { reserved 7 to 64; } -// A QueryRouteRequest requests a set of Partitions of the specific topic with -// necessary route infos. +// Topics are destination of messages to publish to or subscribe from. Similar +// to domain names, they will be addressable after resolution through the +// provided access point. +// +// Access points are usually the addresses of name servers, which fulfill +// service discovery, load-balancing and other auxillary services. Name servers +// receive periodic heartbeats from affiliate brokers and erase those which +// failed to maintain alive status. +// +// Name servers answer queries of QueryRouteRequest, responding clients with +// addressable partitions, which they may directly publish messages to or +// subscribe messages from. +// +// QueryRouteRequest shall include source endpoints, aka, configured +// access-point, which annotates tenant-id, instance-id or other +// vendor-specific settings. Purpose-built name servers may respond customized +// results based on these particular requirements. message QueryRouteRequest { Resource topic = 1; - // The service access points used to issue QueryRouteRequest - // The QueryRouteResponse will indicate the adress of subsequent RPCs. Endpoints endpoints = 2; reserved 3 to 64; @@ -55,6 +68,7 @@ message QueryRouteRequest { message QueryRouteResponse { ResponseCommon common = 1; + repeated Partition partitions = 2; reserved 3 to 64; @@ -264,74 +278,77 @@ message PullMessageResponse { reserved 6 to 64; } -message GenericPollingRequest { - string client_id = 1; - repeated Resource topics = 2; - oneof group { - Resource producer_group = 3; - Resource consumer_group = 4; - } - - reserved 5 to 64; +message NoopCommand { + reserved 1 to 64; } -message GenericPollingResponse { - ResponseCommon common = 1; +message PrintThreadStackTraceCommand { + string command_id = 1; reserved 2 to 64; } -message PrintThreadStackRequest { - string mid = 1; +message ReportThreadStackTraceRequest { + string command_id = 1; + string thread_stack_trace = 2; - reserved 2 to 64; + reserved 3 to 64; } -message PrintThreadStackResponse { +message ReportThreadStackTraceResponse { ResponseCommon common = 1; - string mid = 2; - string stack_trace = 3; - reserved 4 to 64; + reserved 2 to 64; } -message VerifyMessageConsumptionRequest { - string mid = 1; +message VerifyMessageConsumptionCommand { + string command_id = 1; Message message = 2; reserved 3 to 64; } -message VerifyMessageConsumptionResponse { - string mid = 1; - ResponseCommon common = 2; +message ReportMessageConsumptionResultRequest { + string command_id = 1; + google.rpc.Status status = 2; reserved 3 to 64; } -message RecoverOrphanedTransactionRequest { +message ReportMessageConsumptionResultResponse { + ResponseCommon common = 1; + + reserved 2 to 64; +} + +message RecoverOrphanedTransactionCommand { Message orphaned_transactional_message = 1; string transaction_id = 2; reserved 3 to 64; } -message MultiplexingRequest { - oneof type { - GenericPollingRequest polling_request = 1; - PrintThreadStackResponse print_thread_stack_response = 2; - VerifyMessageConsumptionResponse verify_message_consumption_response = 3; +message PollCommandRequest { + string client_id = 1; + repeated Resource topics = 2; + oneof group { + Resource producer_group = 3; + Resource consumer_group = 4; } - reserved 4 to 64; + reserved 5 to 64; } -message MultiplexingResponse { +message PollCommandResponse { oneof type { - GenericPollingResponse polling_response = 1; - PrintThreadStackRequest print_thread_stack_request = 2; - VerifyMessageConsumptionRequest verify_message_consumption_request = 3; - RecoverOrphanedTransactionRequest recover_orphaned_transaction_request = 4; + // Default command when no new command need to be delivered. + NoopCommand noop_command = 1; + // Request client to print thread stack trace. + PrintThreadStackTraceCommand print_thread_stack_trace_command = 2; + // Request client to verify the consumption of the appointed message. + VerifyMessageConsumptionCommand verify_message_consumption_command = 3; + // Request client to recover the orphaned transaction message. + RecoverOrphanedTransactionCommand recover_orphaned_transaction_command = 4; } reserved 5 to 64; @@ -353,36 +370,58 @@ message NotifyClientTerminationResponse { reserved 2 to 64; } -// For all the rpcs in MessagingService may return below erros: +// For all the RPCs in MessagingService, the following error handling policies +// apply: // -// If the request doesn't have a valid authentication credentials, returns `UNAUTHENTICATED`. -// If the caller doesn't permission to execute the specified operation, returns `PERMISSION_DENIED`. -// If the per-user rate quota has been exceeded, returns `RESOURCE_EXHAUSTED`. -// If any unexpected server-side exception occurs, returns `INTERNAL`. +// If the request doesn't bear a valid authentication credential, return a +// response with common.status.code == `UNAUTHENTICATED`. If the authenticated +// user is not granted with sufficient permission to execute the requested +// operation, return a response with common.status.code == `PERMISSION_DENIED`. +// If the per-user-resource-based quota is exhausted, return a response with +// common.status.code == `RESOURCE_EXHAUSTED`. If any unexpected server-side +// errors raise, return a response with common.status.code == `INTERNAL`. service MessagingService { - // Querys the route info of a topic from specific endpoints, the server returns a set of partition if success. + // Querys the route entries of the requested topic in the perspective of the + // given endpoints. On success, servers should return a collection of + // addressable partitions. Note servers may return customized route entries + // based on endpoints provided. // - // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // If the requested topic doesn't exist, returns `NOT_FOUND`. // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { } - // Producer or consumer sends HeartbeatRequest to server in order to report necessary - // client-side information, like subscription data of consumer. Returns `OK` if success. + // Producer or consumer sends HeartbeatRequest to servers periodically to + // keep-alive. Additionally, it also reports client-side configuration, + // including topic subscription, load-balancing group name, etc. + // + // Returns `OK` if success. // - // If the client language info is invalid, returns `INVALID_ARGUMENT` + // If a client specifies a language that is not yet supported by servers, + // returns `INVALID_ARGUMENT` rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { } - // Checks the health status of message server, returns `OK` if no network issues. - // Clients could use this RPC to detect the availability of server, and adpot necessary isolation measures. + // Checks the health status of message server, returns `OK` if services are + // online and serving. Clients may use this RPC to detect availability of + // messaging service, and take isolation actions when necessary. rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { } - // Sends one message to the specific partition of a topic, returns message id or transaction id with status `OK`. + // Delivers messages to brokers. + // Clients may further: + // 1. Refine a message destination to topic partition which fulfills parts of + // FIFO semantic; + // 2. Flag a message as transactional, which keeps it invisible to consumers + // until it commits; + // 3. Time a message, making it invisible to consumers till specified + // time-point; + // 4. And more... // - // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // Returns message-id or transaction-id with status `OK` on success. + // + // If the destination topic doesn't exist, returns `NOT_FOUND`. rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { } @@ -394,34 +433,40 @@ service MessagingService { rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { } - // Receives messages from the server in batch manner, returns a set of messages if success. - // The received messages should be acked or uacked after processed. + // Receives messages from the server in batch manner, returns a set of + // messages if success. The received messages should be acked or uacked after + // processed. // - // If the pending concurrent receive requests exceed the quota of the given consumer group, returns `UNAVAILABLE`. - // If the upstream store server hangs, return `DEADLINE_EXCEEDED` in a timely manner. - // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. - // If there is no new message in the specific topic, returns `OK` with an empty message set. Please note that client - // may suffer from false empty responses. + // If the pending concurrent receive requests exceed the quota of the given + // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, + // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic + // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new + // message in the specific topic, returns `OK` with an empty message set. + // Please note that client may suffer from false empty responses. rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { } - // Acknowledges the message associated with the `receipt_handle` or `offset` in the - // `AckMessageRequest`, it means the message has been successfully processed. - // Returns `OK` if the message server remove the relevant message successfully. + // Acknowledges the message associated with the `receipt_handle` or `offset` + // in the `AckMessageRequest`, it means the message has been successfully + // processed. Returns `OK` if the message server remove the relevant message + // successfully. // - // If the given receipt_handle is illegal or out of date, returns `INVALID_ARGUMENT`. + // If the given receipt_handle is illegal or out of date, returns + // `INVALID_ARGUMENT`. rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { } - // Signals that the message has not been successfully processed. The message server should resend the message - // follow the retry policy defined at server-side. + // Signals that the message has not been successfully processed. The message + // server should resend the message follow the retry policy defined at + // server-side. // - // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. + // If the corresponding topic or consumer group doesn't exist, returns + // `NOT_FOUND`. rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { } - // Forwards one message to dead letter queue if the DeadLetterPolicy is triggered by this message at client-side, - // return `OK` if success. + // Forwards one message to dead letter queue if the DeadLetterPolicy is + // triggered by this message at client-side, return `OK` if success. rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) returns (ForwardMessageToDeadLetterQueueResponse) { } @@ -430,26 +475,57 @@ service MessagingService { rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { } - // Querys the offset of the specific partition, returns the offset with `OK` if success. - // The message server should maintain a numerical offset for each message in a parition. + // Querys the offset of the specific partition, returns the offset with `OK` + // if success. The message server should maintain a numerical offset for each + // message in a parition. rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { } - // Pulls messages from the specific partition, returns a set of messages with next pull offset. - // The pulled messages can't be acked or nacked, while the client is responsible for manage offesets for consumer, - // typically update consume offset to local memory or a third-party storage service. + // Pulls messages from the specific partition, returns a set of messages with + // next pull offset. The pulled messages can't be acked or nacked, while the + // client is responsible for manage offesets for consumer, typically update + // consume offset to local memory or a third-party storage service. // - // If the pending concurrent receive requests exceed the quota of the given consumer group, returns `UNAVAILABLE`. - // If the upstream store server hangs, return `DEADLINE_EXCEEDED` in a timely manner. - // If the corresponding topic or consumer group doesn't exist, returns `NOT_FOUND`. - // If there is no new message in the specific topic, returns `OK` with an empty message set. Please note that client - // may suffer from false empty responses. + // If the pending concurrent receive requests exceed the quota of the given + // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, + // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic + // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new + // message in the specific topic, returns `OK` with an empty message set. + // Please note that client may suffer from false empty responses. rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { } - rpc MultiplexingCall(MultiplexingRequest) returns (MultiplexingResponse) { + // Multiplexing RPC(s) for various polling requests, which issue different + // commands to client. + // + // Sometimes client may need to receive and process the command from server. + // To prevent the complexity of streaming RPC(s), a unary RPC using + // long-polling is another solution. + // + // To mark the request-response of corresponding command, `command_id` in + // message is recorded in the subsequent RPC(s). For example, after receiving + // command of printing thread stack trace, client would send + // `ReportMessageConsumptionResultRequest` to server, which contain both of + // the stack trace and `command_id`. + // + // At same time, `NoopCommand` is delivered from server when no new command is + // needed, it is essential for client to maintain the ping-pong. + // + rpc PollCommand(PollCommandRequest) returns (PollCommandResponse) { + } + + // After receiving the corresponding polling command, the thread stack trace + // is reported to the server. + rpc ReportThreadStackTrace(ReportThreadStackTraceRequest) returns (ReportThreadStackTraceResponse) { + } + + // After receiving the corresponding polling command, the consumption result + // of appointed message is reported to the server. + rpc ReportMessageConsumptionResult(ReportMessageConsumptionResultRequest) + returns (ReportMessageConsumptionResultResponse) { } + // Notify the server that the client is terminated. rpc NotifyClientTermination(NotifyClientTerminationRequest) returns (NotifyClientTerminationResponse) { } } \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 24d56be9e..d0143e586 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -55,7 +55,6 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) tls_channel_credential_options_.watch_identity_key_cert_pairs(); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - // Use unlimited receive message size. channel_arguments_.SetMaxReceiveMessageSize(-1); @@ -1237,42 +1236,23 @@ void ClientManagerImpl::endTransaction( client->asyncEndTransaction(request, invocation_context); } -void ClientManagerImpl::multiplexingCall( - const std::string& target_host, const Metadata& metadata, const MultiplexingRequest& request, - std::chrono::milliseconds timeout, const std::function*)>& cb) { - RpcClientSharedPtr client = getRpcClient(target_host); - if (!client) { - SPDLOG_WARN("No RPC client for {}", target_host); - cb(nullptr); - return; - } +void ClientManagerImpl::pollCommand(const std::string& target, const Metadata& metadata, + const PollCommandRequest& request, std::chrono::milliseconds timeout, + const std::function*)>& cb) { + auto client = getRpcClient(target); - SPDLOG_DEBUG("Prepare to endTransaction. TargetHost={}, Request: {}", target_host.data(), request.DebugString()); - - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = target_host; + auto invocation_context = new InvocationContext(); + invocation_context->remote_address = target; for (const auto& item : metadata) { invocation_context->context.AddMetadata(item.first, item.second); } - - // Set RPC deadline. auto deadline = std::chrono::system_clock::now() + timeout; invocation_context->context.set_deadline(deadline); - auto callback = [cb](const InvocationContext* invocation_context) { - if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to apply multiplexing-call. TargetHost={}, gRPC statusCode={}, errorMessage={}", - invocation_context->remote_address, invocation_context->status.error_message(), - invocation_context->status.error_message()); - cb(invocation_context); - return; - } + auto callback = [cb](const InvocationContext* invocation_context) { cb(invocation_context); }; - SPDLOG_DEBUG("endTransaction completed OK. Response: {}", invocation_context->response.DebugString()); - cb(invocation_context); - }; invocation_context->callback = callback; - client->asyncMultiplexingCall(request, invocation_context); + client->asyncPollCommand(request, invocation_context); } void ClientManagerImpl::queryOffset(const std::string& target_host, const Metadata& metadata, @@ -1458,6 +1438,93 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); } +std::error_code ClientManagerImpl::reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; + auto client = getRpcClient(target_host); + grpc::ClientContext context; + auto deadline = std::chrono::system_clock::now() + timeout; + context.set_deadline(deadline); + + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + ReportThreadStackTraceResponse response; + auto status = client->reportThreadStackTrace(&context, request, &response); + if (!status.ok()) { + ec = ErrorCode::RequestTimeout; + SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); + return ec; + } + + switch (response.common().status().code()) { + case google::rpc::Code::OK: { + return ec; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Forbidden; + break; + } + default: { + ec = ErrorCode::NotImplemented; + SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); + } + } + return ec; +} + +std::error_code ClientManagerImpl::reportMessageConsumptionResult(const std::string& target_host, + const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) { + std::error_code ec; + auto client = getRpcClient(target_host); + grpc::ClientContext context; + auto deadline = std::chrono::system_clock::now() + timeout; + context.set_deadline(deadline); + + for (const auto& item : metadata) { + context.AddMetadata(item.first, item.second); + } + + ReportMessageConsumptionResultResponse response; + auto status = client->reportMessageConsumptionResult(&context, request, &response); + if (!status.ok()) { + ec = ErrorCode::RequestTimeout; + SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); + return ec; + } + + switch (response.common().status().code()) { + case google::rpc::Code::OK: { + return ec; + } + case google::rpc::Code::UNAUTHENTICATED: { + SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Unauthorized; + break; + } + case google::rpc::Code::PERMISSION_DENIED: { + SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); + ec = ErrorCode::Forbidden; + break; + } + default: { + ec = ErrorCode::NotImplemented; + SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); + } + } + return ec; +} + std::error_code ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) { @@ -1523,6 +1590,10 @@ void ClientManagerImpl::logStats() { SPDLOG_INFO("{}", stats); } +void ClientManagerImpl::submit(std::function task) { + callback_thread_pool_->submit(task); +} + const char* ClientManagerImpl::HEARTBEAT_TASK_NAME = "heartbeat-task"; const char* ClientManagerImpl::STATS_TASK_NAME = "stats-task"; const char* ClientManagerImpl::HEALTH_CHECK_TASK_NAME = "health-check-task"; diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 12af6d76d..cabced213 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -119,16 +119,27 @@ void RpcClientImpl::needHeartbeat(bool need_heartbeat) { need_heartbeat_ = need_heartbeat; } -void RpcClientImpl::asyncMultiplexingCall(const MultiplexingRequest& request, - InvocationContext* invocation_context) { - assert(invocation_context); +void RpcClientImpl::asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) { invocation_context->response_reader = - stub_->PrepareAsyncMultiplexingCall(&invocation_context->context, request, completion_queue_.get()); + stub_->PrepareAsyncPollCommand(&invocation_context->context, request, completion_queue_.get()); invocation_context->response_reader->StartCall(); invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, invocation_context); } +grpc::Status RpcClientImpl::reportThreadStackTrace(grpc::ClientContext* context, + const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) { + return stub_->ReportThreadStackTrace(context, request, response); +} + +grpc::Status RpcClientImpl::reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) { + return stub_->ReportMessageConsumptionResult(context, request, response); +} + grpc::Status RpcClientImpl::notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, NotifyClientTerminationResponse* response) { diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 20ea165d8..921256492 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "Client.h" #include "ReceiveMessageCallback.h" @@ -37,10 +38,10 @@ class ClientManager { std::chrono::milliseconds timeout, const std::function& cb) = 0; - virtual void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; - + virtual void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) = 0; + virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, @@ -88,7 +89,19 @@ class ClientManager { const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) = 0; + virtual std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) = 0; + + virtual std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) = 0; + virtual State state() const = 0; + + + virtual void submit(std::function task) = 0; + }; using ClientManagerPtr = std::shared_ptr; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 321664c23..086a4e1f2 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -165,9 +165,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::chrono::milliseconds timeout, const std::function& cb) override; - void multiplexingCall(const std::string& target, const Metadata& metadata, const MultiplexingRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) override; + void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, + std::chrono::milliseconds timeout, + const std::function*)>& cb) override; void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, std::chrono::milliseconds timeout, @@ -181,6 +181,14 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) override; + std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, + const ReportThreadStackTraceRequest& request, + std::chrono::milliseconds timeout) override; + + std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, + const ReportMessageConsumptionResultRequest& request, + std::chrono::milliseconds timeout) override; + void trace(bool trace) { trace_ = trace; } @@ -191,6 +199,8 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share State state() const override; + void submit(std::function task) override; + private: void doHeartbeat(); diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index 86522bdb6..2b93a08fa 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -40,24 +40,25 @@ using HealthCheckRequest = rmq::HealthCheckRequest; using HealthCheckResponse = rmq::HealthCheckResponse; using EndTransactionRequest = rmq::EndTransactionRequest; using EndTransactionResponse = rmq::EndTransactionResponse; -using MultiplexingRequest = rmq::MultiplexingRequest; -using MultiplexingResponse = rmq::MultiplexingResponse; -using GenericPollingRequest = rmq::GenericPollingRequest; -using GenericPollingResponse = rmq::GenericPollingRequest; -using PrintThreadStackRequest = rmq::PrintThreadStackRequest; -using PrintThreadStackResponse = rmq::PrintThreadStackResponse; -using VerifyMessageConsumptionRequest = rmq::VerifyMessageConsumptionRequest; -using VerifyMessageConsumptionResponse = rmq::VerifyMessageConsumptionResponse; -using RecoverOrphanedTransactionRequest = rmq::RecoverOrphanedTransactionRequest; using QueryOffsetRequest = rmq::QueryOffsetRequest; using QueryOffsetResponse = rmq::QueryOffsetResponse; using PullMessageRequest = rmq::PullMessageRequest; using PullMessageResponse = rmq::PullMessageResponse; +using PollCommandRequest = rmq::PollCommandRequest; +using PollCommandResponse = rmq::PollCommandResponse; +using ReportThreadStackTraceRequest = rmq::ReportThreadStackTraceRequest; +using ReportThreadStackTraceResponse = rmq::ReportThreadStackTraceResponse; +using ReportMessageConsumptionResultRequest = rmq::ReportMessageConsumptionResultRequest; +using ReportMessageConsumptionResultResponse = rmq::ReportMessageConsumptionResultResponse; using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; using NotifyClientTerminationRequest = rmq::NotifyClientTerminationRequest; using NotifyClientTerminationResponse = rmq::NotifyClientTerminationResponse; +/** + * @brief A RpcClient represents a session between client and a remote broker. + * + */ class RpcClient { public: RpcClient() = default; @@ -93,8 +94,8 @@ class RpcClient { virtual void asyncEndTransaction(const EndTransactionRequest& request, InvocationContext* invocation_context) = 0; - virtual void asyncMultiplexingCall(const MultiplexingRequest& request, - InvocationContext* invocation_context) = 0; + virtual void asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) = 0; virtual void asyncQueryOffset(const QueryOffsetRequest& request, InvocationContext* invocation_context) = 0; @@ -106,6 +107,14 @@ class RpcClient { const ForwardMessageToDeadLetterQueueRequest& request, InvocationContext* invocation_context) = 0; + virtual grpc::Status reportThreadStackTrace(grpc::ClientContext* context, + const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) = 0; + + virtual grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) = 0; + virtual grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, NotifyClientTerminationResponse* response) = 0; diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index 7cc782b49..e478ddaa3 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -50,8 +50,8 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; - void asyncMultiplexingCall(const MultiplexingRequest& request, - InvocationContext* invocation_context) override; + void asyncPollCommand(const PollCommandRequest& request, + InvocationContext* invocation_context) override; void asyncQueryOffset(const QueryOffsetRequest& request, InvocationContext* invocation_context) override; @@ -63,6 +63,13 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; + grpc::Status reportThreadStackTrace(grpc::ClientContext* context, const ReportThreadStackTraceRequest& request, + ReportThreadStackTraceResponse* response) override; + + grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, + const ReportMessageConsumptionResultRequest& request, + ReportMessageConsumptionResultResponse* response) override; + grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, NotifyClientTerminationResponse* response) override; diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 0f1fe7dfb..03f827e52 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -27,9 +27,9 @@ class ClientManagerMock : public ClientManager { (const std::function&)), (override)); - MOCK_METHOD(void, multiplexingCall, - (const std::string&, const Metadata&, const MultiplexingRequest&, std::chrono::milliseconds, - (const std::function*)>&)), + MOCK_METHOD(void, pollCommand, + (const std::string&, const Metadata&, const PollCommandRequest&, std::chrono::milliseconds, + const std::function*)>&), (override)); MOCK_METHOD(bool, wrapMessage, (const rmq::Message&, MQMessageExt&), (override)); @@ -88,7 +88,18 @@ class ClientManagerMock : public ClientManager { (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), (override)); + MOCK_METHOD(std::error_code, reportThreadStackTrace, + (const std::string&, const Metadata&, const ReportThreadStackTraceRequest&, std::chrono::milliseconds), + (override)); + + MOCK_METHOD(std::error_code, reportMessageConsumptionResult, + (const std::string&, const Metadata&, const ReportMessageConsumptionResultRequest&, + std::chrono::milliseconds), + (override)); + MOCK_METHOD(State, state, (), (const override)); + + MOCK_METHOD(void, submit, (std::function), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index a4363fdf7..dadafe09d 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -38,9 +38,6 @@ class RpcClientMock : public RpcClient { MOCK_METHOD(void, asyncEndTransaction, (const EndTransactionRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncMultiplexingCall, (const MultiplexingRequest&, InvocationContext*), - (override)); - MOCK_METHOD(void, asyncQueryOffset, (const QueryOffsetRequest&, InvocationContext*), (override)); MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); @@ -50,6 +47,17 @@ class RpcClientMock : public RpcClient { InvocationContext*), (override)); + MOCK_METHOD(void, asyncPollCommand, (const PollCommandRequest&, InvocationContext*), (override)); + + MOCK_METHOD(grpc::Status, reportThreadStackTrace, + (grpc::ClientContext*, const ReportThreadStackTraceRequest&, ReportThreadStackTraceResponse*), + (override)); + + MOCK_METHOD(grpc::Status, reportMessageConsumptionResult, + (grpc::ClientContext*, const ReportMessageConsumptionResultRequest&, + ReportMessageConsumptionResultResponse*), + (override)); + MOCK_METHOD(grpc::Status, notifyClientTermination, (grpc::ClientContext*, const NotifyClientTerminationRequest&, NotifyClientTerminationResponse* response), (override)); diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 294de2729..920187cf4 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -24,6 +24,7 @@ #include "MessageAccessor.h" #include "Signature.h" #include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" ROCKETMQ_NAMESPACE_BEGIN @@ -303,94 +304,133 @@ void ClientImpl::updateRouteCache(const std::string& topic, const std::error_cod } } -void ClientImpl::multiplexing(const std::string& target, const MultiplexingRequest& request) { +void ClientImpl::pollCommand(const std::string& target) { absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_manager_->multiplexingCall(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), - std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); + + PollCommandRequest request; + auto&& resource_bundle = resourceBundle(); + request.set_client_id(resource_bundle.client_id); + switch (resource_bundle.group_type) { + case GroupType::PUBLISHER: + request.mutable_producer_group()->set_resource_namespace(resource_namespace_); + request.mutable_producer_group()->set_name(group_name_); + break; + + case GroupType::SUBSCRIBER: + request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); + request.mutable_consumer_group()->set_name(group_name_); + break; + } + auto topics = request.mutable_topics(); + for (const auto& item : resource_bundle.topics) { + auto topic = new rmq::Resource(); + topic->set_resource_namespace(resource_namespace_); + topic->set_name(item); + topics->AddAllocated(topic); + } + + client_manager_->pollCommand(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), + std::bind(&ClientImpl::onPollCommandResponse, this, std::placeholders::_1)); +} + +void ClientImpl::verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message) { + MessageListener* listener = messageListener(); + + Metadata metadata; + Signature::sign(this, metadata); + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + + if (!listener) { + request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); + request.mutable_status()->set_message("Target is not a push consumer client"); + client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + return; + } + + if (MessageListenerType::FIFO == listener->listenerType()) { + request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); + request.mutable_status()->set_message("FIFO message does NOT support verification of message consumption"); + client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); + return; + } + + // Execute the actual verification task in dedicated thread-pool. + client_manager_->submit(std::bind(&ClientImpl::doVerify, this, remote_address, command_id, message)); } -void ClientImpl::onMultiplexingResponse(const InvocationContext* ctx) { +void ClientImpl::onPollCommandResponse(const InvocationContext* ctx) { if (!ctx->status.ok()) { - std::string remote_address = ctx->remote_address; - auto multiplexingLater = [this, remote_address]() { - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(remote_address, request); - }; - static std::string task_name = "Initiate multiplex request later"; - client_manager_->getScheduler().schedule(multiplexingLater, task_name, std::chrono::seconds(3), - std::chrono::seconds(0)); + static std::string task_name = "Poll-Command-Later"; + client_manager_->getScheduler().schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, + std::chrono::seconds(3), std::chrono::seconds(0)); return; } switch (ctx->response.type_case()) { - case MultiplexingResponse::TypeCase::kPrintThreadStackRequest: { - MultiplexingRequest request; - request.mutable_print_thread_stack_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::UNIMPLEMENTED); - request.mutable_print_thread_stack_response()->set_stack_trace( - "Print thread stack trace is not supported by C++ SDK"); + case PollCommandResponse::TypeCase::kPrintThreadStackTraceCommand: { absl::flat_hash_map metadata; Signature::sign(this, metadata); - client_manager_->multiplexingCall(ctx->remote_address, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - std::bind(&ClientImpl::onMultiplexingResponse, this, std::placeholders::_1)); + ReportThreadStackTraceRequest request; + auto command_id = ctx->response.print_thread_stack_trace_command().command_id(); + request.set_command_id(command_id); + request.set_thread_stack_trace("--RocketMQ-Client-CPP does NOT support thread stack trace report--"); + client_manager_->reportThreadStackTrace(ctx->remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); break; } - case MultiplexingResponse::TypeCase::kVerifyMessageConsumptionRequest: { - auto data = ctx->response.verify_message_consumption_request().message(); + case PollCommandResponse::TypeCase::kVerifyMessageConsumptionCommand: { + auto command_id = ctx->response.verify_message_consumption_command().command_id(); + auto data = ctx->response.verify_message_consumption_command().message(); MQMessageExt message; - MultiplexingRequest request; + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + Metadata metadata; + Signature::sign(this, metadata); if (!client_manager_->wrapMessage(data, message)) { SPDLOG_WARN("Message to verify consumption is corrupted"); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::INVALID_ARGUMENT); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message( - "Message to verify is corrupted"); - multiplexing(ctx->remote_address, request); - return; + request.mutable_status()->set_code(google::rpc::Code::INVALID_ARGUMENT); + request.mutable_status()->set_message("Data corrupted"); + client_manager_->reportMessageConsumptionResult(ctx->remote_address, metadata, request, + absl::ToChronoMilliseconds(io_timeout_)); } - std::string&& result = verifyMessageConsumption(message); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_code( - google::rpc::Code::OK); - request.mutable_verify_message_consumption_response()->mutable_common()->mutable_status()->set_message(result); - multiplexing(ctx->remote_address, request); + verifyMessageConsumption(std::move(ctx->remote_address), std::move(command_id), std::move(message)); break; } - case MultiplexingResponse::TypeCase::kRecoverOrphanedTransactionRequest: { - auto orphan = ctx->response.recover_orphaned_transaction_request().orphaned_transactional_message(); + case PollCommandResponse::TypeCase::kRecoverOrphanedTransactionCommand: { + auto orphan = ctx->response.recover_orphaned_transaction_command().orphaned_transactional_message(); MQMessageExt message; if (client_manager_->wrapMessage(orphan, message)) { MessageAccessor::setTargetEndpoint(message, ctx->remote_address); - const std::string& transaction_id = ctx->response.recover_orphaned_transaction_request().transaction_id(); - resolveOrphanedTransactionalMessage(transaction_id, message); + const std::string& transaction_id = ctx->response.recover_orphaned_transaction_command().transaction_id(); + // Dispatch task to thread-pool. + client_manager_->submit( + std::bind(&ClientImpl::resolveOrphanedTransactionalMessage, this, transaction_id, message)); } else { SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " "verification failure."); } - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); break; } - case MultiplexingResponse::TypeCase::kPollingResponse: { - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); + case PollCommandResponse::TypeCase::kNoopCommand: { + SPDLOG_DEBUG("A long-polling-command period completed."); break; } default: { SPDLOG_WARN("Unsupported multiplex type"); - MultiplexingRequest request; - fillGenericPollingRequest(request); - multiplexing(ctx->remote_address, request); break; } } + + // Initiate next round of long-polling-command immediately. + pollCommand(ctx->remote_address); } void ClientImpl::onRemoteEndpointRemoval(const std::vector& hosts) { @@ -451,30 +491,6 @@ void ClientImpl::onHealthCheckResponse(const std::error_code& ec, const Invocati } } -void ClientImpl::fillGenericPollingRequest(MultiplexingRequest& request) { - auto&& resource_bundle = resourceBundle(); - auto polling_request = request.mutable_polling_request(); - polling_request->set_client_id(clientId()); - switch (resource_bundle.group_type) { - case GroupType::PUBLISHER: - polling_request->mutable_producer_group()->set_resource_namespace(resource_namespace_); - polling_request->mutable_producer_group()->set_name(group_name_); - break; - - case GroupType::SUBSCRIBER: - polling_request->mutable_consumer_group()->set_resource_namespace(resource_namespace_); - polling_request->mutable_consumer_group()->set_name(group_name_); - break; - } - auto topics = polling_request->mutable_topics(); - for (const auto& item : resource_bundle.topics) { - auto topic = new rmq::Resource(); - topic->set_resource_namespace(resource_namespace_); - topic->set_name(item); - topics->AddAllocated(topic); - } -} - void ClientImpl::notifyClientTermination() { SPDLOG_WARN("Should NOT reach here. Subclass should have overridden this function."); std::abort(); @@ -492,4 +508,38 @@ void ClientImpl::notifyClientTermination(const NotifyClientTerminationRequest& r } } +void ClientImpl::doVerify(std::string target, std::string command_id, MQMessageExt message) { + ReportMessageConsumptionResultRequest request; + request.set_command_id(command_id); + StandardMessageListener* callback = reinterpret_cast(messageListener()); + try { + std::vector batch = {message}; + auto result = callback->consumeMessage(batch); + switch (result) { + case ConsumeMessageResult::SUCCESS: { + SPDLOG_DEBUG("Verify message[MsgId={}] OK", message.getMsgId()); + request.mutable_status()->set_message("Consume Success"); + break; + } + case ConsumeMessageResult::FAILURE: { + SPDLOG_WARN("Message Listener failed to consume message[MsgId={}] when verifying", message.getMsgId()); + request.mutable_status()->set_code(google::rpc::Code::INTERNAL); + request.mutable_status()->set_message("Consume Failed"); + break; + } + } + } catch (...) { + SPDLOG_WARN("Exception raised when invoking message listener provided by application developer. MsgId of message " + "to verify: {}", + message.getMsgId()); + request.mutable_status()->set_code(google::rpc::Code::INTERNAL); + request.mutable_status()->set_message( + "Unexpected exception raised while invoking message listener provided by application developer"); + } + + Metadata metadata; + Signature::sign(this, metadata); + client_manager_->reportMessageConsumptionResult(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 998d357d4..679f7b37e 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -18,6 +18,7 @@ #include "NameServerResolver.h" #include "OtlpExporter.h" #include "rocketmq/MQMessageExt.h" +#include "rocketmq/MessageListener.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN @@ -100,10 +101,16 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; - virtual std::string verifyMessageConsumption(const MQMessageExt& message) { - return "Unsupported"; - } + virtual void verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message); + /** + * @brief Execute transaction-state-checker to commit or roll-back the orphan transactional message. + * + * It is no-op by default and Producer-subclass is supposed to override it. + * + * @param transaction_id + * @param message + */ virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { } @@ -124,6 +131,17 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void notifyClientTermination(const NotifyClientTerminationRequest& request); + /** + * @brief Return application developer provided message listener if this client is of PushConsumer type. + * + * By default, it returns nullptr such that error messages are generated and directed to server immediately. + * + * @return nullptr by default. + */ + virtual MessageListener* messageListener() { + return nullptr; + } + private: /** * This is a low-level API that fetches route data from name server through @@ -153,14 +171,14 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(topic_route_table_mtx_); - void multiplexing(const std::string& target, const MultiplexingRequest& request); + void pollCommand(const std::string& target); - void onMultiplexingResponse(const InvocationContext* ctx); + void onPollCommandResponse(const InvocationContext* ctx); void onHealthCheckResponse(const std::error_code& endpoint, const InvocationContext* ctx) LOCKS_EXCLUDED(isolated_endpoints_mtx_); - void fillGenericPollingRequest(MultiplexingRequest& request); + void doVerify(std::string target, std::string command_id, MQMessageExt message); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file From 3dc6ee891eb0e20351957e0749ff081394206028 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 13 Oct 2021 08:25:16 +0000 Subject: [PATCH 097/133] Set group when end-transaction --- src/main/cpp/rocketmq/ProducerImpl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 8a6ae27fd..f37ab5731 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -341,6 +341,9 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& EndTransactionRequest request; request.set_message_id(message_id); request.set_transaction_id(transaction_id); + request.mutable_group()->set_name(group_name_); + request.mutable_group()->set_resource_namespace(resource_namespace_); + std::string action; switch (resolution) { case TransactionState::COMMIT: From 2b9aafb6991f2b7c47a77237326fa4aae9ef8cb4 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Thu, 14 Oct 2021 07:00:31 +0000 Subject: [PATCH 098/133] Install bazel from official repository --- .github/workflows/main.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dc3d39791..ffd352282 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,12 +26,9 @@ jobs: - uses: actions/checkout@v2 - name: Setup Bazel - uses: abhinavsingh/setup-bazel@v3 - with: - version: 4.1.0 - - name: Use Bazel (Windows only) - if: matrix.os == 'windows' - run: ./bazel.exe -h + run: | + sudo apt-get -qq install npm + sudo npm install -g @bazel/bazelisk - name: Use Bazel if: matrix.os != 'windows' run: bazel -h From 4bc0e350f77490a89da21f6fe0e825222a5d3ea0 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 14 Oct 2021 15:05:32 +0800 Subject: [PATCH 099/133] Update main.yml Trigger CI on commit or pull-request against main branch --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ffd352282..72c1725fd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,9 @@ name: CI on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master, develop ] + branches: [ main ] pull_request: - branches: [ master, develop ] + branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -35,4 +35,4 @@ jobs: - name: Compile All Targets run: bazel build //... - name: Run Unit Tests - run: bazel test --test_output=errors //src/test/cpp/ut/... \ No newline at end of file + run: bazel test --test_output=errors //src/test/cpp/ut/... From 6984b94343c75f5c5b6a7e945e1d45313409fbc8 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 11:36:23 +0800 Subject: [PATCH 100/133] Add utility to fix file header copyright comment --- tools/copyright.txt | 14 ++++++++++++++ tools/fix_copyright.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tools/copyright.txt create mode 100755 tools/fix_copyright.py diff --git a/tools/copyright.txt b/tools/copyright.txt new file mode 100644 index 000000000..1745cfe66 --- /dev/null +++ b/tools/copyright.txt @@ -0,0 +1,14 @@ +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/fix_copyright.py b/tools/fix_copyright.py new file mode 100755 index 000000000..263fe814c --- /dev/null +++ b/tools/fix_copyright.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +import os +import fnmatch + +def checkAndFixFileHeaderComment(fileName, template): + lines = [] + with open(fileName, 'rw') as f: + lines = f.readlines() + + print(template) + for line in lines: + print(line) + + + +def main(): + template = "/*\n" + + with open('tools/copyright.txt', 'r') as reader: + line = reader.readline() + while line != '': + template += " * " + line + line = reader.readline() + template += " */" + + for root, dir, files in os.walk(os.curdir): + if ".git/" in root: + continue + + for item in fnmatch.filter(files, "*.h"): + checkAndFixFileHeaderComment(root + os.sep + item, template) + + for item in fnmatch.filter(files, "*.cpp"): + checkAndFixFileHeaderComment(root + os.sep + item, template) + + +if __name__ == "__main__": + main() \ No newline at end of file From a0db0cbea37203cc7cb5cd39a5098953840e3939 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 12:06:47 +0800 Subject: [PATCH 101/133] BugFix: Add copyright file header comment --- BUILD | 0 BUILD.bazel | 16 +++++++ api/BUILD.bazel | 16 +++++++ api/rocketmq/AdminServer.h | 16 +++++++ api/rocketmq/AsyncCallback.h | 16 +++++++ api/rocketmq/ConsumeType.h | 16 +++++++ api/rocketmq/Credentials.h | 16 +++++++ api/rocketmq/CredentialsProvider.h | 16 +++++++ api/rocketmq/DefaultMQProducer.h | 16 +++++++ api/rocketmq/DefaultMQPullConsumer.h | 16 +++++++ api/rocketmq/DefaultMQPushConsumer.h | 16 +++++++ api/rocketmq/ErrorCategory.h | 16 +++++++ api/rocketmq/ErrorCode.h | 16 +++++++ api/rocketmq/Executor.h | 16 +++++++ api/rocketmq/ExpressionType.h | 16 +++++++ api/rocketmq/LocalTransactionStateChecker.h | 16 +++++++ api/rocketmq/Logger.h | 16 +++++++ api/rocketmq/MQClientException.h | 16 +++++++ api/rocketmq/MQMessage.h | 16 +++++++ api/rocketmq/MQMessageExt.h | 16 +++++++ api/rocketmq/MQMessageQueue.h | 16 +++++++ api/rocketmq/MQSelector.h | 16 +++++++ api/rocketmq/MessageListener.h | 16 +++++++ api/rocketmq/MessageModel.h | 16 +++++++ api/rocketmq/MessageType.h | 16 +++++++ api/rocketmq/OffsetStore.h | 16 +++++++ api/rocketmq/PullResult.h | 16 +++++++ api/rocketmq/RocketMQ.h | 16 +++++++ api/rocketmq/SendResult.h | 16 +++++++ api/rocketmq/State.h | 16 +++++++ api/rocketmq/Transaction.h | 16 +++++++ bazel/BUILD | 0 bazel/BUILD.bazel | 16 +++++++ example/rocketmq/BUILD.bazel | 16 +++++++ example/rocketmq/BenchmarkPushConsumer.cpp | 17 +++++++- example/rocketmq/ExampleAsyncProducer.cpp | 17 +++++++- .../rocketmq/ExampleBroadcastPushConsumer.cpp | 17 +++++++- example/rocketmq/ExampleProducer.cpp | 17 +++++++- example/rocketmq/ExamplePullConsumer.cpp | 17 +++++++- example/rocketmq/ExamplePushConsumer.cpp | 17 +++++++- .../rocketmq/ExampleTransactionProducer.cpp | 17 +++++++- .../PushConsumerWithCustomExecutor.cpp | 17 +++++++- example/rocketmq/PushConsumerWithThrottle.cpp | 17 +++++++- example/rocketmq/SqlConsumer.cpp | 17 +++++++- example/rocketmq/SqlProducer.cpp | 17 +++++++- proto/BUILD.bazel | 16 +++++++ src/main/cpp/admin/AdminClient.cpp | 16 +++++++ src/main/cpp/admin/AdminFacade.cpp | 16 +++++++ src/main/cpp/admin/AdminServerImpl.cpp | 16 +++++++ src/main/cpp/admin/AdminServiceImpl.cpp | 16 +++++++ src/main/cpp/admin/BUILD.bazel | 16 +++++++ src/main/cpp/admin/include/AdminClient.h | 16 +++++++ src/main/cpp/admin/include/AdminServerImpl.h | 16 +++++++ src/main/cpp/admin/include/AdminServiceImpl.h | 16 +++++++ src/main/cpp/admin/include/ServerCall.h | 16 +++++++ src/main/cpp/base/BUILD.bazel | 16 +++++++ src/main/cpp/base/ErrorCategory.cpp | 16 +++++++ src/main/cpp/base/ErrorCode.cpp | 16 +++++++ src/main/cpp/base/HostInfo.cpp | 16 +++++++ src/main/cpp/base/HttpClientImpl.cpp | 16 +++++++ src/main/cpp/base/MQMessage.cpp | 16 +++++++ src/main/cpp/base/MQMessageExt.cpp | 16 +++++++ src/main/cpp/base/MessageAccessor.cpp | 16 +++++++ src/main/cpp/base/MetadataConstants.cpp | 16 +++++++ src/main/cpp/base/MixAll.cpp | 16 +++++++ src/main/cpp/base/Protocol.cpp | 16 +++++++ src/main/cpp/base/RateLimiter.cpp | 16 +++++++ src/main/cpp/base/ThreadPoolImpl.cpp | 16 +++++++ src/main/cpp/base/TopAddressing.cpp | 16 +++++++ src/main/cpp/base/UniqueIdGenerator.cpp | 16 +++++++ src/main/cpp/base/UtilAll.cpp | 16 +++++++ src/main/cpp/base/include/DigestType.h | 16 +++++++ src/main/cpp/base/include/Encoding.h | 16 +++++++ src/main/cpp/base/include/Histogram.h | 16 +++++++ src/main/cpp/base/include/HostInfo.h | 16 +++++++ src/main/cpp/base/include/HttpClient.h | 16 +++++++ src/main/cpp/base/include/HttpClientImpl.h | 16 +++++++ src/main/cpp/base/include/InvocationContext.h | 16 +++++++ src/main/cpp/base/include/MessageAccessor.h | 16 +++++++ src/main/cpp/base/include/MessageImpl.h | 16 +++++++ src/main/cpp/base/include/MetadataConstants.h | 16 +++++++ src/main/cpp/base/include/MixAll.h | 16 +++++++ src/main/cpp/base/include/Protocol.h | 16 +++++++ src/main/cpp/base/include/RateLimiter.h | 16 +++++++ src/main/cpp/base/include/ThreadPool.h | 16 +++++++ src/main/cpp/base/include/ThreadPoolImpl.h | 16 +++++++ src/main/cpp/base/include/TopAddressing.h | 16 +++++++ src/main/cpp/base/include/UniqueIdGenerator.h | 16 +++++++ src/main/cpp/base/include/UtilAll.h | 16 +++++++ src/main/cpp/base/mocks/BUILD.bazel | 16 +++++++ .../cpp/base/mocks/include/HttpClientMock.h | 16 +++++++ .../base/mocks/include/MessageListenerMock.h | 16 +++++++ src/main/cpp/client/BUILD.bazel | 16 +++++++ src/main/cpp/client/ClientConfigImpl.cpp | 16 +++++++ src/main/cpp/client/ClientManagerFactory.cpp | 16 +++++++ src/main/cpp/client/ClientManagerImpl.cpp | 16 +++++++ src/main/cpp/client/LogInterceptor.cpp | 16 +++++++ src/main/cpp/client/LogInterceptorFactory.cpp | 16 +++++++ src/main/cpp/client/RpcClientImpl.cpp | 16 +++++++ src/main/cpp/client/Signature.cpp | 16 +++++++ src/main/cpp/client/TlsHelper.cpp | 16 +++++++ src/main/cpp/client/TopicAssignmentInfo.cpp | 16 +++++++ src/main/cpp/client/TopicPublishInfo.cpp | 16 +++++++ src/main/cpp/client/TracingUtility.cpp | 16 +++++++ src/main/cpp/client/include/Assignment.h | 16 +++++++ src/main/cpp/client/include/Broker.h | 16 +++++++ src/main/cpp/client/include/Client.h | 16 +++++++ src/main/cpp/client/include/ClientConfig.h | 16 +++++++ .../cpp/client/include/ClientConfigImpl.h | 16 +++++++ src/main/cpp/client/include/ClientManager.h | 16 +++++++ .../cpp/client/include/ClientManagerFactory.h | 16 +++++++ .../cpp/client/include/ClientManagerImpl.h | 16 +++++++ .../cpp/client/include/ConsumeMessageType.h | 16 +++++++ .../client/include/HeartbeatDataCallback.h | 16 +++++++ .../client/include/InterceptorContinuation.h | 16 +++++++ src/main/cpp/client/include/LogInterceptor.h | 16 +++++++ .../client/include/LogInterceptorFactory.h | 16 +++++++ .../cpp/client/include/OpenSSLCompatible.h | 16 +++++++ .../include/OrphanTransactionCallback.h | 16 +++++++ src/main/cpp/client/include/Partition.h | 16 +++++++ .../client/include/ReceiveMessageCallback.h | 16 +++++++ .../cpp/client/include/ReceiveMessageResult.h | 16 +++++++ src/main/cpp/client/include/RpcClient.h | 16 +++++++ src/main/cpp/client/include/RpcClientImpl.h | 16 +++++++ .../cpp/client/include/SendMessageContext.h | 16 +++++++ src/main/cpp/client/include/ServiceAddress.h | 16 +++++++ src/main/cpp/client/include/Signature.h | 16 +++++++ src/main/cpp/client/include/TlsHelper.h | 16 +++++++ src/main/cpp/client/include/Topic.h | 16 +++++++ .../cpp/client/include/TopicAssignmentInfo.h | 16 +++++++ .../cpp/client/include/TopicPublishInfo.h | 16 +++++++ .../client/include/TopicRouteChangeCallback.h | 16 +++++++ src/main/cpp/client/include/TopicRouteData.h | 16 +++++++ src/main/cpp/client/mocks/BUILD.bazel | 16 +++++++ src/main/cpp/client/mocks/RpcClientMock.cpp | 16 +++++++ .../client/mocks/include/ClientConfigMock.h | 16 +++++++ .../client/mocks/include/ClientManagerMock.h | 16 +++++++ .../cpp/client/mocks/include/ClientMock.h | 16 +++++++ .../include/ReceiveMessageCallbackMock.h | 16 +++++++ .../cpp/client/mocks/include/RpcClientMock.h | 16 +++++++ src/main/cpp/concurrent/BUILD.bazel | 16 +++++++ src/main/cpp/concurrent/CountdownLatch.cpp | 16 +++++++ .../cpp/concurrent/include/CountdownLatch.h | 16 +++++++ src/main/cpp/log/BUILD.bazel | 16 +++++++ src/main/cpp/log/LoggerImpl.cpp | 16 +++++++ src/main/cpp/log/include/LoggerImpl.h | 16 +++++++ src/main/cpp/remoting/BUILD.bazel | 16 +++++++ src/main/cpp/remoting/BrokerData.cpp | 16 +++++++ .../cpp/remoting/QueryRouteRequestHeader.cpp | 16 +++++++ src/main/cpp/remoting/QueueData.cpp | 16 +++++++ src/main/cpp/remoting/RemotingCommand.cpp | 16 +++++++ src/main/cpp/remoting/TopicRouteData.cpp | 16 +++++++ src/main/cpp/remoting/include/BrokerData.h | 16 +++++++ .../remoting/include/CommandCustomHeader.h | 16 +++++++ src/main/cpp/remoting/include/LanguageCode.h | 16 +++++++ .../include/QueryRouteRequestHeader.h | 16 +++++++ src/main/cpp/remoting/include/QueueData.h | 16 +++++++ .../cpp/remoting/include/RemotingCommand.h | 16 +++++++ .../remoting/include/RemotingCommandType.h | 16 +++++++ src/main/cpp/remoting/include/RequestCode.h | 16 +++++++ src/main/cpp/remoting/include/ResponseCode.h | 16 +++++++ .../cpp/remoting/include/TopicRouteData.h | 16 +++++++ src/main/cpp/remoting/include/Version.h | 16 +++++++ .../rocketmq/AsyncReceiveMessageCallback.cpp | 16 +++++++ src/main/cpp/rocketmq/AwaitPullCallback.cpp | 16 +++++++ src/main/cpp/rocketmq/BUILD.bazel | 16 +++++++ src/main/cpp/rocketmq/ClientImpl.cpp | 16 +++++++ .../rocketmq/ConsumeFifoMessageService.cpp | 16 +++++++ .../rocketmq/ConsumeMessageServiceBase.cpp | 16 +++++++ .../ConsumeStandardMessageService.cpp | 16 +++++++ src/main/cpp/rocketmq/CredentialsProvider.cpp | 16 +++++++ src/main/cpp/rocketmq/DefaultMQProducer.cpp | 16 +++++++ .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 16 +++++++ .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 16 +++++++ .../rocketmq/DynamicNameServerResolver.cpp | 16 +++++++ src/main/cpp/rocketmq/FilterExpression.cpp | 16 +++++++ .../rocketmq/MessageGroupQueueSelector.cpp | 16 +++++++ src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 16 +++++++ src/main/cpp/rocketmq/ProducerImpl.cpp | 16 +++++++ src/main/cpp/rocketmq/PullConsumerImpl.cpp | 16 +++++++ src/main/cpp/rocketmq/PushConsumerImpl.cpp | 16 +++++++ src/main/cpp/rocketmq/SendCallbacks.cpp | 16 +++++++ .../cpp/rocketmq/StaticNameServerResolver.cpp | 16 +++++++ src/main/cpp/rocketmq/TransactionImpl.cpp | 16 +++++++ .../include/AsyncReceiveMessageCallback.h | 16 +++++++ .../cpp/rocketmq/include/AwaitPullCallback.h | 16 +++++++ src/main/cpp/rocketmq/include/ClientImpl.h | 16 +++++++ .../rocketmq/include/ClientResourceBundle.h | 16 +++++++ .../include/ConsumeFifoMessageService.h | 16 +++++++ .../rocketmq/include/ConsumeMessageService.h | 16 +++++++ .../include/ConsumeMessageServiceBase.h | 16 +++++++ .../include/ConsumeStandardMessageService.h | 16 +++++++ src/main/cpp/rocketmq/include/Consumer.h | 16 +++++++ .../include/DynamicNameServerResolver.h | 16 +++++++ .../cpp/rocketmq/include/FilterExpression.h | 16 +++++++ .../include/MessageGroupQueueSelector.h | 16 +++++++ .../cpp/rocketmq/include/NameServerResolver.h | 16 +++++++ src/main/cpp/rocketmq/include/ProcessQueue.h | 16 +++++++ .../cpp/rocketmq/include/ProcessQueueImpl.h | 16 +++++++ src/main/cpp/rocketmq/include/ProducerImpl.h | 16 +++++++ .../cpp/rocketmq/include/PullConsumerImpl.h | 16 +++++++ src/main/cpp/rocketmq/include/PushConsumer.h | 16 +++++++ .../cpp/rocketmq/include/PushConsumerImpl.h | 16 +++++++ src/main/cpp/rocketmq/include/SendCallbacks.h | 16 +++++++ .../include/StaticNameServerResolver.h | 16 +++++++ .../include/StsCredentialsProviderImpl.h | 16 +++++++ .../cpp/rocketmq/include/TransactionImpl.h | 16 +++++++ src/main/cpp/rocketmq/mocks/BUILD.bazel | 16 +++++++ .../mocks/include/ConsumeMessageServiceMock.h | 16 +++++++ .../cpp/rocketmq/mocks/include/ConsumerMock.h | 16 +++++++ .../mocks/include/NameServerResolverMock.h | 16 +++++++ .../rocketmq/mocks/include/ProcessQueueMock.h | 16 +++++++ .../rocketmq/mocks/include/PushConsumerMock.h | 16 +++++++ src/main/cpp/scheduler/BUILD.bazel | 16 +++++++ src/main/cpp/scheduler/SchedulerImpl.cpp | 16 +++++++ src/main/cpp/scheduler/include/Scheduler.h | 16 +++++++ .../cpp/scheduler/include/SchedulerImpl.h | 16 +++++++ src/main/cpp/tracing/exporters/BUILD.bazel | 16 +++++++ .../cpp/tracing/exporters/OtlpExporter.cpp | 16 +++++++ .../tracing/exporters/include/OtlpExporter.h | 16 +++++++ src/test/cpp/benchmark/BUILD.bazel | 16 +++++++ src/test/cpp/benchmark/BenchmarkTest.cpp | 16 +++++++ src/test/cpp/it/BUILD.bazel | 16 +++++++ src/test/cpp/it/RpcClientTest.cpp | 16 +++++++ src/test/cpp/it/TopAddressingTest.cpp | 16 +++++++ src/test/cpp/it/TopicPublishInfoTest.cpp | 16 +++++++ src/test/cpp/ut/BUILD.bazel | 17 ++++++++ src/test/cpp/ut/admin/AdminServerTest.cpp | 16 +++++++ src/test/cpp/ut/admin/BUILD.bazel | 16 +++++++ src/test/cpp/ut/api/BUILD.bazel | 16 +++++++ src/test/cpp/ut/api/ErrorCodeTest.cpp | 16 +++++++ src/test/cpp/ut/base/BUILD.bazel | 16 +++++++ src/test/cpp/ut/base/HistogramTest.cpp | 16 +++++++ src/test/cpp/ut/base/HostInfoTest.cpp | 16 +++++++ src/test/cpp/ut/base/HttpClientTest.cpp | 16 +++++++ .../cpp/ut/base/InvocationContextTest.cpp | 16 +++++++ src/test/cpp/ut/base/MQMessageExtTest.cpp | 16 +++++++ src/test/cpp/ut/base/MQMessageTest.cpp | 16 +++++++ src/test/cpp/ut/base/MixAllTest.cpp | 16 +++++++ src/test/cpp/ut/base/RateLimiterTest.cpp | 16 +++++++ src/test/cpp/ut/base/ThreadPoolTest.cpp | 16 +++++++ src/test/cpp/ut/base/TopAddressingTest.cpp | 16 +++++++ .../cpp/ut/base/UniqueIdGeneratorTest.cpp | 16 +++++++ src/test/cpp/ut/base/UtilAllTest.cpp | 16 +++++++ src/test/cpp/ut/client/BUILD.bazel | 16 +++++++ .../ut/client/ClientManagerFactoryTest.cpp | 16 +++++++ src/test/cpp/ut/client/ClientManagerTest.cpp | 16 +++++++ src/test/cpp/ut/client/ClientTest.cpp | 16 +++++++ src/test/cpp/ut/client/RpcClientTest.cpp | 16 +++++++ src/test/cpp/ut/client/TlsHelperTest.cpp | 16 +++++++ .../cpp/ut/client/TopicAssignmentInfoTest.cpp | 16 +++++++ src/test/cpp/ut/client/TracingUtilityTest.cpp | 16 +++++++ src/test/cpp/ut/concurrent/BUILD.bazel | 16 +++++++ .../cpp/ut/concurrent/CountdownLatchTest.cpp | 16 +++++++ src/test/cpp/ut/grpc/BUILD.bazel | 16 +++++++ src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp | 16 +++++++ src/test/cpp/ut/grpc/GrpcTimer.cpp | 16 +++++++ src/test/cpp/ut/logger/BUILD.bazel | 16 +++++++ src/test/cpp/ut/logger/LoggerTest.cpp | 16 +++++++ src/test/cpp/ut/remoting/BUILD.bazel | 16 +++++++ src/test/cpp/ut/remoting/BrokerDataTest.cpp | 16 +++++++ .../remoting/QueryRouteRequestHeaderTest.cpp | 16 +++++++ src/test/cpp/ut/remoting/QueueDataTest.cpp | 16 +++++++ .../cpp/ut/remoting/RemotingCommandTest.cpp | 16 +++++++ .../cpp/ut/remoting/TopicRouteDataTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/AwaitPullCallbackTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/BUILD.bazel | 16 +++++++ src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 16 +++++++ .../ConsumeStandardMessageServiceTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/ConsumerTest.cpp | 16 +++++++ .../ut/rocketmq/CredentialsProviderTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 16 +++++++ .../ut/rocketmq/DefaultMQPushConsumerTest.cpp | 16 +++++++ .../DynamicNameServerResolverTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/FilterExpressionTest.cpp | 16 +++++++ .../MessageGroupQueueSelectorTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/MessageTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 16 +++++++ src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/SendCallbacksTest.cpp | 16 +++++++ .../rocketmq/StaticNameServerResolverTest.cpp | 16 +++++++ .../StsCredentialsProviderImplTest.cpp | 16 +++++++ .../cpp/ut/rocketmq/include/MQClientTest.h | 16 +++++++ src/test/cpp/ut/scheduler/BUILD.bazel | 16 +++++++ src/test/cpp/ut/scheduler/SchedulerTest.cpp | 16 +++++++ src/test/cpp/ut/tracing/exporters/BUILD.bazel | 16 +++++++ .../tracing/exporters/OtlpExportersTest.cpp | 16 +++++++ .../ut/tracing/exporters/SpanContextTest.cpp | 16 +++++++ third_party/BUILD | 0 third_party/BUILD.bazel | 16 +++++++ tools/fix_copyright.py | 43 ++++++++++++++----- 294 files changed, 4673 insertions(+), 22 deletions(-) delete mode 100644 BUILD create mode 100644 BUILD.bazel delete mode 100644 bazel/BUILD create mode 100644 bazel/BUILD.bazel create mode 100644 src/test/cpp/ut/BUILD.bazel delete mode 100644 third_party/BUILD create mode 100644 third_party/BUILD.bazel diff --git a/BUILD b/BUILD deleted file mode 100644 index e69de29bb..000000000 diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/api/BUILD.bazel b/api/BUILD.bazel index 86934279c..b7eb594a7 100644 --- a/api/BUILD.bazel +++ b/api/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( diff --git a/api/rocketmq/AdminServer.h b/api/rocketmq/AdminServer.h index afd5dee23..11b92590c 100644 --- a/api/rocketmq/AdminServer.h +++ b/api/rocketmq/AdminServer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "State.h" diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index 782b90c77..0e7add77c 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/ConsumeType.h b/api/rocketmq/ConsumeType.h index d0337dc51..9c11a144a 100644 --- a/api/rocketmq/ConsumeType.h +++ b/api/rocketmq/ConsumeType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index aba5fc731..27c895d60 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index 0a61126d2..ffed50855 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 4110fd20f..8c80f9280 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index 9b7d3ef2f..959acd0f8 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index 28a479866..ced50bc21 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/ErrorCategory.h b/api/rocketmq/ErrorCategory.h index fe928163c..432e66073 100644 --- a/api/rocketmq/ErrorCategory.h +++ b/api/rocketmq/ErrorCategory.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ErrorCode.h" diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index af3078052..1e0e261bd 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "RocketMQ.h" diff --git a/api/rocketmq/Executor.h b/api/rocketmq/Executor.h index ccd4e0f41..a7321b5cd 100644 --- a/api/rocketmq/Executor.h +++ b/api/rocketmq/Executor.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h index fbf99ace6..9418fea93 100644 --- a/api/rocketmq/ExpressionType.h +++ b/api/rocketmq/ExpressionType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/api/rocketmq/LocalTransactionStateChecker.h index 0badebe59..78110bcdd 100644 --- a/api/rocketmq/LocalTransactionStateChecker.h +++ b/api/rocketmq/LocalTransactionStateChecker.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h index 586619927..86e20d749 100644 --- a/api/rocketmq/Logger.h +++ b/api/rocketmq/Logger.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h index 9e77bf7a2..115f5abdd 100644 --- a/api/rocketmq/MQClientException.h +++ b/api/rocketmq/MQClientException.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index 46c479cd6..da9b66fb8 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h index ad5942e55..688ef2561 100644 --- a/api/rocketmq/MQMessageExt.h +++ b/api/rocketmq/MQMessageExt.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h index 6e84fa858..9e701b7a6 100644 --- a/api/rocketmq/MQMessageQueue.h +++ b/api/rocketmq/MQMessageQueue.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MQSelector.h b/api/rocketmq/MQSelector.h index 029c6f89a..edf5b7c33 100644 --- a/api/rocketmq/MQSelector.h +++ b/api/rocketmq/MQSelector.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h index fbe6a1945..03a963db3 100644 --- a/api/rocketmq/MessageListener.h +++ b/api/rocketmq/MessageListener.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "MQMessageExt.h" diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h index 7227f5228..74cdfa362 100644 --- a/api/rocketmq/MessageModel.h +++ b/api/rocketmq/MessageModel.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "RocketMQ.h" diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/MessageType.h index 3e04f3e11..e4cc46203 100644 --- a/api/rocketmq/MessageType.h +++ b/api/rocketmq/MessageType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/OffsetStore.h b/api/rocketmq/OffsetStore.h index 309bc0209..514d5da84 100644 --- a/api/rocketmq/OffsetStore.h +++ b/api/rocketmq/OffsetStore.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "MQMessageQueue.h" diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h index 57e9890c1..ebc544841 100644 --- a/api/rocketmq/PullResult.h +++ b/api/rocketmq/PullResult.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/RocketMQ.h b/api/rocketmq/RocketMQ.h index 6c1f4d1d4..3b114e6bc 100644 --- a/api/rocketmq/RocketMQ.h +++ b/api/rocketmq/RocketMQ.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #ifndef ROCKETMQ_NAMESPACE diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index 7765141b5..c7d430740 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/State.h b/api/rocketmq/State.h index ec3c0a67b..93a5061ea 100644 --- a/api/rocketmq/State.h +++ b/api/rocketmq/State.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index 352fdaded..867a21037 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/bazel/BUILD b/bazel/BUILD deleted file mode 100644 index e69de29bb..000000000 diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/bazel/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel index f0853ba06..a2b2dd386 100644 --- a/example/rocketmq/BUILD.bazel +++ b/example/rocketmq/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_binary") cc_binary( diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp index 3585b4e4f..ab74e09c6 100644 --- a/example/rocketmq/BenchmarkPushConsumer.cpp +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQPushConsumer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQPushConsumer.h" #include #include diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 3bf3801ac..1c624cd99 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQProducer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQProducer.h" #include "rocketmq/ErrorCode.h" #include #include diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 64b527aa3..2afda66bf 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQPushConsumer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQPushConsumer.h" #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 6452509ff..7e15364e0 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQProducer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQProducer.h" #include #include #include diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp index cd745ffdd..2b87b101c 100644 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -1,4 +1,19 @@ -#include +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include #include #include "rocketmq/CredentialsProvider.h" diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index c792c9ce2..5dc0df344 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -1,4 +1,19 @@ -#include +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include #include #include #include diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/example/rocketmq/ExampleTransactionProducer.cpp index 04646cfed..bc125ae2e 100644 --- a/example/rocketmq/ExampleTransactionProducer.cpp +++ b/example/rocketmq/ExampleTransactionProducer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQProducer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQProducer.h" #include using namespace ROCKETMQ_NAMESPACE; diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp index 000d889af..9d6e8a976 100644 --- a/example/rocketmq/PushConsumerWithCustomExecutor.cpp +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQPushConsumer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQPushConsumer.h" #include "rocketmq/State.h" #include #include diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp index e8969cff0..d7b24cd82 100644 --- a/example/rocketmq/PushConsumerWithThrottle.cpp +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQPushConsumer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQPushConsumer.h" #include #include diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp index 82b54df20..a4a739d0f 100644 --- a/example/rocketmq/SqlConsumer.cpp +++ b/example/rocketmq/SqlConsumer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQPushConsumer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQPushConsumer.h" #include #include diff --git a/example/rocketmq/SqlProducer.cpp b/example/rocketmq/SqlProducer.cpp index f9bbe1cbe..02a020990 100644 --- a/example/rocketmq/SqlProducer.cpp +++ b/example/rocketmq/SqlProducer.cpp @@ -1,4 +1,19 @@ -#include "rocketmq/DefaultMQProducer.h" +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */#include "rocketmq/DefaultMQProducer.h" #include using namespace rocketmq; diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel index 0863bd830..3416bf41e 100644 --- a/proto/BUILD.bazel +++ b/proto/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_proto//proto:defs.bzl", "proto_library") load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library", "cpp_grpc_compile") diff --git a/src/main/cpp/admin/AdminClient.cpp b/src/main/cpp/admin/AdminClient.cpp index 2900d6904..e7f260c59 100644 --- a/src/main/cpp/admin/AdminClient.cpp +++ b/src/main/cpp/admin/AdminClient.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AdminClient.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/admin/AdminFacade.cpp b/src/main/cpp/admin/AdminFacade.cpp index 314b59b73..a17479c57 100644 --- a/src/main/cpp/admin/AdminFacade.cpp +++ b/src/main/cpp/admin/AdminFacade.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AdminServerImpl.h" using namespace ROCKETMQ_NAMESPACE::admin; diff --git a/src/main/cpp/admin/AdminServerImpl.cpp b/src/main/cpp/admin/AdminServerImpl.cpp index d77b4fb01..a41c520f1 100644 --- a/src/main/cpp/admin/AdminServerImpl.cpp +++ b/src/main/cpp/admin/AdminServerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AdminServerImpl.h" #include "AdminServiceImpl.h" diff --git a/src/main/cpp/admin/AdminServiceImpl.cpp b/src/main/cpp/admin/AdminServiceImpl.cpp index 283ef5385..f7ab19fc8 100644 --- a/src/main/cpp/admin/AdminServiceImpl.cpp +++ b/src/main/cpp/admin/AdminServiceImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AdminServiceImpl.h" #include "spdlog/spdlog.h" diff --git a/src/main/cpp/admin/BUILD.bazel b/src/main/cpp/admin/BUILD.bazel index 1e18e46bf..1e8efe9bb 100644 --- a/src/main/cpp/admin/BUILD.bazel +++ b/src/main/cpp/admin/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library", "cc_binary") cc_library( name = "admin_server_library", diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h index 9878f3359..da99e5876 100644 --- a/src/main/cpp/admin/include/AdminClient.h +++ b/src/main/cpp/admin/include/AdminClient.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "apache/rocketmq/v1/admin.grpc.pb.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h index ea4b941a6..c396a1387 100644 --- a/src/main/cpp/admin/include/AdminServerImpl.h +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "AdminServiceImpl.h" diff --git a/src/main/cpp/admin/include/AdminServiceImpl.h b/src/main/cpp/admin/include/AdminServiceImpl.h index 2fa865af0..78c398042 100644 --- a/src/main/cpp/admin/include/AdminServiceImpl.h +++ b/src/main/cpp/admin/include/AdminServiceImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "apache/rocketmq/v1/admin.grpc.pb.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h index 297b6f1bf..0c20ca784 100644 --- a/src/main/cpp/admin/include/ServerCall.h +++ b/src/main/cpp/admin/include/ServerCall.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 1d99854ed..5c7ab81c0 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp index dec677225..9110f5986 100644 --- a/src/main/cpp/base/ErrorCategory.cpp +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/ErrorCategory.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/ErrorCode.cpp b/src/main/cpp/base/ErrorCode.cpp index b6e44dc2d..7d8be7d72 100644 --- a/src/main/cpp/base/ErrorCode.cpp +++ b/src/main/cpp/base/ErrorCode.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/ErrorCategory.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp index 89c9a52a9..160e0b019 100644 --- a/src/main/cpp/base/HostInfo.cpp +++ b/src/main/cpp/base/HostInfo.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "HostInfo.h" #include "absl/strings/match.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/base/HttpClientImpl.cpp b/src/main/cpp/base/HttpClientImpl.cpp index d4b484325..2bf8a5fa8 100644 --- a/src/main/cpp/base/HttpClientImpl.cpp +++ b/src/main/cpp/base/HttpClientImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "HttpClientImpl.h" #include diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 3dd4ca58d..9af46ec58 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/MQMessage.h" #include "MessageAccessor.h" #include "MessageImpl.h" diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp index af5127183..ed9009c2e 100644 --- a/src/main/cpp/base/MQMessageExt.cpp +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/MQMessageExt.h" #include "MessageImpl.h" diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp index 5ed587868..1a2e0c0e1 100644 --- a/src/main/cpp/base/MessageAccessor.cpp +++ b/src/main/cpp/base/MessageAccessor.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MessageAccessor.h" #include "MessageImpl.h" #include "rocketmq/MQMessage.h" diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp index 42777244c..39022d9c0 100644 --- a/src/main/cpp/base/MetadataConstants.cpp +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MetadataConstants.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index b64320307..b69c56696 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MixAll.h" #include diff --git a/src/main/cpp/base/Protocol.cpp b/src/main/cpp/base/Protocol.cpp index 563ef3eea..d82ebba63 100644 --- a/src/main/cpp/base/Protocol.cpp +++ b/src/main/cpp/base/Protocol.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "Protocol.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/RateLimiter.cpp b/src/main/cpp/base/RateLimiter.cpp index e53bfb873..a0b48fbb9 100644 --- a/src/main/cpp/base/RateLimiter.cpp +++ b/src/main/cpp/base/RateLimiter.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RateLimiter.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp index ab2b8f365..4ab1c2917 100644 --- a/src/main/cpp/base/ThreadPoolImpl.cpp +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ThreadPoolImpl.h" #include "absl/memory/memory.h" #include "asio/executor_work_guard.hpp" diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index af6cbabad..6d2bcf588 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopAddressing.h" #include "HttpClientImpl.h" diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp index 05f8a5db6..afc5daa33 100644 --- a/src/main/cpp/base/UniqueIdGenerator.cpp +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "UniqueIdGenerator.h" #include "LoggerImpl.h" #include "MixAll.h" diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index 5753edca5..bd1537736 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "UtilAll.h" #include "LoggerImpl.h" diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/base/include/DigestType.h index 5416ffb3c..12ba38c76 100644 --- a/src/main/cpp/base/include/DigestType.h +++ b/src/main/cpp/base/include/DigestType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/base/include/Encoding.h index a4a0efa88..16e7ac58b 100644 --- a/src/main/cpp/base/include/Encoding.h +++ b/src/main/cpp/base/include/Encoding.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h index 6254d767b..50084d7e2 100644 --- a/src/main/cpp/base/include/Histogram.h +++ b/src/main/cpp/base/include/Histogram.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h index 301970ff4..4516a174e 100644 --- a/src/main/cpp/base/include/HostInfo.h +++ b/src/main/cpp/base/include/HostInfo.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h index 6f4cfeede..94a3166bc 100644 --- a/src/main/cpp/base/include/HttpClient.h +++ b/src/main/cpp/base/include/HttpClient.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h index bf93b0c36..d7035d188 100644 --- a/src/main/cpp/base/include/HttpClientImpl.h +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "absl/base/thread_annotations.h" diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index bf6788c27..4cb7e881e 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h index 9e1b3b8ea..f45f36d09 100644 --- a/src/main/cpp/base/include/MessageAccessor.h +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/MQMessageExt.h" diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h index 8572a0793..f03186faa 100644 --- a/src/main/cpp/base/include/MessageImpl.h +++ b/src/main/cpp/base/include/MessageImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index a9dde76c4..199c0722e 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index f201fb545..02b8e63d0 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 8ab32ab12..44a9c9d91 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h index a3026a699..05c2ed60c 100644 --- a/src/main/cpp/base/include/RateLimiter.h +++ b/src/main/cpp/base/include/RateLimiter.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/ThreadPool.h b/src/main/cpp/base/include/ThreadPool.h index 967ad26a2..0dc75b534 100644 --- a/src/main/cpp/base/include/ThreadPool.h +++ b/src/main/cpp/base/include/ThreadPool.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/ThreadPoolImpl.h b/src/main/cpp/base/include/ThreadPoolImpl.h index fad920408..20392398c 100644 --- a/src/main/cpp/base/include/ThreadPoolImpl.h +++ b/src/main/cpp/base/include/ThreadPoolImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h index c013e7af7..52400e6e2 100644 --- a/src/main/cpp/base/include/TopAddressing.h +++ b/src/main/cpp/base/include/TopAddressing.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h index ec6cf50e6..1313b8593 100644 --- a/src/main/cpp/base/include/UniqueIdGenerator.h +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/include/UtilAll.h b/src/main/cpp/base/include/UtilAll.h index 83c2b8baf..6c840bb9f 100644 --- a/src/main/cpp/base/include/UtilAll.h +++ b/src/main/cpp/base/include/UtilAll.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/base/mocks/BUILD.bazel b/src/main/cpp/base/mocks/BUILD.bazel index b50f88c77..be924e72f 100644 --- a/src/main/cpp/base/mocks/BUILD.bazel +++ b/src/main/cpp/base/mocks/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/base/mocks/include/HttpClientMock.h b/src/main/cpp/base/mocks/include/HttpClientMock.h index 6b76cb429..69d59d2e0 100644 --- a/src/main/cpp/base/mocks/include/HttpClientMock.h +++ b/src/main/cpp/base/mocks/include/HttpClientMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "HttpClient.h" diff --git a/src/main/cpp/base/mocks/include/MessageListenerMock.h b/src/main/cpp/base/mocks/include/MessageListenerMock.h index 81da855c9..9c8205367 100644 --- a/src/main/cpp/base/mocks/include/MessageListenerMock.h +++ b/src/main/cpp/base/mocks/include/MessageListenerMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/MessageListener.h" diff --git a/src/main/cpp/client/BUILD.bazel b/src/main/cpp/client/BUILD.bazel index a19626e8b..c56f27931 100644 --- a/src/main/cpp/client/BUILD.bazel +++ b/src/main/cpp/client/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index fe7303d8d..e83d132cb 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientConfigImpl.h" #include diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 2ba6be1e2..3b4100aaf 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientManagerFactory.h" #include "ClientManagerImpl.h" diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index d0143e586..c6c86a6c7 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientManagerImpl.h" #include diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 306a8df41..215b10f8f 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "LogInterceptor.h" #include "InterceptorContinuation.h" #include "absl/container/flat_hash_map.h" diff --git a/src/main/cpp/client/LogInterceptorFactory.cpp b/src/main/cpp/client/LogInterceptorFactory.cpp index 23c8d8388..40bf5658c 100644 --- a/src/main/cpp/client/LogInterceptorFactory.cpp +++ b/src/main/cpp/client/LogInterceptorFactory.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "LogInterceptorFactory.h" #include "LogInterceptor.h" diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index cabced213..650c716c9 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RpcClientImpl.h" #include diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index 3aee816d6..3a4f02f32 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "Signature.h" #include "ClientConfigImpl.h" #include "MetadataConstants.h" diff --git a/src/main/cpp/client/TlsHelper.cpp b/src/main/cpp/client/TlsHelper.cpp index aaa0cd6b8..ac13f856a 100644 --- a/src/main/cpp/client/TlsHelper.cpp +++ b/src/main/cpp/client/TlsHelper.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TlsHelper.h" #include "MixAll.h" diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index 3df672457..e6682cb90 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicAssignmentInfo.h" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp index 657d0863c..5f32d41f9 100644 --- a/src/main/cpp/client/TopicPublishInfo.cpp +++ b/src/main/cpp/client/TopicPublishInfo.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicPublishInfo.h" #include "TopicRouteData.h" diff --git a/src/main/cpp/client/TracingUtility.cpp b/src/main/cpp/client/TracingUtility.cpp index 6f4af461e..c2ccdc2cd 100644 --- a/src/main/cpp/client/TracingUtility.cpp +++ b/src/main/cpp/client/TracingUtility.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifdef ENABLE_TRACING #include "TracingUtility.h" #include diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index 454a61b39..b937118aa 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h index 5464a1d6c..17c96ff62 100644 --- a/src/main/cpp/client/include/Broker.h +++ b/src/main/cpp/client/include/Broker.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index 917115d35..553284edb 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index ec3f529f4..8cff8c462 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index 5a3d30b39..06bf5e477 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 921256492..50ab4acf8 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h index d1baf8548..0a4dbd08e 100644 --- a/src/main/cpp/client/include/ClientManagerFactory.h +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 086a4e1f2..77ec31caf 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h index ae0909f21..8768f12ef 100644 --- a/src/main/cpp/client/include/ConsumeMessageType.h +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h index f105c7066..81d0689b0 100644 --- a/src/main/cpp/client/include/HeartbeatDataCallback.h +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "RpcClient.h" diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h index d7578c93b..b243f83d1 100644 --- a/src/main/cpp/client/include/InterceptorContinuation.h +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "grpcpp/impl/codegen/interceptor.h" diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h index 809a24c9b..6fa9773a3 100644 --- a/src/main/cpp/client/include/LogInterceptor.h +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "grpcpp/impl/codegen/client_interceptor.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/client/include/LogInterceptorFactory.h b/src/main/cpp/client/include/LogInterceptorFactory.h index 85c07191d..533fedf78 100644 --- a/src/main/cpp/client/include/LogInterceptorFactory.h +++ b/src/main/cpp/client/include/LogInterceptorFactory.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "grpcpp/impl/codegen/client_interceptor.h" diff --git a/src/main/cpp/client/include/OpenSSLCompatible.h b/src/main/cpp/client/include/OpenSSLCompatible.h index e64b95b2a..9ca0146ab 100644 --- a/src/main/cpp/client/include/OpenSSLCompatible.h +++ b/src/main/cpp/client/include/OpenSSLCompatible.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once /** diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/src/main/cpp/client/include/OrphanTransactionCallback.h index b320650f8..8a843e0b3 100644 --- a/src/main/cpp/client/include/OrphanTransactionCallback.h +++ b/src/main/cpp/client/include/OrphanTransactionCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/MQMessage.h" diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h index d11018fd6..3502523dc 100644 --- a/src/main/cpp/client/include/Partition.h +++ b/src/main/cpp/client/include/Partition.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "Broker.h" diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h index 6eba0e3f6..631efa31d 100644 --- a/src/main/cpp/client/include/ReceiveMessageCallback.h +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index e53607a45..05a11501d 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index 2b93a08fa..c7add8a72 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index e478ddaa3..15243ed56 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index 9a4c36cbc..9a563e810 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h index 0708fedc3..24a587b47 100644 --- a/src/main/cpp/client/include/ServiceAddress.h +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h index 7d9c0c410..c1e116e6f 100644 --- a/src/main/cpp/client/include/Signature.h +++ b/src/main/cpp/client/include/Signature.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "absl/container/flat_hash_map.h" diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h index b55a982db..c0176c7f0 100644 --- a/src/main/cpp/client/include/TlsHelper.h +++ b/src/main/cpp/client/include/TlsHelper.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h index 20b90e383..9c3ae188d 100644 --- a/src/main/cpp/client/include/Topic.h +++ b/src/main/cpp/client/include/Topic.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h index fdad08e9f..8162e1ded 100644 --- a/src/main/cpp/client/include/TopicAssignmentInfo.h +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h index d4b18ddb9..ad7f1794e 100644 --- a/src/main/cpp/client/include/TopicPublishInfo.h +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/include/TopicRouteChangeCallback.h b/src/main/cpp/client/include/TopicRouteChangeCallback.h index 0a8829e3d..e8adac21b 100644 --- a/src/main/cpp/client/include/TopicRouteChangeCallback.h +++ b/src/main/cpp/client/include/TopicRouteChangeCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "absl/strings/string_view.h" diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h index 265f38270..2d17d906a 100644 --- a/src/main/cpp/client/include/TopicRouteData.h +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/client/mocks/BUILD.bazel b/src/main/cpp/client/mocks/BUILD.bazel index b7ac37138..6da613f6f 100644 --- a/src/main/cpp/client/mocks/BUILD.bazel +++ b/src/main/cpp/client/mocks/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp index 7d85dc92e..a2ad7cf44 100644 --- a/src/main/cpp/client/mocks/RpcClientMock.cpp +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RpcClientMock.h" using namespace testing; diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h index 0e5315eb6..0183d5c14 100644 --- a/src/main/cpp/client/mocks/include/ClientConfigMock.h +++ b/src/main/cpp/client/mocks/include/ClientConfigMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ClientConfig.h" diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 03f827e52..e7c88a8bb 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ClientManager.h" diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h index 176599207..9647f8a26 100644 --- a/src/main/cpp/client/mocks/include/ClientMock.h +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "Client.h" diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h index 76806770f..a7df00e6c 100644 --- a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "gmock/gmock.h" diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index dadafe09d..1ef1e1b4b 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "InvocationContext.h" diff --git a/src/main/cpp/concurrent/BUILD.bazel b/src/main/cpp/concurrent/BUILD.bazel index 7e82ddedf..0501d2005 100644 --- a/src/main/cpp/concurrent/BUILD.bazel +++ b/src/main/cpp/concurrent/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "countdown_latch_library", diff --git a/src/main/cpp/concurrent/CountdownLatch.cpp b/src/main/cpp/concurrent/CountdownLatch.cpp index 6347bb193..8143cb20a 100644 --- a/src/main/cpp/concurrent/CountdownLatch.cpp +++ b/src/main/cpp/concurrent/CountdownLatch.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "CountdownLatch.h" #include "LoggerImpl.h" diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h index 099663093..30263f0c5 100644 --- a/src/main/cpp/concurrent/include/CountdownLatch.h +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/log/BUILD.bazel b/src/main/cpp/log/BUILD.bazel index 6a6208d77..4b7670607 100644 --- a/src/main/cpp/log/BUILD.bazel +++ b/src/main/cpp/log/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "log_library", diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp index a8140f40a..a4241d352 100644 --- a/src/main/cpp/log/LoggerImpl.cpp +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "LoggerImpl.h" #include #include diff --git a/src/main/cpp/log/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h index 2aee66f39..1c337d92b 100644 --- a/src/main/cpp/log/include/LoggerImpl.h +++ b/src/main/cpp/log/include/LoggerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/Logger.h" #include "spdlog/sinks/rotating_file_sink.h" diff --git a/src/main/cpp/remoting/BUILD.bazel b/src/main/cpp/remoting/BUILD.bazel index ae84d43b4..155672b06 100644 --- a/src/main/cpp/remoting/BUILD.bazel +++ b/src/main/cpp/remoting/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/remoting/BrokerData.cpp b/src/main/cpp/remoting/BrokerData.cpp index cadba72b7..5b7762f5a 100644 --- a/src/main/cpp/remoting/BrokerData.cpp +++ b/src/main/cpp/remoting/BrokerData.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "BrokerData.h" #include diff --git a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp index 3a6f03285..055d27cb4 100644 --- a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp +++ b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "QueryRouteRequestHeader.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/remoting/QueueData.cpp b/src/main/cpp/remoting/QueueData.cpp index 31a570269..6f2274030 100644 --- a/src/main/cpp/remoting/QueueData.cpp +++ b/src/main/cpp/remoting/QueueData.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "QueueData.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/remoting/RemotingCommand.cpp b/src/main/cpp/remoting/RemotingCommand.cpp index cbcdf67cf..0019e555e 100644 --- a/src/main/cpp/remoting/RemotingCommand.cpp +++ b/src/main/cpp/remoting/RemotingCommand.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RemotingCommand.h" #include diff --git a/src/main/cpp/remoting/TopicRouteData.cpp b/src/main/cpp/remoting/TopicRouteData.cpp index 44141356d..8b9750dda 100644 --- a/src/main/cpp/remoting/TopicRouteData.cpp +++ b/src/main/cpp/remoting/TopicRouteData.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicRouteData.h" #include "BrokerData.h" #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/remoting/include/BrokerData.h b/src/main/cpp/remoting/include/BrokerData.h index d8d43d078..d522ae71b 100644 --- a/src/main/cpp/remoting/include/BrokerData.h +++ b/src/main/cpp/remoting/include/BrokerData.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/CommandCustomHeader.h b/src/main/cpp/remoting/include/CommandCustomHeader.h index aeabf06f0..441bcd94c 100644 --- a/src/main/cpp/remoting/include/CommandCustomHeader.h +++ b/src/main/cpp/remoting/include/CommandCustomHeader.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "google/protobuf/struct.pb.h" diff --git a/src/main/cpp/remoting/include/LanguageCode.h b/src/main/cpp/remoting/include/LanguageCode.h index cdf8efeac..9204a4da2 100644 --- a/src/main/cpp/remoting/include/LanguageCode.h +++ b/src/main/cpp/remoting/include/LanguageCode.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h index 86fc8917d..0a9682af7 100644 --- a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h +++ b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/QueueData.h b/src/main/cpp/remoting/include/QueueData.h index 6fb5c7f72..4be93bebf 100644 --- a/src/main/cpp/remoting/include/QueueData.h +++ b/src/main/cpp/remoting/include/QueueData.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/RemotingCommand.h b/src/main/cpp/remoting/include/RemotingCommand.h index b7e4cec2b..c4b5ef1a2 100644 --- a/src/main/cpp/remoting/include/RemotingCommand.h +++ b/src/main/cpp/remoting/include/RemotingCommand.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/RemotingCommandType.h b/src/main/cpp/remoting/include/RemotingCommandType.h index 5bd341a90..6e7c4fefc 100644 --- a/src/main/cpp/remoting/include/RemotingCommandType.h +++ b/src/main/cpp/remoting/include/RemotingCommandType.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/RequestCode.h b/src/main/cpp/remoting/include/RequestCode.h index d0a933dc6..58de924ca 100644 --- a/src/main/cpp/remoting/include/RequestCode.h +++ b/src/main/cpp/remoting/include/RequestCode.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/ResponseCode.h b/src/main/cpp/remoting/include/ResponseCode.h index 080f9b3fd..c47014f6e 100644 --- a/src/main/cpp/remoting/include/ResponseCode.h +++ b/src/main/cpp/remoting/include/ResponseCode.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/TopicRouteData.h b/src/main/cpp/remoting/include/TopicRouteData.h index 9e45e3a4e..16cf7c8fd 100644 --- a/src/main/cpp/remoting/include/TopicRouteData.h +++ b/src/main/cpp/remoting/include/TopicRouteData.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/remoting/include/Version.h b/src/main/cpp/remoting/include/Version.h index 5a64a4573..8decd1329 100644 --- a/src/main/cpp/remoting/include/Version.h +++ b/src/main/cpp/remoting/include/Version.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index d4ac095cf..95df783cf 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AsyncReceiveMessageCallback.h" #include diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/rocketmq/AwaitPullCallback.cpp index 6c942a89c..db315f0ab 100644 --- a/src/main/cpp/rocketmq/AwaitPullCallback.cpp +++ b/src/main/cpp/rocketmq/AwaitPullCallback.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "AwaitPullCallback.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index 498fa3987..20cfe8c8e 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 920187cf4..0b48a054e 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 435d8c4ba..3fd79fbd0 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp index 468915d54..993ffafe7 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ConsumeMessageServiceBase.h" #include "LoggerImpl.h" #include "PushConsumer.h" diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 2ba308285..d9b4c0215 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index 0200750c9..41ae320d4 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index bbf6ad86b..ba3581d46 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/DefaultMQProducer.h" #include diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index 8f636259d..f777d774b 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/DefaultMQPullConsumer.h" #include diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index 6cb629137..898368933 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp index 0d591c361..428f6f8bc 100644 --- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "DynamicNameServerResolver.h" #include diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/rocketmq/FilterExpression.cpp index de1f95a65..133eb9830 100644 --- a/src/main/cpp/rocketmq/FilterExpression.cpp +++ b/src/main/cpp/rocketmq/FilterExpression.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "FilterExpression.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp index 8b97e3139..344301d17 100644 --- a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MessageGroupQueueSelector.h" #include diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index e9c1db059..591bd568c 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ProcessQueueImpl.h" #include diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index f37ab5731..0d122493a 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ProducerImpl.h" #include diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index 43f820374..48d4a7bd0 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "PullConsumerImpl.h" #include "ClientManagerFactory.h" #include "InvocationContext.h" diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index a8cbc7bfa..43f53e29e 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "PushConsumerImpl.h" #include diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index ae680e387..596ad01bc 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "SendCallbacks.h" #include "ProducerImpl.h" diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp index 095fd5734..6482c3e4b 100644 --- a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "StaticNameServerResolver.h" #include "absl/strings/str_split.h" diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 75c0c5fa4..52cc6d6c6 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TransactionImpl.h" #include "ProducerImpl.h" #include diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h index 006ba11cd..59fd11539 100644 --- a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index d7596f034..6d79c9a55 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 679f7b37e..f78c922a0 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h index ac062ca61..978849649 100644 --- a/src/main/cpp/rocketmq/include/ClientResourceBundle.h +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/RocketMQ.h" #include diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h index 3ac6b1267..3423ebfc8 100644 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ConsumeMessageServiceBase.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index acf6cfae7..abedc0f9f 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h index 3b883d6dd..821d88753 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h index d7517ed0a..d6b5bfaec 100644 --- a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ConsumeMessageServiceBase.h" diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index ee73e510c..6b3106320 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "absl/container/flat_hash_map.h" diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h index 7626bd14f..b52142cde 100644 --- a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h index 7be3b9c69..77ca68cca 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h index 1553cc978..056b1a324 100644 --- a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h +++ b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "rocketmq/MQSelector.h" diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h index 829d2d21b..52bb345be 100644 --- a/src/main/cpp/rocketmq/include/NameServerResolver.h +++ b/src/main/cpp/rocketmq/include/NameServerResolver.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index a73b67ead..dda6df6a1 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index 018f9be4d..598dcbb8d 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index dea27a288..f4d852534 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index c0091137d..6be279891 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index dd78cec9e..e0092351d 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index e6958106a..cbe95aa74 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index beff4998c..168175504 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h index ab955d295..fed37bcfd 100644 --- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h index 54c24a1ac..dc4c2a370 100644 --- a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "absl/base/thread_annotations.h" diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 8029605d6..37e50dd81 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/rocketmq/mocks/BUILD.bazel b/src/main/cpp/rocketmq/mocks/BUILD.bazel index 541cb9b8c..21262fabc 100644 --- a/src/main/cpp/rocketmq/mocks/BUILD.bazel +++ b/src/main/cpp/rocketmq/mocks/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h index c2645f301..1a1810b46 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ConsumeMessageService.h" diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h index c818458d6..bbd6c3216 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "ClientMock.h" diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h index 89d7c7c8d..005d5e341 100644 --- a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h +++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include "NameServerResolver.h" diff --git a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h index b56b2a93a..56216bcbc 100644 --- a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ProcessQueue.h" #include "gmock/gmock.h" diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 00cd87270..69c517cd4 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/scheduler/BUILD.bazel b/src/main/cpp/scheduler/BUILD.bazel index f0b9158bb..9997c630a 100644 --- a/src/main/cpp/scheduler/BUILD.bazel +++ b/src/main/cpp/scheduler/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "scheduler_library", diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index d92622800..723cfcafc 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "SchedulerImpl.h" #include diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index ab1a8f239..7f86c49cf 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h index fdbe98f04..01c1265a6 100644 --- a/src/main/cpp/scheduler/include/SchedulerImpl.h +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include #include diff --git a/src/main/cpp/tracing/exporters/BUILD.bazel b/src/main/cpp/tracing/exporters/BUILD.bazel index a6687f406..0fee74515 100644 --- a/src/main/cpp/tracing/exporters/BUILD.bazel +++ b/src/main/cpp/tracing/exporters/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "otlp_exporter", diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 40f93d20f..2ea70aa54 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "OtlpExporter.h" #include "InvocationContext.h" #include "MixAll.h" diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index 6a39f96a3..5b40f7f34 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/test/cpp/benchmark/BUILD.bazel b/src/test/cpp/benchmark/BUILD.bazel index e8aa27ac3..c0cabf8f3 100644 --- a/src/test/cpp/benchmark/BUILD.bazel +++ b/src/test/cpp/benchmark/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") cc_test( diff --git a/src/test/cpp/benchmark/BenchmarkTest.cpp b/src/test/cpp/benchmark/BenchmarkTest.cpp index 388a5ef39..fe4e15d34 100644 --- a/src/test/cpp/benchmark/BenchmarkTest.cpp +++ b/src/test/cpp/benchmark/BenchmarkTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "benchmark/benchmark.h" static void BM_StringCreation(benchmark::State& state) { diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel index c2e919089..b4cabee09 100644 --- a/src/test/cpp/it/BUILD.bazel +++ b/src/test/cpp/it/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "it_top_addressing", diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 8b61c2453..051f50d89 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index ed8731adc..85d3954ae 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index c7a2d6045..1f49725dc 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicPublishInfo.h" #include "ClientConfigImpl.h" #include "LogInterceptorFactory.h" diff --git a/src/test/cpp/ut/BUILD.bazel b/src/test/cpp/ut/BUILD.bazel new file mode 100644 index 000000000..2695ae24a --- /dev/null +++ b/src/test/cpp/ut/BUILD.bazel @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp index 684447bdf..8cd005035 100644 --- a/src/test/cpp/ut/admin/AdminServerTest.cpp +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include "AdminServerImpl.h" diff --git a/src/test/cpp/ut/admin/BUILD.bazel b/src/test/cpp/ut/admin/BUILD.bazel index 8a2cc7f86..8fe13fcb6 100644 --- a/src/test/cpp/ut/admin/BUILD.bazel +++ b/src/test/cpp/ut/admin/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( diff --git a/src/test/cpp/ut/api/BUILD.bazel b/src/test/cpp/ut/api/BUILD.bazel index eda5f4bc8..78f575ff8 100644 --- a/src/test/cpp/ut/api/BUILD.bazel +++ b/src/test/cpp/ut/api/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( diff --git a/src/test/cpp/ut/api/ErrorCodeTest.cpp b/src/test/cpp/ut/api/ErrorCodeTest.cpp index 42b525f17..68badd2f9 100644 --- a/src/test/cpp/ut/api/ErrorCodeTest.cpp +++ b/src/test/cpp/ut/api/ErrorCodeTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/ErrorCode.h" #include "gtest/gtest.h" #include diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel index 3e60c186f..092ef7860 100644 --- a/src/test/cpp/ut/base/BUILD.bazel +++ b/src/test/cpp/ut/base/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "histogram_test", diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp index 279c74b48..613e0a215 100644 --- a/src/test/cpp/ut/base/HistogramTest.cpp +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "Histogram.h" #include diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp index fd1b8abbb..d106061d0 100644 --- a/src/test/cpp/ut/base/HostInfoTest.cpp +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "HostInfo.h" #include "fmt/format.h" #include "rocketmq/RocketMQ.h" diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp index ce1290e69..9a5df5642 100644 --- a/src/test/cpp/ut/base/HttpClientTest.cpp +++ b/src/test/cpp/ut/base/HttpClientTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp index 9f94e1e30..25efffd5c 100644 --- a/src/test/cpp/ut/base/InvocationContextTest.cpp +++ b/src/test/cpp/ut/base/InvocationContextTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "InvocationContext.h" #include "RpcClient.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp index aa2d8b1b1..32575a075 100644 --- a/src/test/cpp/ut/base/MQMessageExtTest.cpp +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/MQMessageExt.h" #include "MessageAccessor.h" #include "rocketmq/MQMessage.h" diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp index 1c76a8778..4b018d665 100644 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/MQMessage.h" #include "gtest/gtest.h" #include diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index ee8b281dc..b61f67023 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MixAll.h" #include "rocketmq/MQMessage.h" #include "rocketmq/RocketMQ.h" diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp index bc504b2dc..97726a514 100644 --- a/src/test/cpp/ut/base/RateLimiterTest.cpp +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RateLimiter.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp index 64d9f5e5c..9be1ed7a3 100644 --- a/src/test/cpp/ut/base/ThreadPoolTest.cpp +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ThreadPoolImpl.h" #include "absl/memory/memory.h" #include "absl/synchronization/mutex.h" diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp index 1e4d86906..a2a5652c3 100644 --- a/src/test/cpp/ut/base/TopAddressingTest.cpp +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp index 58dee2442..dbafdeaeb 100644 --- a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "UniqueIdGenerator.h" #include "absl/container/flat_hash_set.h" #include "rocketmq/RocketMQ.h" diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index 5c7354466..a142ddea7 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "UtilAll.h" #include "MixAll.h" #include "absl/strings/str_split.h" diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel index 4d8eeb920..b0757cb35 100644 --- a/src/test/cpp/ut/client/BUILD.bazel +++ b/src/test/cpp/ut/client/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "tls_helper_test", diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index 5e936360f..7c182eda8 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientManagerFactory.h" #include "ClientConfigMock.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index f1c1e2ab4..4dc8c5dc0 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientManagerImpl.h" #include "ReceiveMessageCallbackMock.h" #include "RpcClientMock.h" diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/test/cpp/ut/client/ClientTest.cpp index 098789f4d..d67bec23e 100644 --- a/src/test/cpp/ut/client/ClientTest.cpp +++ b/src/test/cpp/ut/client/ClientTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientMock.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp index b4d841d4f..77d9a8c6a 100644 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RpcClientMock.h" #include "google/rpc/code.pb.h" diff --git a/src/test/cpp/ut/client/TlsHelperTest.cpp b/src/test/cpp/ut/client/TlsHelperTest.cpp index 845f84116..ea834eef6 100644 --- a/src/test/cpp/ut/client/TlsHelperTest.cpp +++ b/src/test/cpp/ut/client/TlsHelperTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TlsHelper.h" #include #include diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp index 521ea64bb..ee8ea7eb0 100644 --- a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp +++ b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicAssignmentInfo.h" #include "rocketmq/ConsumeType.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp index de46b79b9..b47bb0c4c 100644 --- a/src/test/cpp/ut/client/TracingUtilityTest.cpp +++ b/src/test/cpp/ut/client/TracingUtilityTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TracingUtility.h" #include "gtest/gtest.h" #include diff --git a/src/test/cpp/ut/concurrent/BUILD.bazel b/src/test/cpp/ut/concurrent/BUILD.bazel index 993fe1f84..00e4ad82d 100644 --- a/src/test/cpp/ut/concurrent/BUILD.bazel +++ b/src/test/cpp/ut/concurrent/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "countdown_latch_test", diff --git a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp index 4b9d47eb8..6545b0a68 100644 --- a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp +++ b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "CountdownLatch.h" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" diff --git a/src/test/cpp/ut/grpc/BUILD.bazel b/src/test/cpp/ut/grpc/BUILD.bazel index 6194131f6..1383d8874 100644 --- a/src/test/cpp/ut/grpc/BUILD.bazel +++ b/src/test/cpp/ut/grpc/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "grpc_thread_pool_test", diff --git a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp index 1d5ff7128..815dfef37 100644 --- a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp +++ b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "absl/memory/memory.h" #include "absl/synchronization/mutex.h" #include "rocketmq/RocketMQ.h" diff --git a/src/test/cpp/ut/grpc/GrpcTimer.cpp b/src/test/cpp/ut/grpc/GrpcTimer.cpp index ebb9ca01b..7be1ce751 100644 --- a/src/test/cpp/ut/grpc/GrpcTimer.cpp +++ b/src/test/cpp/ut/grpc/GrpcTimer.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "absl/time/time.h" #include "grpc/grpc.h" diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/test/cpp/ut/logger/BUILD.bazel index 1f092b6ec..7d51c3c12 100644 --- a/src/test/cpp/ut/logger/BUILD.bazel +++ b/src/test/cpp/ut/logger/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp index e48b7cb15..bfc474b5a 100644 --- a/src/test/cpp/ut/logger/LoggerTest.cpp +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/Logger.h" #include "LoggerImpl.h" #include "rocketmq/RocketMQ.h" diff --git a/src/test/cpp/ut/remoting/BUILD.bazel b/src/test/cpp/ut/remoting/BUILD.bazel index c0dacd1af..68201979e 100644 --- a/src/test/cpp/ut/remoting/BUILD.bazel +++ b/src/test/cpp/ut/remoting/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( diff --git a/src/test/cpp/ut/remoting/BrokerDataTest.cpp b/src/test/cpp/ut/remoting/BrokerDataTest.cpp index 168203c3e..03d7fee4c 100644 --- a/src/test/cpp/ut/remoting/BrokerDataTest.cpp +++ b/src/test/cpp/ut/remoting/BrokerDataTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "BrokerData.h" #include diff --git a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp index b80a77aef..81ed4ddb9 100644 --- a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp +++ b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "QueryRouteRequestHeader.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/remoting/QueueDataTest.cpp b/src/test/cpp/ut/remoting/QueueDataTest.cpp index 936a68896..dbf8de993 100644 --- a/src/test/cpp/ut/remoting/QueueDataTest.cpp +++ b/src/test/cpp/ut/remoting/QueueDataTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "gtest/gtest.h" #include "QueueData.h" diff --git a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp index 088fa8774..7a76a0686 100644 --- a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp +++ b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "RemotingCommand.h" #include diff --git a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp index 3a11d87c2..f7f9deef8 100644 --- a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp +++ b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "TopicRouteData.h" #include "rocketmq/RocketMQ.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp index b2862714d..3c7800c4d 100644 --- a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp +++ b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index b1fce27cf..0fcfa1bb1 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test", "cc_library") cc_test( diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 6ac3a010f..58d5027a8 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp index 5215ddb79..a139e4fd8 100644 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ConsumeStandardMessageService.h" #include "MessageListenerMock.h" #include "ProcessQueueMock.h" diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp index c2d5b727b..a8d6d5b09 100644 --- a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ConsumerMock.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 859d6f54b..6d23bf81d 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/CredentialsProvider.h" #include "MixAll.h" #include "ghc/filesystem.hpp" diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 81b07b1dd..a5247ac81 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/DefaultMQProducer.h" #include diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 32f6cef19..3b8c793ca 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/DefaultMQPushConsumer.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp index 3aef9dfdd..40c559af4 100644 --- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "DynamicNameServerResolver.h" #include diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp index b64eaa9c1..baf885216 100644 --- a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/RocketMQ.h" #include "rocketmq/State.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp index 5685c3eca..d193944f0 100644 --- a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp +++ b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "FilterExpression.h" #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp index 102992465..09f0533cb 100644 --- a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "MessageGroupQueueSelector.h" #include "rocketmq/MQMessage.h" #include "rocketmq/MQMessageQueue.h" diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp index 4fc598cb3..ed871a2ef 100644 --- a/src/test/cpp/ut/rocketmq/MessageTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "rocketmq/MQMessageExt.h" #include #include diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index 98c9707b0..dea0f5aba 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index eb775148c..5322f66e5 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 6c5ae4172..9c0f045e9 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 4bacd982c..7a25655cf 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include "gtest/gtest.h" diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp index a66005d03..5504e642e 100644 --- a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp +++ b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "SendCallbacks.h" #include "gtest/gtest.h" #include diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp index 6f7ce3448..21f20b3d1 100644 --- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "StaticNameServerResolver.h" #include "absl/strings/str_split.h" diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp index 0daec1e6f..2e0d35634 100644 --- a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "StsCredentialsProviderImpl.h" #include "HttpClientMock.h" #include "absl/memory/memory.h" diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 05e554ded..93064cb7c 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once #include diff --git a/src/test/cpp/ut/scheduler/BUILD.bazel b/src/test/cpp/ut/scheduler/BUILD.bazel index d1e0c56ea..23615c556 100644 --- a/src/test/cpp/ut/scheduler/BUILD.bazel +++ b/src/test/cpp/ut/scheduler/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( name = "scheduler_test", diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 827f0140d..2dfafa743 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include #include diff --git a/src/test/cpp/ut/tracing/exporters/BUILD.bazel b/src/test/cpp/ut/tracing/exporters/BUILD.bazel index 8646cc806..1fc34774a 100644 --- a/src/test/cpp/ut/tracing/exporters/BUILD.bazel +++ b/src/test/cpp/ut/tracing/exporters/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp index 7cc649d00..d0e4ec440 100644 --- a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "ClientConfigMock.h" #include "ClientManagerMock.h" #include "OtlpExporter.h" diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp index 0b248248f..97303029f 100644 --- a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "opencensus/trace/propagation/trace_context.h" #include "gtest/gtest.h" diff --git a/third_party/BUILD b/third_party/BUILD deleted file mode 100644 index e69de29bb..000000000 diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel new file mode 100644 index 000000000..28e0699af --- /dev/null +++ b/third_party/BUILD.bazel @@ -0,0 +1,16 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/tools/fix_copyright.py b/tools/fix_copyright.py index 263fe814c..24bb8f59c 100755 --- a/tools/fix_copyright.py +++ b/tools/fix_copyright.py @@ -2,36 +2,57 @@ import os import fnmatch -def checkAndFixFileHeaderComment(fileName, template): +def checkAndFixFileHeaderComment(fileName, template, bazel=False): lines = [] - with open(fileName, 'rw') as f: + with open(fileName, 'r') as f: lines = f.readlines() - - print(template) - for line in lines: - print(line) + if len(lines) > 0: + if bazel: + if lines[0].startswith("#"): + return False + else: + if lines[0].startswith("/*"): + return False + + lines.insert(0, template) + + with open(fileName, 'w') as f: + for line in lines: + f.write(line) + return True def main(): - template = "/*\n" - + bzl_template = "#" + os.linesep + template = "/*" + os.linesep with open('tools/copyright.txt', 'r') as reader: line = reader.readline() while line != '': template += " * " + line + bzl_template += "# " + line line = reader.readline() - template += " */" + template += " */" + os.linesep + bzl_template += "#" + os.linesep + cnt = 0 for root, dir, files in os.walk(os.curdir): if ".git/" in root: continue for item in fnmatch.filter(files, "*.h"): - checkAndFixFileHeaderComment(root + os.sep + item, template) + if(checkAndFixFileHeaderComment(root + os.sep + item, template)): + cnt = cnt +1 for item in fnmatch.filter(files, "*.cpp"): - checkAndFixFileHeaderComment(root + os.sep + item, template) + if (checkAndFixFileHeaderComment(root + os.sep + item, template)): + cnt = cnt + 1 + + for item in fnmatch.filter(files, "*.bazel"): + if (checkAndFixFileHeaderComment(root + os.sep + item, bzl_template, bazel=True)): + cnt = cnt + 1 + + print("{} files fixed".format(cnt)) if __name__ == "__main__": From 111ddaf9ae52394e64437655d91a5c3f9c7e3b3e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 04:13:58 +0000 Subject: [PATCH 102/133] Remove CMake script files --- CMakeLists.txt | 167 --------------------- api/CMakeLists.txt | 4 - example/CMakeLists.txt | 1 - example/rocketmq/CMakeLists.txt | 65 -------- src/main/cpp/CMakeLists.txt | 72 --------- src/main/cpp/admin/CMakeLists.txt | 44 ------ src/main/cpp/base/CMakeLists.txt | 57 ------- src/main/cpp/base/mocks/CMakeLists.txt | 6 - src/main/cpp/client/CMakeLists.txt | 63 -------- src/main/cpp/client/mocks/CMakeLists.txt | 9 -- src/main/cpp/concurrent/CMakeLists.txt | 13 -- src/main/cpp/log/CMakeLists.txt | 24 --- src/main/cpp/rocketmq/CMakeLists.txt | 42 ------ src/main/cpp/rocketmq/mocks/CMakeLists.txt | 8 - src/main/cpp/scheduler/CMakeLists.txt | 24 --- src/test/cpp/it/CMakeLists.txt | 38 ----- src/test/cpp/ut/CMakeLists.txt | 8 - src/test/cpp/ut/admin/CMakeLists.txt | 6 - src/test/cpp/ut/base/CMakeLists.txt | 39 ----- src/test/cpp/ut/client/CMakeLists.txt | 29 ---- src/test/cpp/ut/concurrent/CMakeLists.txt | 7 - src/test/cpp/ut/grpc/CMakeLists.txt | 7 - src/test/cpp/ut/logger/CMakeLists.txt | 5 - src/test/cpp/ut/rocketmq/CMakeLists.txt | 86 ----------- src/test/cpp/ut/scheduler/CMakeLists.txt | 8 - 25 files changed, 832 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 api/CMakeLists.txt delete mode 100644 example/CMakeLists.txt delete mode 100644 example/rocketmq/CMakeLists.txt delete mode 100644 src/main/cpp/CMakeLists.txt delete mode 100644 src/main/cpp/admin/CMakeLists.txt delete mode 100644 src/main/cpp/base/CMakeLists.txt delete mode 100644 src/main/cpp/base/mocks/CMakeLists.txt delete mode 100644 src/main/cpp/client/CMakeLists.txt delete mode 100644 src/main/cpp/client/mocks/CMakeLists.txt delete mode 100644 src/main/cpp/concurrent/CMakeLists.txt delete mode 100644 src/main/cpp/log/CMakeLists.txt delete mode 100644 src/main/cpp/rocketmq/CMakeLists.txt delete mode 100644 src/main/cpp/rocketmq/mocks/CMakeLists.txt delete mode 100644 src/main/cpp/scheduler/CMakeLists.txt delete mode 100644 src/test/cpp/it/CMakeLists.txt delete mode 100644 src/test/cpp/ut/CMakeLists.txt delete mode 100644 src/test/cpp/ut/admin/CMakeLists.txt delete mode 100644 src/test/cpp/ut/base/CMakeLists.txt delete mode 100644 src/test/cpp/ut/client/CMakeLists.txt delete mode 100644 src/test/cpp/ut/concurrent/CMakeLists.txt delete mode 100644 src/test/cpp/ut/grpc/CMakeLists.txt delete mode 100644 src/test/cpp/ut/logger/CMakeLists.txt delete mode 100644 src/test/cpp/ut/rocketmq/CMakeLists.txt delete mode 100644 src/test/cpp/ut/scheduler/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index fa08c2eb8..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,167 +0,0 @@ -cmake_minimum_required(VERSION 3.13 FATAL_ERROR) -project(rocketmq VERSION 1.0.0 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_VERBOSE_MAKEFILE ON) -set(VERSION "1.0.0") - -include(FetchContent) - -find_package(Threads) - -FetchContent_Declare( - gRPC - GIT_REPOSITORY https://gitee.com/lizhanhui/grpc.git - GIT_TAG v1.38.0-p2 -) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.10.x -) -FetchContent_Declare( - spdlog - GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG v1.8.1 -) - -FetchContent_Declare( - fmt - GIT_REPOSITORY https://github.com/fmtlib/fmt.git - GIT_TAG 7.0.2 -) - -FetchContent_Declare( - ghc - GIT_REPOSITORY https://github.com/gulrak/filesystem.git - GIT_TAG v1.3.2 -) - -FetchContent_Declare( - opentelemetry - GIT_REPOSITORY https://github.com/open-telemetry/opentelemetry-cpp.git - GIT_TAG v1.0.0-rc4 -) - -FetchContent_Declare( - ThreadPool - GIT_REPOSITORY https://github.com/lizhanhui/ThreadPool.git - GIT_TAG v1.1 -) - -set(FETCHCONTENT_QUIET OFF) -option(INSTALL_GTEST OFF) -set(BUILD_TESTING OFF) -set(ABSL_RUN_TESTS OFF) -set(SPDLOG_FMT_EXTERNAL ON) -if (BUILD_TESTS) - FetchContent_MakeAvailable(googletest) -endif () - -FetchContent_MakeAvailable(fmt gRPC spdlog ghc ThreadPool opentelemetry) - -option(ENABLE_TRACING OFF) -if (ENABLE_TRACING) - FetchContent_MakeAvailable(opentelemetry) -endif () - -# Since FetchContent uses add_subdirectory under the hood, we can use -# the grpc targets directly from this build. -set(_PROTOBUF_LIBPROTOBUF libprotobuf) -set(_REFLECTION grpc++_reflection) -set(_PROTOBUF_PROTOC $) -set(_GRPC_GRPCPP grpc++) -if(CMAKE_CROSSCOMPILING) - message("CMake cross compiling") - find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) -else() - set(_GRPC_CPP_PLUGIN_EXECUTABLE $) - message("CMake non-cross compiling: ${_GRPC_CPP_PLUGIN_EXECUTABLE}") -endif() - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/proto) -file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/proto/dummy.cc) -file(GLOB PROTO_FILES - "${CMAKE_CURRENT_SOURCE_DIR}/proto/apache/rocketmq/v1/*.proto" - "${CMAKE_CURRENT_SOURCE_DIR}/proto/google/rpc/*.proto") -foreach(proto_file ${PROTO_FILES}) - get_filename_component(proto_file_base_name ${proto_file} NAME_WE) - message("Proto file base file name: ${proto_file_base_name}, abs: ${proto_file}") - set(proto_src_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.pb.cc") - set(proto_hdr_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.pb.h") - set(grpc_src_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.grpc.pb.cc") - set(grpc_hdr_file "${CMAKE_BINARY_DIR}/proto/apache/rocketmq/v1/${proto_file_base_name}.grpc.pb.h") - - add_custom_command( - OUTPUT "${proto_src_file}" "${proto_hdr_file}" "${grpc_src_file}" "${grpc_hdr_file}" - COMMAND ${_PROTOBUF_PROTOC} - ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}/proto" - --cpp_out "${CMAKE_BINARY_DIR}/proto" - -I "${CMAKE_SOURCE_DIR}/proto" - --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" - "${proto_file}" - DEPENDS "${proto_file}" - COMMENT "Protoc to generate gRPC stub files" - ) - - add_custom_target(${proto_file_base_name} - ALL - DEPENDS ${proto_src_file} ${proto_hdr_file} ${grpc_src_file} ${grpc_hdr_file} - COMMENT "Custom target" - VERBATIM) -endforeach() - -file(GLOB_RECURSE STUB_FILES ${CMAKE_BINARY_DIR}/proto/*.cc) -add_library(mq_proto_library STATIC ${STUB_FILES}) -target_include_directories(mq_proto_library PUBLIC ${CMAKE_BINARY_DIR}/proto) -target_link_libraries(mq_proto_library - PUBLIC - ${_REFLECTION} - ${_GRPC_GRPCPP} - ${_PROTOBUF_LIBPROTOBUF}) - -enable_testing() - -option(ENABLE_TLS ON) -if (ENABLE_TLS) - message("TLS enabled") - find_package(OpenSSL REQUIRED) - add_compile_definitions(TLS_ENABLED=ON) -endif () - -add_subdirectory(api) -add_subdirectory(src/main/cpp) - -if (BUILD_TESTS) - add_subdirectory(src/test/cpp/ut) -endif() - -if (BUILD_INTEGRATION_TESTS) - add_subdirectory(src/test/cpp/it) -endif () - -if (BUILD_EXAMPLES) - add_subdirectory(example) -endif () - -include(GNUInstallDirs) -install(DIRECTORY api/rocketmq - DESTINATION include) -install(TARGETS rocketmq_static rocketmq_shared - LIBRARY DESTINATION lib) -install(FILES - example/rocketmq/ExampleProducer.cpp - example/rocketmq/ExamplePushConsumer.cpp - DESTINATION example) - -set(CPACK_PACKAGE_VERSION ${VERSION}) -set(CPACK_GENERATOR "RPM") -set(CPACK_PACKAGE_NAME "rocketmq-cpp") -set(CPACK_PACKAGE_RELEASE 1) -set(CPACK_PACKAGE_CONTACT "Zhanhui Li") -set(CPACK_PACKAGE_VENDOR "Alibaba") -set(CPACK_PACKAGING_INSTALL_PREFIX "/home/admin") -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}") -include(CPack) \ No newline at end of file diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt deleted file mode 100644 index b41166c59..000000000 --- a/api/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(rocketmq_interface INTERFACE) -target_include_directories(rocketmq_interface - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt deleted file mode 100644 index ee3f37f78..000000000 --- a/example/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(rocketmq) \ No newline at end of file diff --git a/example/rocketmq/CMakeLists.txt b/example/rocketmq/CMakeLists.txt deleted file mode 100644 index c4f9af5d7..000000000 --- a/example/rocketmq/CMakeLists.txt +++ /dev/null @@ -1,65 +0,0 @@ -add_executable(example_producer ExampleProducer.cpp) -target_link_libraries(example_producer - PRIVATE - CURL::libcurl - rocketmq - $<$:-static-libgcc> - $<$:-static-libstdc++>) - -add_executable(example_async_producer ExampleAsyncProducer.cpp) -target_link_libraries(example_async_producer - PRIVATE - CURL::libcurl - rocketmq - $<$:-static-libgcc> - $<$:-static-libstdc++>) - - -add_executable(example_push_consumer ExamplePushConsumer.cpp) -target_link_libraries(example_push_consumer - PRIVATE - CURL::libcurl - rocketmq - spdlog::spdlog_header_only - $<$:-static-libgcc> - $<$:-static-libstdc++>) - - -add_executable(example_broadcast_push_consumer ExampleBroadcastPushConsumer.cpp) -target_link_libraries(example_broadcast_push_consumer - PRIVATE - CURL::libcurl - rocketmq - spdlog::spdlog_header_only - $<$:-static-libgcc> - $<$:-static-libstdc++>) - -add_executable(benchmark_push_consumer BenchmarkPushConsumer.cpp) -target_link_libraries(benchmark_push_consumer - CURL::libcurl - rocketmq) - -add_executable(sql_producer SqlProducer.cpp) -target_link_libraries(sql_producer - CURL::libcurl - rocketmq) - -add_executable(sql_consumer SqlConsumer.cpp) -target_link_libraries(sql_consumer - CURL::libcurl - rocketmq) - -add_executable(consumer_with_custom_executor PushConsumerWithCustomExecutor.cpp) -target_link_libraries(consumer_with_custom_executor - CURL::libcurl - rocketmq) - -add_executable(consumer_with_throttle PushConsumerWithThrottle.cpp) -target_link_libraries(consumer_with_throttle - CURL::libcurl - rocketmq) - -add_executable(pull_consumer ExamplePullConsumer.cpp) -target_link_libraries(pull_consumer - CURL::libcurl - rocketmq) \ No newline at end of file diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt deleted file mode 100644 index df43e76b8..000000000 --- a/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,72 +0,0 @@ -add_subdirectory(base) -add_subdirectory(log) -add_subdirectory(admin) -add_subdirectory(concurrent) -add_subdirectory(scheduler) -add_subdirectory(client) -add_subdirectory(rocketmq) - -add_library(rocketmq_static - STATIC - $ - $ - $ - $ - $ - $ - $) -target_link_libraries(rocketmq_static - PUBLIC - absl::flat_hash_set - absl::flat_hash_map - absl::random_random - absl::synchronization - absl::strings - fmt - mq_proto_library - re2::re2 - rocketmq_interface - uv_a - zlib) -if (ENABLE_TRACING) - target_link_libraries(rocketmq_static - PUBLIC - opentelemetry_trace - opentelemetry_exporter_otprotocol) -endif () - -add_library(rocketmq_shared - SHARED - $ - $ - $ - $ - $ - $ - $) -target_link_libraries(rocketmq_shared - PUBLIC - absl::flat_hash_set - absl::flat_hash_map - absl::random_random - absl::synchronization - absl::strings - fmt - mq_proto_library - re2::re2 - rocketmq_interface - uv_a - zlib) -if (ENABLE_TRACING) - target_link_libraries(rocketmq_shared - PUBLIC - opentelemetry_trace - opentelemetry_exporter_otprotocol) -endif () - -set_target_properties(rocketmq_static rocketmq_shared - PROPERTIES - ARCHIVE_OUTPUT_NAME rocketmq - LIBRARY_OUTPUT_NAME rocketmq) - -add_library(rocketmq ALIAS rocketmq_static) \ No newline at end of file diff --git a/src/main/cpp/admin/CMakeLists.txt b/src/main/cpp/admin/CMakeLists.txt deleted file mode 100644 index c05093fbf..000000000 --- a/src/main/cpp/admin/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -add_library(admin_obj OBJECT) - -target_include_directories(admin_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -target_sources(admin_obj - PRIVATE - AdminServiceImpl.cpp - AdminServerImpl.cpp - AdminFacade.cpp) - -target_link_libraries(admin_obj - PUBLIC - rocketmq_interface - spdlog::spdlog_header_only - mq_proto_library - grpc++) - -add_library(admin_library - STATIC - $) -target_include_directories(admin_library - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(admin_library - PUBLIC - mq_proto_library - grpc++ - rocketmq_interface - spdlog::spdlog_header_only) - - -add_executable(admin_client AdminClient.cpp) -target_include_directories(admin_client - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(admin_client - PRIVATE - grpc++ - mq_proto_library - rocketmq_interface - $<$:-static-libgcc> - $<$:-static-libstdc++>) \ No newline at end of file diff --git a/src/main/cpp/base/CMakeLists.txt b/src/main/cpp/base/CMakeLists.txt deleted file mode 100644 index 6a1f7495b..000000000 --- a/src/main/cpp/base/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -add_library(base_obj OBJECT) -target_sources(base_obj - PRIVATE - GHttpClient.cpp - HostInfo.cpp - MessageAccessor.cpp - MetadataConstants.cpp - MixAll.cpp - MQMessage.cpp - MQMessageExt.cpp - Protocol.cpp - RateLimiter.cpp - TopAddressing.cpp - UniqueIdGenerator.cpp - UtilAll.cpp) -target_include_directories(base_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(base_obj - PUBLIC - absl::flat_hash_set - absl::flat_hash_map - absl::random_random - absl::synchronization - absl::strings - absl::time - crypto - fmt - grpc++ - logger_library - ssl - re2::re2 - uv_a - zlib) - -add_library(base_library - STATIC - $) -target_include_directories(base_library - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(base_library - PUBLIC - absl::flat_hash_set - absl::flat_hash_map - absl::random_random - absl::synchronization - absl::strings - fmt - grpc++ - logger_library - re2::re2 - rocketmq_interface - spdlog::spdlog_header_only - uv_a - zlib) -add_subdirectory(mocks) \ No newline at end of file diff --git a/src/main/cpp/base/mocks/CMakeLists.txt b/src/main/cpp/base/mocks/CMakeLists.txt deleted file mode 100644 index 8ca2461df..000000000 --- a/src/main/cpp/base/mocks/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(base_mocks INTERFACE) -target_include_directories(base_mocks INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(base_mocks - INTERFACE - base_library - gmock) \ No newline at end of file diff --git a/src/main/cpp/client/CMakeLists.txt b/src/main/cpp/client/CMakeLists.txt deleted file mode 100644 index f01219b72..000000000 --- a/src/main/cpp/client/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -add_library(client_obj OBJECT) -target_sources(client_obj - PRIVATE - ClientConfigImpl.cpp - ClientManagerImpl.cpp - ClientManagerFactory.cpp - LogInterceptorFactory.cpp - LogInterceptor.cpp - TopicAssignmentInfo.cpp - RpcClientImpl.cpp - Signature.cpp - TlsHelper.cpp - TopicPublishInfo.cpp - TracingUtility.cpp) -target_include_directories(client_obj - PUBLIC - ${CMAKE_BINARY_DIR}/generated/third_party/opentelemetry-proto - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(client_obj - PUBLIC - admin_library - crc_library - crypto - scheduler_library - ssl - ThreadPool - PRIVATE - gtest) -if (ENABLE_TRACING) - target_link_libraries(client_obj - PUBLIC - opentelemetry_trace - opentelemetry_exporter_otprotocol) -endif () - -add_library(client_library - STATIC - $) - -target_include_directories(client_library - PUBLIC - ${CMAKE_BINARY_DIR}/generated/third_party/opentelemetry-proto - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -target_link_libraries(client_library - PUBLIC - admin_library - crc_library - crypto - mq_proto_library - scheduler_library - ssl - ThreadPool) -if (ENABLE_TRACING) - target_link_libraries(client_library - PUBLIC - opentelemetry_trace - opentelemetry_exporter_otprotocol) -endif () - -if (BUILD_TESTS) - add_subdirectory(mocks) -endif() diff --git a/src/main/cpp/client/mocks/CMakeLists.txt b/src/main/cpp/client/mocks/CMakeLists.txt deleted file mode 100644 index 02113513a..000000000 --- a/src/main/cpp/client/mocks/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -add_library(client_mocks RpcClientMock.cpp) -target_include_directories(client_mocks - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(client_mocks - PUBLIC - gmock - client_library - mq_proto_library) \ No newline at end of file diff --git a/src/main/cpp/concurrent/CMakeLists.txt b/src/main/cpp/concurrent/CMakeLists.txt deleted file mode 100644 index d2c11baaf..000000000 --- a/src/main/cpp/concurrent/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_library(countdown_latch_obj OBJECT) -target_sources(countdown_latch_obj PRIVATE CountdownLatch.cpp) -target_include_directories(countdown_latch_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(countdown_latch_obj - PUBLIC absl::strings - PUBLIC absl::base - PUBLIC absl::synchronization - PUBLIC base_library - PRIVATE spdlog::spdlog_header_only) - -add_library(countdown_latch_library STATIC $) \ No newline at end of file diff --git a/src/main/cpp/log/CMakeLists.txt b/src/main/cpp/log/CMakeLists.txt deleted file mode 100644 index 95351ceaa..000000000 --- a/src/main/cpp/log/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -add_library(logger_obj OBJECT) -target_sources(logger_obj - PRIVATE - LoggerImpl.cpp) -target_link_libraries(logger_obj - PUBLIC - rocketmq_interface - ghc_filesystem - spdlog::spdlog_header_only) -target_include_directories(logger_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -add_library(logger_library - STATIC - $) -target_include_directories(logger_library - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(logger_library - PUBLIC - rocketmq_interface - ghc_filesystem - spdlog::spdlog_header_only) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/CMakeLists.txt b/src/main/cpp/rocketmq/CMakeLists.txt deleted file mode 100644 index e451fa1b1..000000000 --- a/src/main/cpp/rocketmq/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -add_library(rocketmq_obj OBJECT) -target_sources(rocketmq_obj - PRIVATE - AsyncReceiveMessageCallback.cpp - SendCallbacks.cpp - AwaitPullCallback.cpp - ClientImpl.cpp - ConsumeMessageService.cpp - ConsumeStandardMessageService.cpp - ConsumeFifoMessageService.cpp - CredentialsProvider.cpp - DefaultMQProducer.cpp - ProducerImpl.cpp - DefaultMQPushConsumer.cpp - PushConsumerImpl.cpp - DefaultMQPullConsumer.cpp - PullConsumerImpl.cpp - FilterExpression.cpp - MessageGroupQueueSelector.cpp - ProcessQueueImpl.cpp - TransactionImpl.cpp) -target_include_directories(rocketmq_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(rocketmq_obj - PUBLIC - client_library - logger_library - thread_pool) - -add_library(rocketmq_library - STATIC - $) -target_include_directories(rocketmq_library - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(rocketmq_library - PUBLIC - client_library - logger_library) - -add_subdirectory(mocks) \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/CMakeLists.txt b/src/main/cpp/rocketmq/mocks/CMakeLists.txt deleted file mode 100644 index 3374fd42b..000000000 --- a/src/main/cpp/rocketmq/mocks/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_library(rocketmq_mocks INTERFACE) -target_include_directories(rocketmq_mocks - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(rocketmq_mocks - INTERFACE - rocketmq_library - gmock) \ No newline at end of file diff --git a/src/main/cpp/scheduler/CMakeLists.txt b/src/main/cpp/scheduler/CMakeLists.txt deleted file mode 100644 index d3bf794f6..000000000 --- a/src/main/cpp/scheduler/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -add_library(scheduler_obj OBJECT) -target_sources(scheduler_obj - PRIVATE - Scheduler.cpp) -target_include_directories(scheduler_obj - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(scheduler_obj - PUBLIC - base_library - countdown_latch_library - thread_pool) - -add_library(scheduler_library - STATIC - $) -target_include_directories(scheduler_library - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(scheduler_library - PUBLIC - base_library - countdown_latch_library - thread_pool) \ No newline at end of file diff --git a/src/test/cpp/it/CMakeLists.txt b/src/test/cpp/it/CMakeLists.txt deleted file mode 100644 index 9d48d3a9c..000000000 --- a/src/test/cpp/it/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -add_executable(client_instance_test ClientInstanceTest.cpp) -target_link_libraries(client_instance_test - PRIVATE - client_library - gtest_main) -add_test(it_client_instance client_instance_test) - -add_executable(rpc_client_test RpcClientTest.cpp) -target_link_libraries(rpc_client_test - PRIVATE - client_library - credentials_observable_mock - rpc_client_mock - gtest_main) -add_test(it_rpc_client_2 rpc_client_test) - -add_executable(top_addressing_test TopAddressingTest.cpp) -target_link_libraries(top_addressing_test - PRIVATE - base_library - gtest_main) -add_test(it_top_addressing top_addressing_test) - - -add_executable(topic_publish_info_test TopicPublishInfoTest.cpp) -target_link_libraries(topic_publish_info_test - PRIVATE - client_library - credentials_observable_mock - gtest_main) -add_test(it_topic_publish_info topic_publish_info_test) - -add_executable(default_push_consumer_test DefaultMQPushConsumerIntegrationTest.cpp) -target_link_libraries(default_push_consumer_test - PRIVATE - rocketmq_library - gtest_main) -add_test(it_default_push_consumer default_push_consumer_test) \ No newline at end of file diff --git a/src/test/cpp/ut/CMakeLists.txt b/src/test/cpp/ut/CMakeLists.txt deleted file mode 100644 index b5efbd2a1..000000000 --- a/src/test/cpp/ut/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_subdirectory(base) -add_subdirectory(concurrent) -add_subdirectory(scheduler) -add_subdirectory(client) -add_subdirectory(grpc) -add_subdirectory(rocketmq) -add_subdirectory(logger) -add_subdirectory(admin) \ No newline at end of file diff --git a/src/test/cpp/ut/admin/CMakeLists.txt b/src/test/cpp/ut/admin/CMakeLists.txt deleted file mode 100644 index d746bef03..000000000 --- a/src/test/cpp/ut/admin/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_executable(admin_server_test AdminServerTest.cpp) -target_link_libraries(admin_server_test - PRIVATE - gtest_main - admin_library - ) \ No newline at end of file diff --git a/src/test/cpp/ut/base/CMakeLists.txt b/src/test/cpp/ut/base/CMakeLists.txt deleted file mode 100644 index b175c241a..000000000 --- a/src/test/cpp/ut/base/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -add_executable(mix_all_test MixAllTest.cpp) -target_link_libraries(mix_all_test - PRIVATE - base_library - gtest_main) - -add_executable(util_all_test UtilAllTest.cpp) -target_link_libraries(util_all_test - PRIVATE - base_library - spdlog::spdlog - absl::base - gtest_main) - -add_executable(rate_limiter_test RateLimiterTest.cpp) -target_link_libraries(rate_limiter_test PRIVATE - gtest_main - base_library) - -add_executable(histogram_test HistogramTest.cpp) -target_link_libraries(histogram_test - PRIVATE - gtest_main - base_library) -add_test(test_histogram histogram_test) - - -add_executable(unique_id_generator_test UniqueIdGeneratorTest.cpp) -target_link_libraries(unique_id_generator_test - PRIVATE - gtest_main - base_library) -add_test(ut_unique_id_generator unique_id_generator_test) - -add_executable(ghttp_client_test GHttpClientTest.cpp) -target_link_libraries(ghttp_client_test - PRIVATE - base_library - gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/client/CMakeLists.txt b/src/test/cpp/ut/client/CMakeLists.txt deleted file mode 100644 index 4851bfd66..000000000 --- a/src/test/cpp/ut/client/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -if (ENABLE_TRACING) -# add_executable(tracing_utility_test TracingUtilityTest.cpp) -# target_link_libraries(tracing_utility_test -# PRIVATE -# client_library -# gtest_main) -# add_test(ut_tracing_utility_test tracing_utility_test) -endif () - -add_executable(tls_helper_test TlsHelperTest.cpp) -target_link_libraries(tls_helper_test - PRIVATE - client_library - gtest_main) - - -add_executable(rpc_client_unit_test RpcClientTest.cpp) -target_link_libraries(rpc_client_unit_test - PRIVATE - client_mocks - client_library - gtest_main) -add_test(ut_rpc_client rpc_client_unit_test) - -add_executable(client_manager_test ClientManagerTest.cpp) -target_link_libraries(client_manager_test - PRIVATE - client_mocks - gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/CMakeLists.txt b/src/test/cpp/ut/concurrent/CMakeLists.txt deleted file mode 100644 index d8960f064..000000000 --- a/src/test/cpp/ut/concurrent/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(countdown_latch_test CountdownLatchTest.cpp) -target_link_libraries(countdown_latch_test - PRIVATE - gtest_main - countdown_latch_library - spdlog) -add_test(ut_countdown_latch_test countdown_latch_test) \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/CMakeLists.txt b/src/test/cpp/ut/grpc/CMakeLists.txt deleted file mode 100644 index b1fb84776..000000000 --- a/src/test/cpp/ut/grpc/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(grpc_thread_pool_test GrpcThreadPoolTest.cpp) -target_link_libraries(grpc_thread_pool_test - PRIVATE - grpc - base_library - gtest_main) -add_test(ut_grpc_thread_pool grpc_thread_pool_test) \ No newline at end of file diff --git a/src/test/cpp/ut/logger/CMakeLists.txt b/src/test/cpp/ut/logger/CMakeLists.txt deleted file mode 100644 index 00b056d27..000000000 --- a/src/test/cpp/ut/logger/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(logger_test LoggerTest.cpp) -target_link_libraries(logger_test - PRIVATE gtest_main - PRIVATE logger_library - PRIVATE spdlog::spdlog) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CMakeLists.txt b/src/test/cpp/ut/rocketmq/CMakeLists.txt deleted file mode 100644 index 5c0c44cab..000000000 --- a/src/test/cpp/ut/rocketmq/CMakeLists.txt +++ /dev/null @@ -1,86 +0,0 @@ -add_library(client_test_interface INTERFACE) -target_include_directories(client_test_interface - INTERFACE - ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_link_libraries(client_test_interface - INTERFACE - rpc_client_mock - gtest_main) - -add_executable(default_mq_producer_test DefaultMQProducerTest.cpp) -target_link_libraries(default_mq_producer_test - PRIVATE - client_test_interface - rocketmq_library) -add_test(ut_default_mq_producer_test default_mq_producer_test) - -add_executable(executor_test ExecutorTest.cpp) -target_link_libraries(executor_test - PRIVATE - rocketmq_interface - gtest_main) - -add_executable(message_test MessageTest.cpp) -target_link_libraries(message_test - PRIVATE - rocketmq_library - gtest_main) - -add_executable(process_queue_test ProcessQueueTest.cpp) -target_link_libraries(process_queue_test - PRIVATE - gtest_main - base_mocks - client_mocks - rocketmq_mocks - rocketmq_library) - -add_executable(ut_credentials_provider CredentialsProviderTest.cpp) -target_link_libraries(ut_credentials_provider - gtest_main - rocketmq_library) - -add_executable(ut_send_callbacks SendCallbacksTest.cpp) -target_link_libraries(ut_send_callbacks - PRIVATE - gtest_main - rocketmq_library) - -add_executable(consume_message_service ConsumeStandardMessageServiceTest.cpp) -target_link_libraries(consume_message_service - PRIVATE - base_mocks - gtest_main - rocketmq_library - rocketmq_mocks) - -add_executable(filter_expression_test FilterExpressionTest.cpp) -target_link_libraries(filter_expression_test - PRIVATE - gtest_main - rocketmq_library) - -add_executable(message_group_queue_selector_test MessageGroupQueueSelectorTest.cpp) -target_link_libraries(message_group_queue_selector_test - PRIVATE - gtest_main - rocketmq_library) - -add_executable(sts_credentials_provider_test StsCredentialsProviderImplTest.cpp) -target_link_libraries(sts_credentials_provider_test - PRIVATE - base_mocks - gtest_main - rocketmq_library) - -add_executable(consumer_test ConsumerTest.cpp) -target_link_libraries(consumer_test - PRIVATE - rocketmq_mocks - gtest_main) - -add_executable(await_pull_callback_test AwaitPullCallbackTest.cpp) -target_link_libraries(await_pull_callback_test - PRIVATE - rocketmq_library - gtest_main) \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/CMakeLists.txt b/src/test/cpp/ut/scheduler/CMakeLists.txt deleted file mode 100644 index 34d6064da..000000000 --- a/src/test/cpp/ut/scheduler/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_executable(scheduler_test SchedulerTest.cpp) -target_link_libraries(scheduler_test - PRIVATE - absl::synchronization - base_library - gtest_main - scheduler_library) -add_test(ut_scheduler_test scheduler_test) \ No newline at end of file From 7cc2efc63b9946a2ef0b568ebe134be392a0e89e Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 13:30:39 +0800 Subject: [PATCH 103/133] Format all source files --- api/rocketmq/AdminServer.h | 4 ++-- api/rocketmq/AsyncCallback.h | 4 ++-- api/rocketmq/ConsumeType.h | 4 ++-- api/rocketmq/Credentials.h | 4 ++-- api/rocketmq/CredentialsProvider.h | 4 ++-- api/rocketmq/DefaultMQProducer.h | 4 ++-- api/rocketmq/DefaultMQPullConsumer.h | 4 ++-- api/rocketmq/DefaultMQPushConsumer.h | 4 ++-- api/rocketmq/ErrorCategory.h | 4 ++-- api/rocketmq/ErrorCode.h | 4 ++-- api/rocketmq/Executor.h | 4 ++-- api/rocketmq/ExpressionType.h | 4 ++-- api/rocketmq/LocalTransactionStateChecker.h | 4 ++-- api/rocketmq/Logger.h | 4 ++-- api/rocketmq/MQClientException.h | 4 ++-- api/rocketmq/MQMessage.h | 4 ++-- api/rocketmq/MQMessageExt.h | 4 ++-- api/rocketmq/MQMessageQueue.h | 4 ++-- api/rocketmq/MQSelector.h | 4 ++-- api/rocketmq/MessageListener.h | 4 ++-- api/rocketmq/MessageModel.h | 4 ++-- api/rocketmq/MessageType.h | 4 ++-- api/rocketmq/PullResult.h | 4 ++-- api/rocketmq/RocketMQ.h | 4 ++-- api/rocketmq/SendResult.h | 4 ++-- api/rocketmq/State.h | 4 ++-- api/rocketmq/Transaction.h | 4 ++-- example/rocketmq/BenchmarkPushConsumer.cpp | 9 +++++---- example/rocketmq/ExampleAsyncProducer.cpp | 9 +++++---- example/rocketmq/ExampleBroadcastPushConsumer.cpp | 9 +++++---- example/rocketmq/ExampleProducer.cpp | 9 +++++---- example/rocketmq/ExamplePullConsumer.cpp | 9 +++++---- example/rocketmq/ExamplePushConsumer.cpp | 9 +++++---- example/rocketmq/ExampleTransactionProducer.cpp | 9 +++++---- .../rocketmq/PushConsumerWithCustomExecutor.cpp | 11 ++++++----- example/rocketmq/PushConsumerWithThrottle.cpp | 11 ++++++----- example/rocketmq/SqlConsumer.cpp | 9 +++++---- example/rocketmq/SqlProducer.cpp | 9 +++++---- src/main/cpp/admin/AdminClient.cpp | 4 ++-- src/main/cpp/admin/AdminFacade.cpp | 4 ++-- src/main/cpp/admin/AdminServerImpl.cpp | 4 ++-- src/main/cpp/admin/AdminServiceImpl.cpp | 4 ++-- src/main/cpp/admin/include/AdminClient.h | 4 ++-- src/main/cpp/admin/include/AdminServerImpl.h | 4 ++-- src/main/cpp/admin/include/AdminServiceImpl.h | 4 ++-- src/main/cpp/admin/include/ServerCall.h | 4 ++-- src/main/cpp/base/ErrorCategory.cpp | 4 ++-- src/main/cpp/base/ErrorCode.cpp | 4 ++-- src/main/cpp/base/HostInfo.cpp | 4 ++-- src/main/cpp/base/HttpClientImpl.cpp | 4 ++-- src/main/cpp/base/MQMessage.cpp | 4 ++-- src/main/cpp/base/MQMessageExt.cpp | 4 ++-- src/main/cpp/base/MessageAccessor.cpp | 4 ++-- src/main/cpp/base/MetadataConstants.cpp | 4 ++-- src/main/cpp/base/MixAll.cpp | 4 ++-- src/main/cpp/base/Protocol.cpp | 4 ++-- src/main/cpp/base/RateLimiter.cpp | 4 ++-- src/main/cpp/base/ThreadPoolImpl.cpp | 4 ++-- src/main/cpp/base/TopAddressing.cpp | 4 ++-- src/main/cpp/base/UniqueIdGenerator.cpp | 4 ++-- src/main/cpp/base/UtilAll.cpp | 4 ++-- src/main/cpp/base/include/DigestType.h | 4 ++-- src/main/cpp/base/include/Encoding.h | 4 ++-- src/main/cpp/base/include/Histogram.h | 4 ++-- src/main/cpp/base/include/HostInfo.h | 4 ++-- src/main/cpp/base/include/HttpClient.h | 4 ++-- src/main/cpp/base/include/HttpClientImpl.h | 4 ++-- src/main/cpp/base/include/InvocationContext.h | 4 ++-- src/main/cpp/base/include/MessageAccessor.h | 4 ++-- src/main/cpp/base/include/MessageImpl.h | 4 ++-- src/main/cpp/base/include/MetadataConstants.h | 4 ++-- src/main/cpp/base/include/MixAll.h | 4 ++-- src/main/cpp/base/include/Protocol.h | 4 ++-- src/main/cpp/base/include/RateLimiter.h | 4 ++-- src/main/cpp/base/include/ThreadPool.h | 4 ++-- src/main/cpp/base/include/ThreadPoolImpl.h | 4 ++-- src/main/cpp/base/include/TopAddressing.h | 4 ++-- src/main/cpp/base/include/UniqueIdGenerator.h | 4 ++-- src/main/cpp/base/include/UtilAll.h | 4 ++-- src/main/cpp/base/mocks/include/HttpClientMock.h | 4 ++-- .../cpp/base/mocks/include/MessageListenerMock.h | 4 ++-- src/main/cpp/client/ClientConfigImpl.cpp | 4 ++-- src/main/cpp/client/ClientManagerFactory.cpp | 4 ++-- src/main/cpp/client/ClientManagerImpl.cpp | 4 ++-- src/main/cpp/client/LogInterceptor.cpp | 4 ++-- src/main/cpp/client/LogInterceptorFactory.cpp | 4 ++-- src/main/cpp/client/RpcClientImpl.cpp | 4 ++-- src/main/cpp/client/Signature.cpp | 4 ++-- src/main/cpp/client/TlsHelper.cpp | 4 ++-- src/main/cpp/client/TopicAssignmentInfo.cpp | 6 +++--- src/main/cpp/client/TopicPublishInfo.cpp | 4 ++-- src/main/cpp/client/TracingUtility.cpp | 4 ++-- src/main/cpp/client/include/Assignment.h | 4 ++-- src/main/cpp/client/include/Broker.h | 4 ++-- src/main/cpp/client/include/Client.h | 11 +++++------ src/main/cpp/client/include/ClientConfig.h | 4 ++-- src/main/cpp/client/include/ClientConfigImpl.h | 4 ++-- src/main/cpp/client/include/ClientManager.h | 12 +++++------- src/main/cpp/client/include/ClientManagerFactory.h | 4 ++-- src/main/cpp/client/include/ClientManagerImpl.h | 4 ++-- src/main/cpp/client/include/ConsumeMessageType.h | 4 ++-- .../cpp/client/include/HeartbeatDataCallback.h | 4 ++-- .../cpp/client/include/InterceptorContinuation.h | 4 ++-- src/main/cpp/client/include/LogInterceptor.h | 4 ++-- .../cpp/client/include/LogInterceptorFactory.h | 4 ++-- src/main/cpp/client/include/OpenSSLCompatible.h | 4 ++-- .../cpp/client/include/OrphanTransactionCallback.h | 4 ++-- src/main/cpp/client/include/Partition.h | 4 ++-- .../cpp/client/include/ReceiveMessageCallback.h | 4 ++-- src/main/cpp/client/include/ReceiveMessageResult.h | 4 ++-- src/main/cpp/client/include/RpcClient.h | 4 ++-- src/main/cpp/client/include/RpcClientImpl.h | 4 ++-- src/main/cpp/client/include/SendMessageContext.h | 4 ++-- src/main/cpp/client/include/ServiceAddress.h | 4 ++-- src/main/cpp/client/include/Signature.h | 4 ++-- src/main/cpp/client/include/TlsHelper.h | 4 ++-- src/main/cpp/client/include/Topic.h | 4 ++-- src/main/cpp/client/include/TopicAssignmentInfo.h | 4 ++-- src/main/cpp/client/include/TopicPublishInfo.h | 4 ++-- .../cpp/client/include/TopicRouteChangeCallback.h | 4 ++-- src/main/cpp/client/include/TopicRouteData.h | 4 ++-- src/main/cpp/client/mocks/RpcClientMock.cpp | 4 ++-- .../cpp/client/mocks/include/ClientConfigMock.h | 4 ++-- .../cpp/client/mocks/include/ClientManagerMock.h | 4 ++-- src/main/cpp/client/mocks/include/ClientMock.h | 4 ++-- .../mocks/include/ReceiveMessageCallbackMock.h | 4 ++-- src/main/cpp/client/mocks/include/RpcClientMock.h | 4 ++-- src/main/cpp/concurrent/CountdownLatch.cpp | 4 ++-- src/main/cpp/concurrent/include/CountdownLatch.h | 4 ++-- src/main/cpp/log/LoggerImpl.cpp | 4 ++-- src/main/cpp/log/include/LoggerImpl.h | 4 ++-- src/main/cpp/remoting/BrokerData.cpp | 4 ++-- src/main/cpp/remoting/QueryRouteRequestHeader.cpp | 4 ++-- src/main/cpp/remoting/QueueData.cpp | 4 ++-- src/main/cpp/remoting/RemotingCommand.cpp | 4 ++-- src/main/cpp/remoting/TopicRouteData.cpp | 4 ++-- src/main/cpp/remoting/include/BrokerData.h | 4 ++-- .../cpp/remoting/include/CommandCustomHeader.h | 4 ++-- src/main/cpp/remoting/include/LanguageCode.h | 4 ++-- .../cpp/remoting/include/QueryRouteRequestHeader.h | 4 ++-- src/main/cpp/remoting/include/QueueData.h | 4 ++-- src/main/cpp/remoting/include/RemotingCommand.h | 4 ++-- .../cpp/remoting/include/RemotingCommandType.h | 4 ++-- src/main/cpp/remoting/include/RequestCode.h | 6 +++--- src/main/cpp/remoting/include/ResponseCode.h | 4 ++-- src/main/cpp/remoting/include/TopicRouteData.h | 5 ++--- src/main/cpp/remoting/include/Version.h | 4 ++-- .../cpp/rocketmq/AsyncReceiveMessageCallback.cpp | 4 ++-- src/main/cpp/rocketmq/AwaitPullCallback.cpp | 4 ++-- src/main/cpp/rocketmq/ClientImpl.cpp | 4 ++-- .../cpp/rocketmq/ConsumeFifoMessageService.cpp | 4 ++-- .../cpp/rocketmq/ConsumeMessageServiceBase.cpp | 4 ++-- .../cpp/rocketmq/ConsumeStandardMessageService.cpp | 4 ++-- src/main/cpp/rocketmq/CredentialsProvider.cpp | 4 ++-- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 4 ++-- src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp | 4 ++-- src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp | 4 ++-- .../cpp/rocketmq/DynamicNameServerResolver.cpp | 4 ++-- src/main/cpp/rocketmq/FilterExpression.cpp | 4 ++-- .../cpp/rocketmq/MessageGroupQueueSelector.cpp | 4 ++-- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 4 ++-- src/main/cpp/rocketmq/ProducerImpl.cpp | 4 ++-- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 4 ++-- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 4 ++-- src/main/cpp/rocketmq/SendCallbacks.cpp | 4 ++-- src/main/cpp/rocketmq/StaticNameServerResolver.cpp | 4 ++-- src/main/cpp/rocketmq/TransactionImpl.cpp | 4 ++-- .../rocketmq/include/AsyncReceiveMessageCallback.h | 14 ++++++-------- src/main/cpp/rocketmq/include/AwaitPullCallback.h | 4 ++-- src/main/cpp/rocketmq/include/ClientImpl.h | 4 ++-- .../cpp/rocketmq/include/ClientResourceBundle.h | 4 ++-- .../rocketmq/include/ConsumeFifoMessageService.h | 4 ++-- .../cpp/rocketmq/include/ConsumeMessageService.h | 4 ++-- .../rocketmq/include/ConsumeMessageServiceBase.h | 4 ++-- .../include/ConsumeStandardMessageService.h | 4 ++-- src/main/cpp/rocketmq/include/Consumer.h | 4 ++-- .../rocketmq/include/DynamicNameServerResolver.h | 4 ++-- src/main/cpp/rocketmq/include/FilterExpression.h | 4 ++-- .../rocketmq/include/MessageGroupQueueSelector.h | 4 ++-- src/main/cpp/rocketmq/include/NameServerResolver.h | 4 ++-- src/main/cpp/rocketmq/include/ProcessQueue.h | 4 ++-- src/main/cpp/rocketmq/include/ProcessQueueImpl.h | 4 ++-- src/main/cpp/rocketmq/include/ProducerImpl.h | 4 ++-- src/main/cpp/rocketmq/include/PullConsumerImpl.h | 4 ++-- src/main/cpp/rocketmq/include/PushConsumer.h | 4 ++-- src/main/cpp/rocketmq/include/PushConsumerImpl.h | 4 ++-- src/main/cpp/rocketmq/include/SendCallbacks.h | 4 ++-- .../rocketmq/include/StaticNameServerResolver.h | 4 ++-- .../rocketmq/include/StsCredentialsProviderImpl.h | 4 ++-- src/main/cpp/rocketmq/include/TransactionImpl.h | 4 ++-- .../mocks/include/ConsumeMessageServiceMock.h | 4 ++-- src/main/cpp/rocketmq/mocks/include/ConsumerMock.h | 4 ++-- .../mocks/include/NameServerResolverMock.h | 4 ++-- .../cpp/rocketmq/mocks/include/ProcessQueueMock.h | 4 ++-- .../cpp/rocketmq/mocks/include/PushConsumerMock.h | 4 ++-- src/main/cpp/scheduler/SchedulerImpl.cpp | 4 ++-- src/main/cpp/scheduler/include/Scheduler.h | 4 ++-- src/main/cpp/scheduler/include/SchedulerImpl.h | 4 ++-- src/main/cpp/tracing/exporters/OtlpExporter.cpp | 4 ++-- .../cpp/tracing/exporters/include/OtlpExporter.h | 4 ++-- src/test/cpp/benchmark/BenchmarkTest.cpp | 4 ++-- src/test/cpp/it/RpcClientTest.cpp | 4 ++-- src/test/cpp/it/TopAddressingTest.cpp | 4 ++-- src/test/cpp/it/TopicPublishInfoTest.cpp | 4 ++-- src/test/cpp/ut/admin/AdminServerTest.cpp | 4 ++-- src/test/cpp/ut/api/ErrorCodeTest.cpp | 4 ++-- src/test/cpp/ut/base/HistogramTest.cpp | 4 ++-- src/test/cpp/ut/base/HostInfoTest.cpp | 4 ++-- src/test/cpp/ut/base/HttpClientTest.cpp | 4 ++-- src/test/cpp/ut/base/InvocationContextTest.cpp | 4 ++-- src/test/cpp/ut/base/MQMessageExtTest.cpp | 4 ++-- src/test/cpp/ut/base/MQMessageTest.cpp | 4 ++-- src/test/cpp/ut/base/MixAllTest.cpp | 4 ++-- src/test/cpp/ut/base/RateLimiterTest.cpp | 4 ++-- src/test/cpp/ut/base/ThreadPoolTest.cpp | 4 ++-- src/test/cpp/ut/base/TopAddressingTest.cpp | 4 ++-- src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp | 4 ++-- src/test/cpp/ut/base/UtilAllTest.cpp | 4 ++-- .../cpp/ut/client/ClientManagerFactoryTest.cpp | 4 ++-- src/test/cpp/ut/client/ClientManagerTest.cpp | 4 ++-- src/test/cpp/ut/client/ClientTest.cpp | 4 ++-- src/test/cpp/ut/client/RpcClientTest.cpp | 4 ++-- src/test/cpp/ut/client/TlsHelperTest.cpp | 4 ++-- src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp | 4 ++-- src/test/cpp/ut/client/TracingUtilityTest.cpp | 4 ++-- src/test/cpp/ut/concurrent/CountdownLatchTest.cpp | 4 ++-- src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp | 4 ++-- src/test/cpp/ut/grpc/GrpcTimer.cpp | 4 ++-- src/test/cpp/ut/logger/LoggerTest.cpp | 4 ++-- src/test/cpp/ut/remoting/BrokerDataTest.cpp | 4 ++-- .../ut/remoting/QueryRouteRequestHeaderTest.cpp | 4 ++-- src/test/cpp/ut/remoting/QueueDataTest.cpp | 4 ++-- src/test/cpp/ut/remoting/RemotingCommandTest.cpp | 4 ++-- src/test/cpp/ut/remoting/TopicRouteDataTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 4 ++-- .../rocketmq/ConsumeStandardMessageServiceTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/ConsumerTest.cpp | 4 ++-- .../cpp/ut/rocketmq/CredentialsProviderTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 4 ++-- .../cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp | 4 ++-- .../ut/rocketmq/DynamicNameServerResolverTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp | 4 ++-- .../ut/rocketmq/MessageGroupQueueSelectorTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/MessageTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp | 4 ++-- .../ut/rocketmq/StaticNameServerResolverTest.cpp | 4 ++-- .../ut/rocketmq/StsCredentialsProviderImplTest.cpp | 4 ++-- src/test/cpp/ut/rocketmq/include/MQClientTest.h | 4 ++-- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 4 ++-- .../cpp/ut/tracing/exporters/OtlpExportersTest.cpp | 4 ++-- .../cpp/ut/tracing/exporters/SpanContextTest.cpp | 4 ++-- tools/format.sh | 0 258 files changed, 561 insertions(+), 556 deletions(-) mode change 100644 => 100755 tools/format.sh diff --git a/api/rocketmq/AdminServer.h b/api/rocketmq/AdminServer.h index 11b92590c..3876a2ca0 100644 --- a/api/rocketmq/AdminServer.h +++ b/api/rocketmq/AdminServer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h index 0e7add77c..8004ba027 100644 --- a/api/rocketmq/AsyncCallback.h +++ b/api/rocketmq/AsyncCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/ConsumeType.h b/api/rocketmq/ConsumeType.h index 9c11a144a..4cbf5a2fa 100644 --- a/api/rocketmq/ConsumeType.h +++ b/api/rocketmq/ConsumeType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/Credentials.h b/api/rocketmq/Credentials.h index 27c895d60..1bd5dbcfc 100644 --- a/api/rocketmq/Credentials.h +++ b/api/rocketmq/Credentials.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/CredentialsProvider.h b/api/rocketmq/CredentialsProvider.h index ffed50855..595746f1c 100644 --- a/api/rocketmq/CredentialsProvider.h +++ b/api/rocketmq/CredentialsProvider.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h index 8c80f9280..97306d538 100644 --- a/api/rocketmq/DefaultMQProducer.h +++ b/api/rocketmq/DefaultMQProducer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h index 959acd0f8..9bbfc6031 100644 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ b/api/rocketmq/DefaultMQPullConsumer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index ced50bc21..03ac7d124 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/ErrorCategory.h b/api/rocketmq/ErrorCategory.h index 432e66073..075266b86 100644 --- a/api/rocketmq/ErrorCategory.h +++ b/api/rocketmq/ErrorCategory.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index 1e0e261bd..c3ebb7a39 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/Executor.h b/api/rocketmq/Executor.h index a7321b5cd..bbac1d0eb 100644 --- a/api/rocketmq/Executor.h +++ b/api/rocketmq/Executor.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/ExpressionType.h b/api/rocketmq/ExpressionType.h index 9418fea93..6c2848fe5 100644 --- a/api/rocketmq/ExpressionType.h +++ b/api/rocketmq/ExpressionType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/api/rocketmq/LocalTransactionStateChecker.h index 78110bcdd..59d788120 100644 --- a/api/rocketmq/LocalTransactionStateChecker.h +++ b/api/rocketmq/LocalTransactionStateChecker.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/Logger.h b/api/rocketmq/Logger.h index 86e20d749..7518d19f7 100644 --- a/api/rocketmq/Logger.h +++ b/api/rocketmq/Logger.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MQClientException.h b/api/rocketmq/MQClientException.h index 115f5abdd..f74c45f3c 100644 --- a/api/rocketmq/MQClientException.h +++ b/api/rocketmq/MQClientException.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index da9b66fb8..a4e642999 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h index 688ef2561..ed95484fc 100644 --- a/api/rocketmq/MQMessageExt.h +++ b/api/rocketmq/MQMessageExt.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h index 9e701b7a6..ec62b3d8f 100644 --- a/api/rocketmq/MQMessageQueue.h +++ b/api/rocketmq/MQMessageQueue.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MQSelector.h b/api/rocketmq/MQSelector.h index edf5b7c33..0b31e55d8 100644 --- a/api/rocketmq/MQSelector.h +++ b/api/rocketmq/MQSelector.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h index 03a963db3..5fdf59d2b 100644 --- a/api/rocketmq/MessageListener.h +++ b/api/rocketmq/MessageListener.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/MessageModel.h index 74cdfa362..2422d32b9 100644 --- a/api/rocketmq/MessageModel.h +++ b/api/rocketmq/MessageModel.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/MessageType.h index e4cc46203..8d4ea261b 100644 --- a/api/rocketmq/MessageType.h +++ b/api/rocketmq/MessageType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h index ebc544841..2e91ec4c8 100644 --- a/api/rocketmq/PullResult.h +++ b/api/rocketmq/PullResult.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/RocketMQ.h b/api/rocketmq/RocketMQ.h index 3b114e6bc..bc8e10ba8 100644 --- a/api/rocketmq/RocketMQ.h +++ b/api/rocketmq/RocketMQ.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h index c7d430740..b491cc629 100644 --- a/api/rocketmq/SendResult.h +++ b/api/rocketmq/SendResult.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/State.h b/api/rocketmq/State.h index 93a5061ea..8c8ca529b 100644 --- a/api/rocketmq/State.h +++ b/api/rocketmq/State.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index 867a21037..9a135a68f 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp index ab74e09c6..5d4d3a4eb 100644 --- a/example/rocketmq/BenchmarkPushConsumer.cpp +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQPushConsumer.h" + */ +#include "rocketmq/DefaultMQPushConsumer.h" #include #include @@ -21,7 +22,7 @@ #include #include -using namespace rocketmq; + using namespace rocketmq; class CounterMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index 1c624cd99..b4d4bd453 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQProducer.h" + */ +#include "rocketmq/DefaultMQProducer.h" #include "rocketmq/ErrorCode.h" #include #include @@ -24,7 +25,7 @@ #include #include -using namespace rocketmq; + using namespace rocketmq; int getAndReset(std::atomic_int& counter) { int current; diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 2afda66bf..4302608da 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQPushConsumer.h" + */ +#include "rocketmq/DefaultMQPushConsumer.h" #include "rocketmq/Logger.h" #include "spdlog/spdlog.h" @@ -23,7 +24,7 @@ #include #include -using namespace rocketmq; + using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 7e15364e0..a74ea9faf 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -5,21 +5,22 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQProducer.h" + */ +#include "rocketmq/DefaultMQProducer.h" #include #include #include #include -using namespace rocketmq; + using namespace rocketmq; const std::string& alphaNumeric() { static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp index 2b87b101c..694956c28 100644 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -5,22 +5,23 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include + */ +#include #include #include "rocketmq/CredentialsProvider.h" #include "rocketmq/DefaultMQPullConsumer.h" #include "rocketmq/Logger.h" -int main(int argc, char* argv[]) { + int main(int argc, char* argv[]) { const char* group = "GID_group003"; const char* topic = "yc001"; const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 5dc0df344..6114a724d 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include + */ +#include #include #include #include @@ -24,7 +25,7 @@ #include "rocketmq/DefaultMQPushConsumer.h" -using namespace rocketmq; + using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/example/rocketmq/ExampleTransactionProducer.cpp index bc125ae2e..3858174f9 100644 --- a/example/rocketmq/ExampleTransactionProducer.cpp +++ b/example/rocketmq/ExampleTransactionProducer.cpp @@ -5,18 +5,19 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQProducer.h" + */ +#include "rocketmq/DefaultMQProducer.h" #include -using namespace ROCKETMQ_NAMESPACE; + using namespace ROCKETMQ_NAMESPACE; int main(int argc, char* argv[]) { DefaultMQProducer producer("TestGroup"); diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp index 9d6e8a976..72212f155 100644 --- a/example/rocketmq/PushConsumerWithCustomExecutor.cpp +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQPushConsumer.h" + */ +#include "rocketmq/DefaultMQPushConsumer.h" #include "rocketmq/State.h" #include #include @@ -22,9 +23,9 @@ #include #include -ROCKETMQ_NAMESPACE_BEGIN + ROCKETMQ_NAMESPACE_BEGIN -class ExecutorImpl { + class ExecutorImpl { public: ExecutorImpl() : state_(State::CREATED) { } diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp index d7b24cd82..29cbd9a63 100644 --- a/example/rocketmq/PushConsumerWithThrottle.cpp +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -5,15 +5,16 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQPushConsumer.h" + */ +#include "rocketmq/DefaultMQPushConsumer.h" #include #include @@ -21,9 +22,9 @@ #include #include -ROCKETMQ_NAMESPACE_BEGIN + ROCKETMQ_NAMESPACE_BEGIN -class CounterMessageListener : public StandardMessageListener { + class CounterMessageListener : public StandardMessageListener { public: explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { } diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp index a4a739d0f..11c16efa1 100644 --- a/example/rocketmq/SqlConsumer.cpp +++ b/example/rocketmq/SqlConsumer.cpp @@ -5,22 +5,23 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQPushConsumer.h" + */ +#include "rocketmq/DefaultMQPushConsumer.h" #include #include #include #include -using namespace rocketmq; + using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/SqlProducer.cpp b/example/rocketmq/SqlProducer.cpp index 02a020990..20900f975 100644 --- a/example/rocketmq/SqlProducer.cpp +++ b/example/rocketmq/SqlProducer.cpp @@ -5,18 +5,19 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */#include "rocketmq/DefaultMQProducer.h" + */ +#include "rocketmq/DefaultMQProducer.h" #include -using namespace rocketmq; + using namespace rocketmq; int main(int argc, char* argv[]) { diff --git a/src/main/cpp/admin/AdminClient.cpp b/src/main/cpp/admin/AdminClient.cpp index e7f260c59..8a20d649a 100644 --- a/src/main/cpp/admin/AdminClient.cpp +++ b/src/main/cpp/admin/AdminClient.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/AdminFacade.cpp b/src/main/cpp/admin/AdminFacade.cpp index a17479c57..a224abe75 100644 --- a/src/main/cpp/admin/AdminFacade.cpp +++ b/src/main/cpp/admin/AdminFacade.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/AdminServerImpl.cpp b/src/main/cpp/admin/AdminServerImpl.cpp index a41c520f1..7382b9719 100644 --- a/src/main/cpp/admin/AdminServerImpl.cpp +++ b/src/main/cpp/admin/AdminServerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/AdminServiceImpl.cpp b/src/main/cpp/admin/AdminServiceImpl.cpp index f7ab19fc8..3a4d0fa35 100644 --- a/src/main/cpp/admin/AdminServiceImpl.cpp +++ b/src/main/cpp/admin/AdminServiceImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h index da99e5876..968d837d7 100644 --- a/src/main/cpp/admin/include/AdminClient.h +++ b/src/main/cpp/admin/include/AdminClient.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h index c396a1387..b6056352a 100644 --- a/src/main/cpp/admin/include/AdminServerImpl.h +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/include/AdminServiceImpl.h b/src/main/cpp/admin/include/AdminServiceImpl.h index 78c398042..58933abd9 100644 --- a/src/main/cpp/admin/include/AdminServiceImpl.h +++ b/src/main/cpp/admin/include/AdminServiceImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h index 0c20ca784..138d43341 100644 --- a/src/main/cpp/admin/include/ServerCall.h +++ b/src/main/cpp/admin/include/ServerCall.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp index 9110f5986..166f632e1 100644 --- a/src/main/cpp/base/ErrorCategory.cpp +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/ErrorCode.cpp b/src/main/cpp/base/ErrorCode.cpp index 7d8be7d72..59c1aec41 100644 --- a/src/main/cpp/base/ErrorCode.cpp +++ b/src/main/cpp/base/ErrorCode.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/HostInfo.cpp b/src/main/cpp/base/HostInfo.cpp index 160e0b019..8b57247b3 100644 --- a/src/main/cpp/base/HostInfo.cpp +++ b/src/main/cpp/base/HostInfo.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/HttpClientImpl.cpp b/src/main/cpp/base/HttpClientImpl.cpp index 2bf8a5fa8..cb25ae96f 100644 --- a/src/main/cpp/base/HttpClientImpl.cpp +++ b/src/main/cpp/base/HttpClientImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index 9af46ec58..c5d096e63 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp index ed9009c2e..21a947614 100644 --- a/src/main/cpp/base/MQMessageExt.cpp +++ b/src/main/cpp/base/MQMessageExt.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp index 1a2e0c0e1..957f07652 100644 --- a/src/main/cpp/base/MessageAccessor.cpp +++ b/src/main/cpp/base/MessageAccessor.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp index 39022d9c0..0aa8d933d 100644 --- a/src/main/cpp/base/MetadataConstants.cpp +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index b69c56696..9a86e3380 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/Protocol.cpp b/src/main/cpp/base/Protocol.cpp index d82ebba63..be031e324 100644 --- a/src/main/cpp/base/Protocol.cpp +++ b/src/main/cpp/base/Protocol.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/RateLimiter.cpp b/src/main/cpp/base/RateLimiter.cpp index a0b48fbb9..1a8247ec3 100644 --- a/src/main/cpp/base/RateLimiter.cpp +++ b/src/main/cpp/base/RateLimiter.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/ThreadPoolImpl.cpp b/src/main/cpp/base/ThreadPoolImpl.cpp index 4ab1c2917..3befa9c35 100644 --- a/src/main/cpp/base/ThreadPoolImpl.cpp +++ b/src/main/cpp/base/ThreadPoolImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/TopAddressing.cpp b/src/main/cpp/base/TopAddressing.cpp index 6d2bcf588..dd45e67fa 100644 --- a/src/main/cpp/base/TopAddressing.cpp +++ b/src/main/cpp/base/TopAddressing.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/UniqueIdGenerator.cpp b/src/main/cpp/base/UniqueIdGenerator.cpp index afc5daa33..aab5b146d 100644 --- a/src/main/cpp/base/UniqueIdGenerator.cpp +++ b/src/main/cpp/base/UniqueIdGenerator.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/UtilAll.cpp b/src/main/cpp/base/UtilAll.cpp index bd1537736..892dc87c2 100644 --- a/src/main/cpp/base/UtilAll.cpp +++ b/src/main/cpp/base/UtilAll.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/base/include/DigestType.h index 12ba38c76..e255d828a 100644 --- a/src/main/cpp/base/include/DigestType.h +++ b/src/main/cpp/base/include/DigestType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/base/include/Encoding.h index 16e7ac58b..cfb71cae0 100644 --- a/src/main/cpp/base/include/Encoding.h +++ b/src/main/cpp/base/include/Encoding.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/Histogram.h b/src/main/cpp/base/include/Histogram.h index 50084d7e2..89b7f3fcc 100644 --- a/src/main/cpp/base/include/Histogram.h +++ b/src/main/cpp/base/include/Histogram.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/HostInfo.h b/src/main/cpp/base/include/HostInfo.h index 4516a174e..1b2a543c9 100644 --- a/src/main/cpp/base/include/HostInfo.h +++ b/src/main/cpp/base/include/HostInfo.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/HttpClient.h b/src/main/cpp/base/include/HttpClient.h index 94a3166bc..77f695516 100644 --- a/src/main/cpp/base/include/HttpClient.h +++ b/src/main/cpp/base/include/HttpClient.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/HttpClientImpl.h b/src/main/cpp/base/include/HttpClientImpl.h index d7035d188..e54bb4372 100644 --- a/src/main/cpp/base/include/HttpClientImpl.h +++ b/src/main/cpp/base/include/HttpClientImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index 4cb7e881e..cad3e0618 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h index f45f36d09..371bd23a4 100644 --- a/src/main/cpp/base/include/MessageAccessor.h +++ b/src/main/cpp/base/include/MessageAccessor.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h index f03186faa..079ba33fe 100644 --- a/src/main/cpp/base/include/MessageImpl.h +++ b/src/main/cpp/base/include/MessageImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index 199c0722e..ac3af5ba8 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 02b8e63d0..1a84c66bc 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 44a9c9d91..df51ce151 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/RateLimiter.h b/src/main/cpp/base/include/RateLimiter.h index 05c2ed60c..76982a34d 100644 --- a/src/main/cpp/base/include/RateLimiter.h +++ b/src/main/cpp/base/include/RateLimiter.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/ThreadPool.h b/src/main/cpp/base/include/ThreadPool.h index 0dc75b534..d4582252a 100644 --- a/src/main/cpp/base/include/ThreadPool.h +++ b/src/main/cpp/base/include/ThreadPool.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/ThreadPoolImpl.h b/src/main/cpp/base/include/ThreadPoolImpl.h index 20392398c..b5d754cf8 100644 --- a/src/main/cpp/base/include/ThreadPoolImpl.h +++ b/src/main/cpp/base/include/ThreadPoolImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/TopAddressing.h b/src/main/cpp/base/include/TopAddressing.h index 52400e6e2..02602598f 100644 --- a/src/main/cpp/base/include/TopAddressing.h +++ b/src/main/cpp/base/include/TopAddressing.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/UniqueIdGenerator.h b/src/main/cpp/base/include/UniqueIdGenerator.h index 1313b8593..e44c5e64c 100644 --- a/src/main/cpp/base/include/UniqueIdGenerator.h +++ b/src/main/cpp/base/include/UniqueIdGenerator.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/include/UtilAll.h b/src/main/cpp/base/include/UtilAll.h index 6c840bb9f..d4cb2eac4 100644 --- a/src/main/cpp/base/include/UtilAll.h +++ b/src/main/cpp/base/include/UtilAll.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/mocks/include/HttpClientMock.h b/src/main/cpp/base/mocks/include/HttpClientMock.h index 69d59d2e0..c2e946470 100644 --- a/src/main/cpp/base/mocks/include/HttpClientMock.h +++ b/src/main/cpp/base/mocks/include/HttpClientMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/base/mocks/include/MessageListenerMock.h b/src/main/cpp/base/mocks/include/MessageListenerMock.h index 9c8205367..431d1bb79 100644 --- a/src/main/cpp/base/mocks/include/MessageListenerMock.h +++ b/src/main/cpp/base/mocks/include/MessageListenerMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index e83d132cb..c3c3294db 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index 3b4100aaf..c72aedbfa 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index c6c86a6c7..5e37dfa24 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/LogInterceptor.cpp b/src/main/cpp/client/LogInterceptor.cpp index 215b10f8f..09dbc9fff 100644 --- a/src/main/cpp/client/LogInterceptor.cpp +++ b/src/main/cpp/client/LogInterceptor.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/LogInterceptorFactory.cpp b/src/main/cpp/client/LogInterceptorFactory.cpp index 40bf5658c..16247e0ef 100644 --- a/src/main/cpp/client/LogInterceptorFactory.cpp +++ b/src/main/cpp/client/LogInterceptorFactory.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 650c716c9..5d546b00e 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index 3a4f02f32..33641582d 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/TlsHelper.cpp b/src/main/cpp/client/TlsHelper.cpp index ac13f856a..62d3ac1f2 100644 --- a/src/main/cpp/client/TlsHelper.cpp +++ b/src/main/cpp/client/TlsHelper.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index e6682cb90..88e997601 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,9 +15,9 @@ * limitations under the License. */ #include "TopicAssignmentInfo.h" +#include "google/rpc/code.pb.h" #include "rocketmq/RocketMQ.h" #include "spdlog/spdlog.h" -#include "google/rpc/code.pb.h" ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp index 5f32d41f9..9d43afb12 100644 --- a/src/main/cpp/client/TopicPublishInfo.cpp +++ b/src/main/cpp/client/TopicPublishInfo.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/TracingUtility.cpp b/src/main/cpp/client/TracingUtility.cpp index c2ccdc2cd..b2f216123 100644 --- a/src/main/cpp/client/TracingUtility.cpp +++ b/src/main/cpp/client/TracingUtility.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h index b937118aa..eea6bd0d6 100644 --- a/src/main/cpp/client/include/Assignment.h +++ b/src/main/cpp/client/include/Assignment.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h index 17c96ff62..35d24b881 100644 --- a/src/main/cpp/client/include/Broker.h +++ b/src/main/cpp/client/include/Broker.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index 553284edb..0ad6e3dc3 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,13 +28,13 @@ class Client : virtual public ClientConfig { public: ~Client() override = default; - virtual void endpointsInUse(absl::flat_hash_set &endpoints) = 0; + virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; virtual void heartbeat() = 0; virtual bool active() = 0; - virtual void onRemoteEndpointRemoval(const std::vector &) = 0; + virtual void onRemoteEndpointRemoval(const std::vector&) = 0; /** * For endpoints that are marked as inactive due to one or multiple business @@ -44,8 +44,7 @@ class Client : virtual public ClientConfig { */ virtual void healthCheck() = 0; - virtual void schedule(const std::string &task_name, - const std::function &task, + virtual void schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) = 0; virtual void notifyClientTermination() = 0; diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 8cff8c462..669b2205d 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index 06bf5e477..d2771908a 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 50ab4acf8..2f5223eb3 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,9 +17,9 @@ #pragma once #include +#include #include #include -#include #include "Client.h" #include "ReceiveMessageCallback.h" @@ -57,7 +57,7 @@ class ClientManager { virtual void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, std::chrono::milliseconds timeout, const std::function*)>& cb) = 0; - + virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, @@ -108,16 +108,14 @@ class ClientManager { virtual std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, const ReportThreadStackTraceRequest& request, std::chrono::milliseconds timeout) = 0; - + virtual std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, const ReportMessageConsumptionResultRequest& request, std::chrono::milliseconds timeout) = 0; virtual State state() const = 0; - virtual void submit(std::function task) = 0; - }; using ClientManagerPtr = std::shared_ptr; diff --git a/src/main/cpp/client/include/ClientManagerFactory.h b/src/main/cpp/client/include/ClientManagerFactory.h index 0a4dbd08e..ec6488de7 100644 --- a/src/main/cpp/client/include/ClientManagerFactory.h +++ b/src/main/cpp/client/include/ClientManagerFactory.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 77ec31caf..f3ed5ff10 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ConsumeMessageType.h b/src/main/cpp/client/include/ConsumeMessageType.h index 8768f12ef..9af69ba8f 100644 --- a/src/main/cpp/client/include/ConsumeMessageType.h +++ b/src/main/cpp/client/include/ConsumeMessageType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/HeartbeatDataCallback.h b/src/main/cpp/client/include/HeartbeatDataCallback.h index 81d0689b0..f4466e2b0 100644 --- a/src/main/cpp/client/include/HeartbeatDataCallback.h +++ b/src/main/cpp/client/include/HeartbeatDataCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/InterceptorContinuation.h b/src/main/cpp/client/include/InterceptorContinuation.h index b243f83d1..07c64335f 100644 --- a/src/main/cpp/client/include/InterceptorContinuation.h +++ b/src/main/cpp/client/include/InterceptorContinuation.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/LogInterceptor.h b/src/main/cpp/client/include/LogInterceptor.h index 6fa9773a3..0bec16162 100644 --- a/src/main/cpp/client/include/LogInterceptor.h +++ b/src/main/cpp/client/include/LogInterceptor.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/LogInterceptorFactory.h b/src/main/cpp/client/include/LogInterceptorFactory.h index 533fedf78..b18ab10e5 100644 --- a/src/main/cpp/client/include/LogInterceptorFactory.h +++ b/src/main/cpp/client/include/LogInterceptorFactory.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/OpenSSLCompatible.h b/src/main/cpp/client/include/OpenSSLCompatible.h index 9ca0146ab..cca778726 100644 --- a/src/main/cpp/client/include/OpenSSLCompatible.h +++ b/src/main/cpp/client/include/OpenSSLCompatible.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/src/main/cpp/client/include/OrphanTransactionCallback.h index 8a843e0b3..0e78db11c 100644 --- a/src/main/cpp/client/include/OrphanTransactionCallback.h +++ b/src/main/cpp/client/include/OrphanTransactionCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h index 3502523dc..1d5d2658c 100644 --- a/src/main/cpp/client/include/Partition.h +++ b/src/main/cpp/client/include/Partition.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h index 631efa31d..10ddaf7ac 100644 --- a/src/main/cpp/client/include/ReceiveMessageCallback.h +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index 05a11501d..32efad98a 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index c7add8a72..add9931c9 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index 15243ed56..80a397241 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index 9a563e810..fa1660921 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h index 24a587b47..b5b8c7fe7 100644 --- a/src/main/cpp/client/include/ServiceAddress.h +++ b/src/main/cpp/client/include/ServiceAddress.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h index c1e116e6f..b10581c0c 100644 --- a/src/main/cpp/client/include/Signature.h +++ b/src/main/cpp/client/include/Signature.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h index c0176c7f0..b96f4d6ac 100644 --- a/src/main/cpp/client/include/TlsHelper.h +++ b/src/main/cpp/client/include/TlsHelper.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/Topic.h b/src/main/cpp/client/include/Topic.h index 9c3ae188d..258abab55 100644 --- a/src/main/cpp/client/include/Topic.h +++ b/src/main/cpp/client/include/Topic.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h index 8162e1ded..7d522fd96 100644 --- a/src/main/cpp/client/include/TopicAssignmentInfo.h +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/client/include/TopicPublishInfo.h index ad7f1794e..b1d22ddca 100644 --- a/src/main/cpp/client/include/TopicPublishInfo.h +++ b/src/main/cpp/client/include/TopicPublishInfo.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/TopicRouteChangeCallback.h b/src/main/cpp/client/include/TopicRouteChangeCallback.h index e8adac21b..0490808f7 100644 --- a/src/main/cpp/client/include/TopicRouteChangeCallback.h +++ b/src/main/cpp/client/include/TopicRouteChangeCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h index 2d17d906a..f151d0058 100644 --- a/src/main/cpp/client/include/TopicRouteData.h +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp index a2ad7cf44..f2fc94ac9 100644 --- a/src/main/cpp/client/mocks/RpcClientMock.cpp +++ b/src/main/cpp/client/mocks/RpcClientMock.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h index 0183d5c14..703e3482c 100644 --- a/src/main/cpp/client/mocks/include/ClientConfigMock.h +++ b/src/main/cpp/client/mocks/include/ClientConfigMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index e7c88a8bb..4172ab54c 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h index 9647f8a26..d7fbf782d 100644 --- a/src/main/cpp/client/mocks/include/ClientMock.h +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h index a7df00e6c..02c958838 100644 --- a/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h +++ b/src/main/cpp/client/mocks/include/ReceiveMessageCallbackMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index 1ef1e1b4b..2cde10fd9 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/concurrent/CountdownLatch.cpp b/src/main/cpp/concurrent/CountdownLatch.cpp index 8143cb20a..363494e06 100644 --- a/src/main/cpp/concurrent/CountdownLatch.cpp +++ b/src/main/cpp/concurrent/CountdownLatch.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/concurrent/include/CountdownLatch.h b/src/main/cpp/concurrent/include/CountdownLatch.h index 30263f0c5..966b5e291 100644 --- a/src/main/cpp/concurrent/include/CountdownLatch.h +++ b/src/main/cpp/concurrent/include/CountdownLatch.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/log/LoggerImpl.cpp b/src/main/cpp/log/LoggerImpl.cpp index a4241d352..6ff39f105 100644 --- a/src/main/cpp/log/LoggerImpl.cpp +++ b/src/main/cpp/log/LoggerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/log/include/LoggerImpl.h b/src/main/cpp/log/include/LoggerImpl.h index 1c337d92b..338b7e2e7 100644 --- a/src/main/cpp/log/include/LoggerImpl.h +++ b/src/main/cpp/log/include/LoggerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/BrokerData.cpp b/src/main/cpp/remoting/BrokerData.cpp index 5b7762f5a..51650aeae 100644 --- a/src/main/cpp/remoting/BrokerData.cpp +++ b/src/main/cpp/remoting/BrokerData.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp index 055d27cb4..551f3a2c5 100644 --- a/src/main/cpp/remoting/QueryRouteRequestHeader.cpp +++ b/src/main/cpp/remoting/QueryRouteRequestHeader.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/QueueData.cpp b/src/main/cpp/remoting/QueueData.cpp index 6f2274030..1a0796eaf 100644 --- a/src/main/cpp/remoting/QueueData.cpp +++ b/src/main/cpp/remoting/QueueData.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/RemotingCommand.cpp b/src/main/cpp/remoting/RemotingCommand.cpp index 0019e555e..743096aab 100644 --- a/src/main/cpp/remoting/RemotingCommand.cpp +++ b/src/main/cpp/remoting/RemotingCommand.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/TopicRouteData.cpp b/src/main/cpp/remoting/TopicRouteData.cpp index 8b9750dda..71a9f3c00 100644 --- a/src/main/cpp/remoting/TopicRouteData.cpp +++ b/src/main/cpp/remoting/TopicRouteData.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/BrokerData.h b/src/main/cpp/remoting/include/BrokerData.h index d522ae71b..83ef5fd81 100644 --- a/src/main/cpp/remoting/include/BrokerData.h +++ b/src/main/cpp/remoting/include/BrokerData.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/CommandCustomHeader.h b/src/main/cpp/remoting/include/CommandCustomHeader.h index 441bcd94c..a6afbc22e 100644 --- a/src/main/cpp/remoting/include/CommandCustomHeader.h +++ b/src/main/cpp/remoting/include/CommandCustomHeader.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/LanguageCode.h b/src/main/cpp/remoting/include/LanguageCode.h index 9204a4da2..7151faf4d 100644 --- a/src/main/cpp/remoting/include/LanguageCode.h +++ b/src/main/cpp/remoting/include/LanguageCode.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h index 0a9682af7..b5669690e 100644 --- a/src/main/cpp/remoting/include/QueryRouteRequestHeader.h +++ b/src/main/cpp/remoting/include/QueryRouteRequestHeader.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/QueueData.h b/src/main/cpp/remoting/include/QueueData.h index 4be93bebf..9530cfca8 100644 --- a/src/main/cpp/remoting/include/QueueData.h +++ b/src/main/cpp/remoting/include/QueueData.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/RemotingCommand.h b/src/main/cpp/remoting/include/RemotingCommand.h index c4b5ef1a2..67e5ff4c5 100644 --- a/src/main/cpp/remoting/include/RemotingCommand.h +++ b/src/main/cpp/remoting/include/RemotingCommand.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/RemotingCommandType.h b/src/main/cpp/remoting/include/RemotingCommandType.h index 6e7c4fefc..9ca78d2c0 100644 --- a/src/main/cpp/remoting/include/RemotingCommandType.h +++ b/src/main/cpp/remoting/include/RemotingCommandType.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/RequestCode.h b/src/main/cpp/remoting/include/RequestCode.h index 58de924ca..cb28eaf2a 100644 --- a/src/main/cpp/remoting/include/RequestCode.h +++ b/src/main/cpp/remoting/include/RequestCode.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,7 +26,7 @@ enum class RequestCode : std::int32_t { SendMessage = 10, PullMessage = 11, - QueryRoute = 105, + QueryRoute = 105, }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/remoting/include/ResponseCode.h b/src/main/cpp/remoting/include/ResponseCode.h index c47014f6e..4cfa924e4 100644 --- a/src/main/cpp/remoting/include/ResponseCode.h +++ b/src/main/cpp/remoting/include/ResponseCode.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/remoting/include/TopicRouteData.h b/src/main/cpp/remoting/include/TopicRouteData.h index 16cf7c8fd..2c044f8c9 100644 --- a/src/main/cpp/remoting/include/TopicRouteData.h +++ b/src/main/cpp/remoting/include/TopicRouteData.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -38,7 +38,6 @@ struct TopicRouteData { */ std::vector broker_data_; - static TopicRouteData decode(const google::protobuf::Struct& root); }; diff --git a/src/main/cpp/remoting/include/Version.h b/src/main/cpp/remoting/include/Version.h index 8decd1329..85fdc4629 100644 --- a/src/main/cpp/remoting/include/Version.h +++ b/src/main/cpp/remoting/include/Version.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index 95df783cf..3d128189d 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/rocketmq/AwaitPullCallback.cpp index db315f0ab..f4a5d99fd 100644 --- a/src/main/cpp/rocketmq/AwaitPullCallback.cpp +++ b/src/main/cpp/rocketmq/AwaitPullCallback.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 0b48a054e..0cc18d99a 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 3fd79fbd0..99d9b16c8 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp index 993ffafe7..4b34a8df4 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index d9b4c0215..4376afdf4 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/rocketmq/CredentialsProvider.cpp index 41ae320d4..548e9d810 100644 --- a/src/main/cpp/rocketmq/CredentialsProvider.cpp +++ b/src/main/cpp/rocketmq/CredentialsProvider.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp index ba3581d46..e4beacb12 100644 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQProducer.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp index f777d774b..56e22d391 100644 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index 898368933..7a50dacac 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp index 428f6f8bc..d2eb40f95 100644 --- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/rocketmq/FilterExpression.cpp index 133eb9830..a60290b8d 100644 --- a/src/main/cpp/rocketmq/FilterExpression.cpp +++ b/src/main/cpp/rocketmq/FilterExpression.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp index 344301d17..6604f1134 100644 --- a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index 591bd568c..d9ba8cefd 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 0d122493a..e14399fd2 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp index 48d4a7bd0..530f2f3c8 100644 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PullConsumerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 43f53e29e..49eb830f5 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendCallbacks.cpp index 596ad01bc..cddf1fc5a 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendCallbacks.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp index 6482c3e4b..85e865f2c 100644 --- a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 52cc6d6c6..21610aa98 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h index 59fd11539..77df4d576 100644 --- a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,16 +24,14 @@ ROCKETMQ_NAMESPACE_BEGIN -class AsyncReceiveMessageCallback - : public ReceiveMessageCallback, - public std::enable_shared_from_this { +class AsyncReceiveMessageCallback : public ReceiveMessageCallback, + public std::enable_shared_from_this { public: explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); ~AsyncReceiveMessageCallback() override = default; - void onCompletion(const std::error_code &ec, - const ReceiveMessageResult &result) override; + void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) override; void receiveMessageLater(); @@ -50,7 +48,7 @@ class AsyncReceiveMessageCallback void checkThrottleThenReceive(); - static const char *RECEIVE_LATER_TASK_NAME; + static const char* RECEIVE_LATER_TASK_NAME; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h index 6d79c9a55..c3fd373ee 100644 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ b/src/main/cpp/rocketmq/include/AwaitPullCallback.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index f78c922a0..80f1f8cf4 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ClientResourceBundle.h b/src/main/cpp/rocketmq/include/ClientResourceBundle.h index 978849649..0cabca736 100644 --- a/src/main/cpp/rocketmq/include/ClientResourceBundle.h +++ b/src/main/cpp/rocketmq/include/ClientResourceBundle.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h index 3423ebfc8..8f42cde73 100644 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index abedc0f9f..e6980e4f1 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h index 821d88753..50173a578 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h index d6b5bfaec..52bad1406 100644 --- a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index 6b3106320..ad9cdad52 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h index b52142cde..d6fc21e80 100644 --- a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/src/main/cpp/rocketmq/include/FilterExpression.h index 77ca68cca..51cd89a00 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/src/main/cpp/rocketmq/include/FilterExpression.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h index 056b1a324..270c494a8 100644 --- a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h +++ b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h index 52bb345be..443f36022 100644 --- a/src/main/cpp/rocketmq/include/NameServerResolver.h +++ b/src/main/cpp/rocketmq/include/NameServerResolver.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index dda6df6a1..e53edb76c 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index 598dcbb8d..68947720b 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index f4d852534..678bcc3ff 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h index 6be279891..30c7cc019 100644 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PullConsumerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index e0092351d..fee31369c 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index cbe95aa74..238e377f3 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h index 168175504..d7cc68608 100644 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ b/src/main/cpp/rocketmq/include/SendCallbacks.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h index fed37bcfd..2648b4a84 100644 --- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h index dc4c2a370..0789e4ae6 100644 --- a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h +++ b/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 37e50dd81..4b721ed93 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h index 1a1810b46..ee9251937 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h index bbd6c3216..9ed217ef0 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumerMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h index 005d5e341..f572a0445 100644 --- a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h +++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h index 56216bcbc..4ae20860d 100644 --- a/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ProcessQueueMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 69c517cd4..63275ab8a 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index 723cfcafc..c5c48e439 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index 7f86c49cf..34d676bde 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h index 01c1265a6..14b5fd254 100644 --- a/src/main/cpp/scheduler/include/SchedulerImpl.h +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 2ea70aa54..7ad9f5b51 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index 5b40f7f34..c00b8ec7c 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/benchmark/BenchmarkTest.cpp b/src/test/cpp/benchmark/BenchmarkTest.cpp index fe4e15d34..421541d2b 100644 --- a/src/test/cpp/benchmark/BenchmarkTest.cpp +++ b/src/test/cpp/benchmark/BenchmarkTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp index 051f50d89..fe3920420 100644 --- a/src/test/cpp/it/RpcClientTest.cpp +++ b/src/test/cpp/it/RpcClientTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp index 85d3954ae..42c7a13e8 100644 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ b/src/test/cpp/it/TopAddressingTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp index 1f49725dc..1edfea028 100644 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ b/src/test/cpp/it/TopicPublishInfoTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp index 8cd005035..e88e00dad 100644 --- a/src/test/cpp/ut/admin/AdminServerTest.cpp +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/api/ErrorCodeTest.cpp b/src/test/cpp/ut/api/ErrorCodeTest.cpp index 68badd2f9..c63df2f33 100644 --- a/src/test/cpp/ut/api/ErrorCodeTest.cpp +++ b/src/test/cpp/ut/api/ErrorCodeTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp index 613e0a215..388a2c43f 100644 --- a/src/test/cpp/ut/base/HistogramTest.cpp +++ b/src/test/cpp/ut/base/HistogramTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp index d106061d0..3b75f0d38 100644 --- a/src/test/cpp/ut/base/HostInfoTest.cpp +++ b/src/test/cpp/ut/base/HostInfoTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp index 9a5df5642..5ea118516 100644 --- a/src/test/cpp/ut/base/HttpClientTest.cpp +++ b/src/test/cpp/ut/base/HttpClientTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp index 25efffd5c..91212e5ef 100644 --- a/src/test/cpp/ut/base/InvocationContextTest.cpp +++ b/src/test/cpp/ut/base/InvocationContextTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp index 32575a075..894cfe50f 100644 --- a/src/test/cpp/ut/base/MQMessageExtTest.cpp +++ b/src/test/cpp/ut/base/MQMessageExtTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp index 4b018d665..c42ef2219 100644 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ b/src/test/cpp/ut/base/MQMessageTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp index b61f67023..89b3312b5 100644 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ b/src/test/cpp/ut/base/MixAllTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp index 97726a514..ca6c047f3 100644 --- a/src/test/cpp/ut/base/RateLimiterTest.cpp +++ b/src/test/cpp/ut/base/RateLimiterTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp index 9be1ed7a3..9cbde6cfa 100644 --- a/src/test/cpp/ut/base/ThreadPoolTest.cpp +++ b/src/test/cpp/ut/base/ThreadPoolTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp index a2a5652c3..cfb6e7879 100644 --- a/src/test/cpp/ut/base/TopAddressingTest.cpp +++ b/src/test/cpp/ut/base/TopAddressingTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp index dbafdeaeb..d78e1347b 100644 --- a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp +++ b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp index a142ddea7..9ef8c83c7 100644 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ b/src/test/cpp/ut/base/UtilAllTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index 7c182eda8..d63cabf5a 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp index 4dc8c5dc0..3ab764802 100644 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/test/cpp/ut/client/ClientTest.cpp index d67bec23e..3d8148f61 100644 --- a/src/test/cpp/ut/client/ClientTest.cpp +++ b/src/test/cpp/ut/client/ClientTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp index 77d9a8c6a..f57477978 100644 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ b/src/test/cpp/ut/client/RpcClientTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/TlsHelperTest.cpp b/src/test/cpp/ut/client/TlsHelperTest.cpp index ea834eef6..89745de50 100644 --- a/src/test/cpp/ut/client/TlsHelperTest.cpp +++ b/src/test/cpp/ut/client/TlsHelperTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp index ee8ea7eb0..003fab703 100644 --- a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp +++ b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp index b47bb0c4c..42b6cf0e5 100644 --- a/src/test/cpp/ut/client/TracingUtilityTest.cpp +++ b/src/test/cpp/ut/client/TracingUtilityTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp index 6545b0a68..3204a6055 100644 --- a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp +++ b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp index 815dfef37..b912d066c 100644 --- a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp +++ b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/grpc/GrpcTimer.cpp b/src/test/cpp/ut/grpc/GrpcTimer.cpp index 7be1ce751..06432715d 100644 --- a/src/test/cpp/ut/grpc/GrpcTimer.cpp +++ b/src/test/cpp/ut/grpc/GrpcTimer.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp index bfc474b5a..372ed177f 100644 --- a/src/test/cpp/ut/logger/LoggerTest.cpp +++ b/src/test/cpp/ut/logger/LoggerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/remoting/BrokerDataTest.cpp b/src/test/cpp/ut/remoting/BrokerDataTest.cpp index 03d7fee4c..96249ebb3 100644 --- a/src/test/cpp/ut/remoting/BrokerDataTest.cpp +++ b/src/test/cpp/ut/remoting/BrokerDataTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp index 81ed4ddb9..d887ba6fa 100644 --- a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp +++ b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/remoting/QueueDataTest.cpp b/src/test/cpp/ut/remoting/QueueDataTest.cpp index dbf8de993..81c55ffe3 100644 --- a/src/test/cpp/ut/remoting/QueueDataTest.cpp +++ b/src/test/cpp/ut/remoting/QueueDataTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp index 7a76a0686..68f887286 100644 --- a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp +++ b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp index f7f9deef8..d2718b47b 100644 --- a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp +++ b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp index 3c7800c4d..e8e437474 100644 --- a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp +++ b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 58d5027a8..561d26c62 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp index a139e4fd8..b43267e9c 100644 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp index a8d6d5b09..0c950c206 100644 --- a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/ConsumerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp index 6d23bf81d..4f8bb9d81 100644 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index a5247ac81..7aab28175 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp index 3b8c793ca..a01cb6511 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp index 40c559af4..f296f93c1 100644 --- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp index baf885216..e637b415d 100644 --- a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp +++ b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp index d193944f0..f7df8ce9c 100644 --- a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp +++ b/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp index 09f0533cb..1f51c70c9 100644 --- a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp index ed871a2ef..77267d6ad 100644 --- a/src/test/cpp/ut/rocketmq/MessageTest.cpp +++ b/src/test/cpp/ut/rocketmq/MessageTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp index dea0f5aba..bfa7d5177 100644 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index 5322f66e5..5bf670cfb 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index 9c0f045e9..a71451981 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index 7a25655cf..bf83bdaf5 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp index 5504e642e..da037d99a 100644 --- a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp +++ b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp index 21f20b3d1..cf16502f1 100644 --- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp index 2e0d35634..9cdec60fd 100644 --- a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h index 93064cb7c..3e84ef2b0 100644 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ b/src/test/cpp/ut/rocketmq/include/MQClientTest.h @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 2dfafa743..6e223e314 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp index d0e4ec440..1d859dc85 100644 --- a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp index 97303029f..beb39fa13 100644 --- a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp +++ b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp @@ -5,9 +5,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/tools/format.sh b/tools/format.sh old mode 100644 new mode 100755 From 55bccfc35e57b9989bcba0df28414ed331f9f0ac Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 13:49:41 +0800 Subject: [PATCH 104/133] Set --color=yes --- .bazelrc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.bazelrc b/.bazelrc index 1c53b01e2..dce4c4711 100644 --- a/.bazelrc +++ b/.bazelrc @@ -6,7 +6,11 @@ # leave room for compiler/linker. # The number 2G is chosen heuristically to both support large VM and small VM with RBE. # Startup options cannot be selected via config. -startup --host_jvm_args=-Xmx4g +startup --host_jvm_args=-Xmx2g + +run --color=yes + +build --color=yes build --host_force_python=PY3 build --host_javabase=@bazel_tools//tools/jdk:remote_jdk11 From 5ba187ac92965afec98efe474814fd837dcfb1c6 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 18 Oct 2021 15:58:12 +0800 Subject: [PATCH 105/133] Format code (#369) --- example/rocketmq/BenchmarkPushConsumer.cpp | 2 +- example/rocketmq/ExampleAsyncProducer.cpp | 2 +- example/rocketmq/ExampleBroadcastPushConsumer.cpp | 2 +- example/rocketmq/ExampleProducer.cpp | 2 +- example/rocketmq/ExamplePullConsumer.cpp | 2 +- example/rocketmq/ExamplePushConsumer.cpp | 2 +- example/rocketmq/ExampleTransactionProducer.cpp | 2 +- example/rocketmq/PushConsumerWithCustomExecutor.cpp | 4 ++-- example/rocketmq/PushConsumerWithThrottle.cpp | 4 ++-- example/rocketmq/SqlConsumer.cpp | 2 +- example/rocketmq/SqlProducer.cpp | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/example/rocketmq/BenchmarkPushConsumer.cpp index 5d4d3a4eb..400fa8071 100644 --- a/example/rocketmq/BenchmarkPushConsumer.cpp +++ b/example/rocketmq/BenchmarkPushConsumer.cpp @@ -22,7 +22,7 @@ #include #include - using namespace rocketmq; +using namespace rocketmq; class CounterMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp index b4d4bd453..083cb34a0 100644 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ b/example/rocketmq/ExampleAsyncProducer.cpp @@ -25,7 +25,7 @@ #include #include - using namespace rocketmq; +using namespace rocketmq; int getAndReset(std::atomic_int& counter) { int current; diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp index 4302608da..529f1e603 100644 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ b/example/rocketmq/ExampleBroadcastPushConsumer.cpp @@ -24,7 +24,7 @@ #include #include - using namespace rocketmq; +using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index a74ea9faf..e3e2c8042 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -20,7 +20,7 @@ #include #include - using namespace rocketmq; +using namespace rocketmq; const std::string& alphaNumeric() { static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp index 694956c28..729963361 100644 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ b/example/rocketmq/ExamplePullConsumer.cpp @@ -21,7 +21,7 @@ #include "rocketmq/DefaultMQPullConsumer.h" #include "rocketmq/Logger.h" - int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { const char* group = "GID_group003"; const char* topic = "yc001"; const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 6114a724d..8a2e9a604 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -25,7 +25,7 @@ #include "rocketmq/DefaultMQPushConsumer.h" - using namespace rocketmq; +using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/example/rocketmq/ExampleTransactionProducer.cpp index 3858174f9..1fe5b87ed 100644 --- a/example/rocketmq/ExampleTransactionProducer.cpp +++ b/example/rocketmq/ExampleTransactionProducer.cpp @@ -17,7 +17,7 @@ #include "rocketmq/DefaultMQProducer.h" #include - using namespace ROCKETMQ_NAMESPACE; +using namespace ROCKETMQ_NAMESPACE; int main(int argc, char* argv[]) { DefaultMQProducer producer("TestGroup"); diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/example/rocketmq/PushConsumerWithCustomExecutor.cpp index 72212f155..82dd57de4 100644 --- a/example/rocketmq/PushConsumerWithCustomExecutor.cpp +++ b/example/rocketmq/PushConsumerWithCustomExecutor.cpp @@ -23,9 +23,9 @@ #include #include - ROCKETMQ_NAMESPACE_BEGIN +ROCKETMQ_NAMESPACE_BEGIN - class ExecutorImpl { +class ExecutorImpl { public: ExecutorImpl() : state_(State::CREATED) { } diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/example/rocketmq/PushConsumerWithThrottle.cpp index 29cbd9a63..c03b6c54e 100644 --- a/example/rocketmq/PushConsumerWithThrottle.cpp +++ b/example/rocketmq/PushConsumerWithThrottle.cpp @@ -22,9 +22,9 @@ #include #include - ROCKETMQ_NAMESPACE_BEGIN +ROCKETMQ_NAMESPACE_BEGIN - class CounterMessageListener : public StandardMessageListener { +class CounterMessageListener : public StandardMessageListener { public: explicit CounterMessageListener(std::atomic_long& counter) : counter_(counter) { } diff --git a/example/rocketmq/SqlConsumer.cpp b/example/rocketmq/SqlConsumer.cpp index 11c16efa1..c07e4a034 100644 --- a/example/rocketmq/SqlConsumer.cpp +++ b/example/rocketmq/SqlConsumer.cpp @@ -21,7 +21,7 @@ #include #include - using namespace rocketmq; +using namespace rocketmq; class SampleMQMessageListener : public StandardMessageListener { public: diff --git a/example/rocketmq/SqlProducer.cpp b/example/rocketmq/SqlProducer.cpp index 20900f975..bc0fb4744 100644 --- a/example/rocketmq/SqlProducer.cpp +++ b/example/rocketmq/SqlProducer.cpp @@ -17,7 +17,7 @@ #include "rocketmq/DefaultMQProducer.h" #include - using namespace rocketmq; +using namespace rocketmq; int main(int argc, char* argv[]) { From a7184b2a59d5c08227b329160a1f9525c2a6b546 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 18 Oct 2021 19:35:37 +0800 Subject: [PATCH 106/133] Allow configuring custom offset-store --- api/rocketmq/DefaultMQPushConsumer.h | 3 +++ src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp | 4 ++++ src/main/cpp/rocketmq/include/PushConsumer.h | 3 +++ src/main/cpp/rocketmq/include/PushConsumerImpl.h | 2 +- src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h | 2 ++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h index 03ac7d124..0693b8d3c 100644 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ b/api/rocketmq/DefaultMQPushConsumer.h @@ -28,6 +28,7 @@ #include "MessageListener.h" #include "rocketmq/Executor.h" #include "rocketmq/MessageModel.h" +#include "rocketmq/OffsetStore.h" ROCKETMQ_NAMESPACE_BEGIN @@ -110,6 +111,8 @@ class DefaultMQPushConsumer { std::string groupName() const; + void setOffsetStore(std::unique_ptr offset_store); + private: std::shared_ptr impl_; }; diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp index 7a50dacac..890221cf5 100644 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp @@ -127,4 +127,8 @@ std::string DefaultMQPushConsumer::groupName() const { return impl_->getGroupName(); } +void DefaultMQPushConsumer::setOffsetStore(std::unique_ptr offset_store) { + impl_->setOffsetStore(std::move(offset_store)); +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h index fee31369c..cf2da65b7 100644 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ b/src/main/cpp/rocketmq/include/PushConsumer.h @@ -25,6 +25,7 @@ #include "rocketmq/Executor.h" #include "rocketmq/MessageListener.h" #include "rocketmq/MessageModel.h" +#include "rocketmq/OffsetStore.h" ROCKETMQ_NAMESPACE_BEGIN @@ -53,6 +54,8 @@ class PushConsumer : virtual public Consumer { virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) = 0; virtual MessageListener* messageListener() = 0; + + virtual void setOffsetStore(std::unique_ptr offset_store) = 0; }; using PushConsumerSharedPtr = std::shared_ptr; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 238e377f3..399b87ed6 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -158,7 +158,7 @@ class PushConsumerImpl : virtual public ClientImpl, message_model_ = message_model; } - void offsetStore(std::unique_ptr offset_store) { + void setOffsetStore(std::unique_ptr offset_store) override { offset_store_ = std::move(offset_store); } diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 63275ab8a..e4508baa6 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -46,6 +46,8 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&), (override)); MOCK_METHOD(MessageListener*, messageListener, (), (override)); + + MOCK_METHOD(void, setOffsetStore, (std::unique_ptr), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file From 46444c2d8056ff9117a98ff8e792008af243494b Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Tue, 19 Oct 2021 03:52:47 +0000 Subject: [PATCH 107/133] BugFix: forward system attributes --- src/main/cpp/rocketmq/ProducerImpl.cpp | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index e14399fd2..4145e0aa4 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -17,6 +17,7 @@ #include "ProducerImpl.h" #include +#include #include #include #include @@ -112,6 +113,36 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM auto system_attribute = request.mutable_message()->mutable_system_attribute(); + // Handle Tag + auto&& tag = message.getTags(); + if (!tag.empty()) { + system_attribute->set_tag(tag); + } + + // Handle Key + const auto& keys = message.getKeys(); + if (!keys.empty()) { + system_attribute->mutable_keys()->Add(keys.begin(), keys.end()); + } + + // TraceContext + const auto& trace_context = message.traceContext(); + if (!trace_context.empty()) { + system_attribute->set_trace_context(trace_context); + } + + // Delivery Timestamp + auto delivery_timestamp = message.deliveryTimestamp(); + if (delivery_timestamp.time_since_epoch().count()) { + auto duration = delivery_timestamp.time_since_epoch(); + system_attribute->set_delivery_attempt(std::chrono::duration_cast(duration).count()); + } + + // Delay Level + if (message.getDelayTimeLevel()) { + system_attribute->set_delay_level(message.getDelayTimeLevel()); + } + // Born-time auto duration = absl::Now() - absl::UnixEpoch(); int64_t seconds = absl::ToInt64Seconds(duration); @@ -148,6 +179,7 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); } + // Forward user-defined-properties for (auto& item : message.getProperties()) { request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); } From 33b0cee832d09c5f635c35800b0393385a3ed630 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Wed, 20 Oct 2021 10:01:15 +0800 Subject: [PATCH 108/133] Bugfix: Compare and initiate poll-command-cycle when topic route is updated (#371) --- src/main/cpp/rocketmq/ClientImpl.cpp | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 0cc18d99a..0bc220566 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -303,8 +303,18 @@ void ClientImpl::updateRouteCache(const std::string& topic, const std::error_cod return; } + absl::flat_hash_set new_hosts; { absl::MutexLock lk(&topic_route_table_mtx_); + absl::flat_hash_set existed_hosts; + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!existed_hosts.contains(endpoint)) { + existed_hosts.emplace(std::move(endpoint)); + } + } + } if (!topic_route_table_.contains(topic)) { topic_route_table_.insert({topic, route}); SPDLOG_INFO("TopicRouteData for topic={} has changed. NONE --> {}", topic, route->debugString()); @@ -317,10 +327,25 @@ void ClientImpl::updateRouteCache(const std::string& topic, const std::error_cod route->debugString()); } } + absl::flat_hash_set hosts; + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!hosts.contains(endpoint)) { + hosts.emplace(std::move(endpoint)); + } + } + } + std::set_difference(hosts.begin(), hosts.end(), existed_hosts.begin(), existed_hosts.end(), + std::inserter(new_hosts, new_hosts.begin())); + } + for (const auto& endpoints : new_hosts) { + pollCommand(endpoints); } } void ClientImpl::pollCommand(const std::string& target) { + SPDLOG_INFO("Start to poll command to remote, target={}", target); absl::flat_hash_map metadata; Signature::sign(this, metadata); @@ -351,6 +376,7 @@ void ClientImpl::pollCommand(const std::string& target) { } void ClientImpl::verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message) { + SPDLOG_INFO("Received message to verify consumption, messageId={}", message.getMsgId()); MessageListener* listener = messageListener(); Metadata metadata; @@ -379,6 +405,13 @@ void ClientImpl::verifyMessageConsumption(std::string remote_address, std::strin } void ClientImpl::onPollCommandResponse(const InvocationContext* ctx) { + std::string address = ctx->remote_address; + absl::flat_hash_set hosts; + endpointsInUse(hosts); + if (!hosts.contains(address)) { + SPDLOG_INFO("Endpoints={} was not used, stop to poll command.", address); + return; + } if (!ctx->status.ok()) { static std::string task_name = "Poll-Command-Later"; client_manager_->getScheduler().schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, From d515ca50947b994d4e750c57d2f1581ae3c9830c Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Wed, 20 Oct 2021 02:10:48 +0000 Subject: [PATCH 109/133] Fix log --- src/main/cpp/rocketmq/ClientImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 0bc220566..7996f0386 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -409,7 +409,7 @@ void ClientImpl::onPollCommandResponse(const InvocationContext hosts; endpointsInUse(hosts); if (!hosts.contains(address)) { - SPDLOG_INFO("Endpoints={} was not used, stop to poll command.", address); + SPDLOG_INFO("Endpoint={} is now absent from route table. Break poll-command-cycle.", address); return; } if (!ctx->status.ok()) { From cc2af6dbe95bac5c5ff9c10498c451402c6b9d0c Mon Sep 17 00:00:00 2001 From: aaron ai Date: Wed, 20 Oct 2021 17:42:57 +0800 Subject: [PATCH 110/133] Bugfix: map iterator is out of bounds (#372) --- src/main/cpp/tracing/exporters/OtlpExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 7ad9f5b51..41796bae8 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -94,7 +94,7 @@ void OtlpExporterHandler::syncExportClients() { std::vector&& hosts = exp->hosts(); { absl::MutexLock lk(&clients_map_mtx_); - for (auto i = clients_map_.begin(); i != clients_map_.end(); i++) { + for (auto i = clients_map_.begin(); i != clients_map_.end();) { if (std::none_of(hosts.cbegin(), hosts.cend(), [&](const std::string& host) { return i->first == host; })) { clients_map_.erase(i++); } else { From 26223ed6d876671ef6af017031e43d8c7501b537 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Thu, 21 Oct 2021 10:50:57 +0800 Subject: [PATCH 111/133] Export trace-id and span-id using raw data (#374) --- src/main/cpp/rocketmq/ClientImpl.cpp | 23 +++++++++++++ src/main/cpp/rocketmq/include/ClientImpl.h | 5 +++ .../cpp/tracing/exporters/OtlpExporter.cpp | 32 +++++++++++++++---- .../tracing/exporters/include/OtlpExporter.h | 6 ++++ 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 7996f0386..049569ade 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -297,6 +297,28 @@ void ClientImpl::onTopicRouteReady(const std::string& topic, const std::error_co } } +void ClientImpl::updateTraceHosts() { + absl::flat_hash_set hosts; + absl::MutexLock lk(&topic_route_table_mtx_); + for (const auto& item : topic_route_table_) { + for (const auto& partition : item.second->partitions()) { + if (Permission::NONE == partition.permission()) { + continue; + } + if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { + continue; + } + std::string endpoint = partition.asMessageQueue().serviceAddress(); + if (!hosts.contains(endpoint)) { + hosts.emplace(std::move(endpoint)); + } + } + } + std::vector host_list(hosts.begin(), hosts.end()); + SPDLOG_DEBUG("Trace candidate hosts size={}", host_list.size()); + exporter_->updateHosts(host_list); +} + void ClientImpl::updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) { if (ec || !route || route->partitions().empty()) { SPDLOG_WARN("Yuck! route for {} is invalid. Cause: {}", topic, ec.message()); @@ -339,6 +361,7 @@ void ClientImpl::updateRouteCache(const std::string& topic, const std::error_cod std::set_difference(hosts.begin(), hosts.end(), existed_hosts.begin(), existed_hosts.end(), std::inserter(new_hosts, new_hosts.begin())); } + updateTraceHosts(); for (const auto& endpoints : new_hosts) { pollCommand(endpoints); } diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 80f1f8cf4..036179bfc 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -177,6 +177,11 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void onTopicRouteReady(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(inflight_route_requests_mtx_); + /** + * Update Trace candidate hosts. + */ + void updateTraceHosts() LOCKS_EXCLUDED(topic_route_table_mtx_); + /** * Update local cache for the topic. Note, route differences are logged in * INFO level since route bears fundamental importance. diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 41796bae8..836ceff84 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -65,6 +65,9 @@ void ExportClient::asyncExport(const collector_trace::ExportTraceServiceRequest& invocation_context); } +const int OtlpExporterHandler::SPAN_ID_SIZE = 8; +const int OtlpExporterHandler::TRACE_ID_SIZE = 16; + OtlpExporterHandler::OtlpExporterHandler(std::weak_ptr exporter) : exporter_(std::move(exporter)), completion_queue_(std::make_shared()) { auto exp = exporter_.lock(); @@ -198,11 +201,21 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter auto resource = new trace::ResourceSpans(); auto instrument_library_span = new trace::InstrumentationLibrarySpans(); + + uint8_t span_id_buf[SPAN_ID_SIZE]; + uint8_t trace_id_buf[TRACE_ID_SIZE]; for (const auto& span : spans) { auto item = new trace::Span(); - item->set_trace_id(span.context().trace_id().ToHex()); - item->set_span_id(span.context().span_id().ToHex()); - item->set_parent_span_id(span.parent_span_id().ToHex()); + + span.context().span_id().CopyTo(trace_id_buf); + item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); + + span.context().span_id().CopyTo(span_id_buf); + item->set_span_id(&span_id_buf, SPAN_ID_SIZE); + + span.parent_span_id().CopyTo(span_id_buf); + item->set_parent_span_id(&span_id_buf, SPAN_ID_SIZE); + item->set_name(span.name().data()); item->set_start_time_unix_nano(absl::ToUnixNanos(span.start_time())); @@ -260,8 +273,13 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter for (const auto& link : span.links()) { auto span_link = new trace::Span::Link(); - span_link->set_trace_id(link.trace_id().ToHex()); - span_link->set_span_id(link.span_id().ToHex()); + + link.trace_id().CopyTo(trace_id_buf); + item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); + + link.span_id().CopyTo(span_id_buf); + item->set_trace_id(&span_id_buf, SPAN_ID_SIZE); + for (const auto& attribute : link.attributes()) { auto kv = new common::KeyValue(); kv->set_key(attribute.first); @@ -331,7 +349,9 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter if (invocation_context->status.ok()) { SPDLOG_DEBUG("Export tracing spans OK"); } else { - SPDLOG_WARN("Failed to export tracing spans to {}", invocation_context->remote_address); + SPDLOG_WARN("Failed to export tracing spans to {}, gRPC code:{}, gRPC error message: {}", + invocation_context->remote_address, invocation_context->status.error_code(), + invocation_context->status.error_message()); } }; invocation_context->callback = callback; diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index c00b8ec7c..3b6755b9f 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -59,6 +59,9 @@ class OtlpExporter : public std::enable_shared_from_this { } void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { + if (hosts.empty()) { + return; + } absl::MutexLock lk(&hosts_mtx_); hosts_ = std::move(hosts); } @@ -114,6 +117,9 @@ class ExportClient { class OtlpExporterHandler : public ::opencensus::trace::exporter::SpanExporter::Handler { public: + static const int SPAN_ID_SIZE; + static const int TRACE_ID_SIZE; + OtlpExporterHandler(std::weak_ptr exporter); void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) override; From f8836bd14041e6c414eaecadb326937a408ff226 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Thu, 21 Oct 2021 12:50:49 +0800 Subject: [PATCH 112/133] Messages assign unique message identifiers in constructor and reuse them when filling send message request (#375) --- example/rocketmq/ExampleProducer.cpp | 12 ++++++------ src/main/cpp/rocketmq/ProducerImpl.cpp | 9 +++------ src/main/cpp/rocketmq/include/ProducerImpl.h | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index e3e2c8042..2fda81f5c 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -60,11 +60,6 @@ int main(int argc, char* argv[]) { producer.setResourceNamespace(resource_namespace); producer.setCredentialsProvider(std::make_shared()); - MQMessage message; - message.setTopic(topic); - message.setTags("TagA"); - message.setKey("Yuck! Why-plural?"); - std::atomic_bool stopped; std::atomic_long count(0); @@ -83,11 +78,16 @@ int main(int argc, char* argv[]) { std::string body = randomString(1024 * 4); std::cout << "Message body size: " << body.length() << std::endl; - message.setBody(body); try { producer.start(); for (int i = 0; i < 16; ++i) { + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody(body); + SendResult sendResult = producer.send(message); std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; count++; diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 4145e0aa4..034c920dc 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -106,8 +106,8 @@ bool ProducerImpl::validate(const MQMessage& message) { return MixAll::validate(message); } -std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, - const MQMessageQueue& message_queue) { +void ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue) { request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); request.mutable_message()->mutable_topic()->set_name(message.getTopic()); @@ -184,12 +184,9 @@ std::string ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendM request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); } - // Create unique message-id - std::string message_id = UniqueIdGenerator::instance().next(); - system_attribute->set_message_id(message_id); + system_attribute->set_message_id(message.getMsgId()); system_attribute->set_partition_id(message_queue.getQueueId()); SPDLOG_TRACE("SendMessageRequest: {}", request.DebugString()); - return message_id; } SendResult ProducerImpl::send(const MQMessage& message, std::error_code& ec) noexcept { diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index 678bcc3ff..e28f8a3c0 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -145,8 +145,8 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, std::vector& message_queues, int number); - std::string wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, - const MQMessageQueue& message_queue); + void wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, + const MQMessageQueue& message_queue); bool isRunning() const; From 2be2f7cbd2a8672d396ef056b1bb272ffe02e4b5 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 21 Oct 2021 12:52:58 +0800 Subject: [PATCH 113/133] Polish Scheduler implementation (#373) --- src/main/cpp/admin/AdminServerImpl.cpp | 1 + src/main/cpp/client/ClientManagerImpl.cpp | 29 ++++++------ src/main/cpp/client/include/ClientManager.h | 2 +- .../cpp/client/include/ClientManagerImpl.h | 5 +- .../client/mocks/include/ClientManagerMock.h | 2 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 4 +- src/main/cpp/rocketmq/ClientImpl.cpp | 12 ++--- .../rocketmq/DynamicNameServerResolver.cpp | 4 +- src/main/cpp/rocketmq/PushConsumerImpl.cpp | 4 +- .../include/DynamicNameServerResolver.h | 3 +- src/main/cpp/scheduler/SchedulerImpl.cpp | 44 ++++++++++++------ src/main/cpp/scheduler/include/Scheduler.h | 4 ++ .../cpp/scheduler/include/SchedulerImpl.h | 17 +++++-- src/test/cpp/ut/admin/AdminServerTest.cpp | 2 + .../ut/client/ClientManagerFactoryTest.cpp | 2 + src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 27 ++++++----- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 1 + src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 26 +++-------- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 13 ++++-- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 25 +++------- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 46 +++++++++---------- 21 files changed, 146 insertions(+), 127 deletions(-) diff --git a/src/main/cpp/admin/AdminServerImpl.cpp b/src/main/cpp/admin/AdminServerImpl.cpp index 7382b9719..ce16072cc 100644 --- a/src/main/cpp/admin/AdminServerImpl.cpp +++ b/src/main/cpp/admin/AdminServerImpl.cpp @@ -69,6 +69,7 @@ void AdminServerImpl::loop() { while (completion_queue_->Next(&tag, &ok)) { if (!ok) { + delete static_cast(tag); break; } static_cast(tag)->proceed(); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 5e37dfa24..227623d6f 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -24,6 +24,7 @@ #include #include "ReceiveMessageResult.h" +#include "Scheduler.h" #include "google/rpc/code.pb.h" #include "InvocationContext.h" @@ -46,8 +47,8 @@ ROCKETMQ_NAMESPACE_BEGIN ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) - : resource_namespace_(std::move(resource_namespace)), state_(State::CREATED), - completion_queue_(std::make_shared()), + : scheduler_(std::make_shared()), resource_namespace_(std::move(resource_namespace)), + state_(State::CREATED), completion_queue_(std::make_shared()), callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); @@ -112,7 +113,8 @@ void ClientManagerImpl::start() { state_.store(State::STARTING, std::memory_order_relaxed); callback_thread_pool_->start(); - scheduler_.start(); + + scheduler_->start(); std::weak_ptr client_instance_weak_ptr = shared_from_this(); @@ -122,8 +124,8 @@ void ClientManagerImpl::start() { client_instance->doHealthCheck(); } }; - health_check_task_id_ = scheduler_.schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), - std::chrono::seconds(5)); + health_check_task_id_ = scheduler_->schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), + std::chrono::seconds(5)); auto heartbeat_functor = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); if (client_instance) { @@ -131,7 +133,7 @@ void ClientManagerImpl::start() { } }; heartbeat_task_id_ = - scheduler_.schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); + scheduler_->schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); completion_queue_thread_ = std::thread(std::bind(&ClientManagerImpl::pollCompletionQueue, this)); @@ -142,12 +144,12 @@ void ClientManagerImpl::start() { } }; stats_task_id_ = - scheduler_.schedule(stats_functor_, STATS_TASK_NAME, std::chrono::seconds(0), std::chrono::seconds(10)); + scheduler_->schedule(stats_functor_, STATS_TASK_NAME, std::chrono::seconds(0), std::chrono::seconds(10)); state_.store(State::STARTED, std::memory_order_relaxed); } void ClientManagerImpl::shutdown() { - SPDLOG_DEBUG("Client instance shutdown"); + SPDLOG_INFO("Client manager shutdown"); if (State::STARTED != state_.load(std::memory_order_relaxed)) { SPDLOG_WARN("Unexpected client instance state: {}", state_.load(std::memory_order_relaxed)); return; @@ -157,17 +159,18 @@ void ClientManagerImpl::shutdown() { callback_thread_pool_->shutdown(); if (health_check_task_id_) { - scheduler_.cancel(health_check_task_id_); + scheduler_->cancel(health_check_task_id_); } if (heartbeat_task_id_) { - scheduler_.cancel(heartbeat_task_id_); + scheduler_->cancel(heartbeat_task_id_); } if (stats_task_id_) { - scheduler_.cancel(stats_task_id_); + scheduler_->cancel(stats_task_id_); } - scheduler_.shutdown(); + + scheduler_->shutdown(); { absl::MutexLock lk(&rpc_clients_mtx_); @@ -1065,7 +1068,7 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess return true; } -Scheduler& ClientManagerImpl::getScheduler() { +SchedulerSharedPtr ClientManagerImpl::getScheduler() { return scheduler_; } diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index 2f5223eb3..dceace394 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -42,7 +42,7 @@ class ClientManager { virtual void shutdown() = 0; - virtual Scheduler& getScheduler() = 0; + virtual SchedulerSharedPtr getScheduler() = 0; virtual std::shared_ptr createChannel(const std::string& target_host) = 0; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index f3ed5ff10..47d33909b 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -25,6 +25,7 @@ #include #include +#include "Scheduler.h" #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" @@ -145,7 +146,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share */ bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) override; - Scheduler& getScheduler() override; + SchedulerSharedPtr getScheduler() override; /** * Ack message asynchronously. @@ -224,7 +225,7 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void logStats(); - SchedulerImpl scheduler_; + SchedulerSharedPtr scheduler_; static const char* HEARTBEAT_TASK_NAME; static const char* STATS_TASK_NAME; diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 4172ab54c..01d6d41b3 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -29,7 +29,7 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, shutdown, (), (override)); - MOCK_METHOD(Scheduler&, getScheduler, (), (override)); + MOCK_METHOD(SchedulerSharedPtr, getScheduler, (), (override)); MOCK_METHOD((std::shared_ptr), createChannel, (const std::string&), (override)); diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index 3d128189d..c3429ab8c 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -97,8 +97,8 @@ void AsyncReceiveMessageCallback::receiveMessageLater() { } }; - client_instance->getScheduler().schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), - std::chrono::seconds(0)); + client_instance->getScheduler()->schedule(task, RECEIVE_LATER_TASK_NAME, std::chrono::seconds(1), + std::chrono::seconds(0)); } void AsyncReceiveMessageCallback::receiveMessageImmediately() { diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 049569ade..0d22e9593 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -76,8 +76,8 @@ void ClientImpl::start() { } }; - route_update_handle_ = client_manager_->getScheduler().schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, - std::chrono::seconds(10), std::chrono::seconds(30)); + route_update_handle_ = client_manager_->getScheduler()->schedule(route_update_functor, UPDATE_ROUTE_TASK_NAME, + std::chrono::seconds(10), std::chrono::seconds(30)); } void ClientImpl::shutdown() { @@ -85,7 +85,7 @@ void ClientImpl::shutdown() { if (state_.compare_exchange_strong(expected, State::STOPPED)) { name_server_resolver_->shutdown(); if (route_update_handle_) { - client_manager_->getScheduler().cancel(route_update_handle_); + client_manager_->getScheduler()->cancel(route_update_handle_); } client_manager_.reset(); } else { @@ -437,8 +437,8 @@ void ClientImpl::onPollCommandResponse(const InvocationContextstatus.ok()) { static std::string task_name = "Poll-Command-Later"; - client_manager_->getScheduler().schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, - std::chrono::seconds(3), std::chrono::seconds(0)); + client_manager_->getScheduler()->schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, + std::chrono::seconds(3), std::chrono::seconds(0)); return; } @@ -547,7 +547,7 @@ void ClientImpl::healthCheck() { void ClientImpl::schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) { - client_manager_->getScheduler().schedule(task, task_name, delay, std::chrono::milliseconds(0)); + client_manager_->getScheduler()->schedule(task, task_name, delay, std::chrono::milliseconds(0)); } void ClientImpl::onHealthCheckResponse(const std::error_code& ec, const InvocationContext* ctx) { diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp index d2eb40f95..b1a961481 100644 --- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -31,8 +31,8 @@ ROCKETMQ_NAMESPACE_BEGIN DynamicNameServerResolver::DynamicNameServerResolver(absl::string_view endpoint, std::chrono::milliseconds refresh_interval) - : endpoint_(endpoint.data(), endpoint.length()), refresh_interval_(refresh_interval), - scheduler_(absl::make_unique()) { + : endpoint_(endpoint.data(), endpoint.length()), scheduler_(std::make_shared(1)), + refresh_interval_(refresh_interval) { absl::string_view remains; if (absl::StartsWith(endpoint_, "https://")) { ssl_ = true; diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 49eb830f5..6691716dc 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -98,7 +98,7 @@ void PushConsumerImpl::start() { } }; - scan_assignment_handle_ = client_manager_->getScheduler().schedule( + scan_assignment_handle_ = client_manager_->getScheduler()->schedule( scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); SPDLOG_INFO("PushConsumer started, groupName={}", group_name_); @@ -110,7 +110,7 @@ void PushConsumerImpl::shutdown() { State expecting = State::STARTED; if (state_.compare_exchange_strong(expecting, State::STOPPING)) { if (scan_assignment_handle_) { - client_manager_->getScheduler().cancel(scan_assignment_handle_); + client_manager_->getScheduler()->cancel(scan_assignment_handle_); SPDLOG_DEBUG("Scan assignment periodic task cancelled"); } diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h index d6fc21e80..bac08bf77 100644 --- a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -55,6 +55,7 @@ class DynamicNameServerResolver : public NameServerResolver, private: std::string endpoint_; + SchedulerSharedPtr scheduler_; std::chrono::milliseconds refresh_interval_; void fetch(); @@ -68,8 +69,6 @@ class DynamicNameServerResolver : public NameServerResolver, bool ssl_{false}; std::unique_ptr top_addressing_; - - std::unique_ptr scheduler_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/SchedulerImpl.cpp b/src/main/cpp/scheduler/SchedulerImpl.cpp index c5c48e439..7eb4da384 100644 --- a/src/main/cpp/scheduler/SchedulerImpl.cpp +++ b/src/main/cpp/scheduler/SchedulerImpl.cpp @@ -31,22 +31,27 @@ #include "asio/executor_work_guard.hpp" #include "asio/io_context.hpp" #include "asio/steady_timer.hpp" - -#include "LoggerImpl.h" +#include "spdlog/spdlog.h" ROCKETMQ_NAMESPACE_BEGIN -SchedulerImpl::SchedulerImpl() +SchedulerImpl::SchedulerImpl(std::uint32_t worker_num) : work_guard_( - absl::make_unique>(context_.get_executor())) { - spdlog::set_level(spdlog::level::debug); + absl::make_unique>(context_.get_executor())), + worker_num_(worker_num) { +} + +SchedulerImpl::SchedulerImpl() : SchedulerImpl(std::thread::hardware_concurrency()) { +} + +SchedulerImpl::~SchedulerImpl() { + shutdown0(); } void SchedulerImpl::start() { State expected = State::CREATED; if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { - - for (unsigned int i = 0; i < std::thread::hardware_concurrency(); i++) { + for (std::uint32_t i = 0; i < worker_num_; i++) { auto worker = std::thread([this]() { { State expect = State::STARTING; @@ -72,7 +77,7 @@ void SchedulerImpl::start() { #endif if (State::STARTED != state_.load(std::memory_order_relaxed)) { - SPDLOG_INFO("A scheduler worker quit"); + SPDLOG_INFO("One scheduler worker thread quit"); break; } } @@ -91,6 +96,10 @@ void SchedulerImpl::start() { } void SchedulerImpl::shutdown() { + shutdown0(); +} + +void SchedulerImpl::shutdown0() { State expected = State::STARTED; if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { work_guard_->reset(); @@ -105,6 +114,7 @@ void SchedulerImpl::shutdown() { worker.join(); } } + threads_.clear(); state_.store(State::STOPPED); } @@ -125,9 +135,12 @@ std::uint32_t SchedulerImpl::schedule(const std::function& functor, id = ++task_id; tasks_.insert({id, task}); } - asio::steady_timer* timer = new asio::steady_timer(context_, delay); + task->task_id = id; + task->timer = absl::make_unique(context_, delay); + task->scheduler = shared_from_this(); SPDLOG_DEBUG("Timer-task[name={}] to fire in {}ms", task_name, delay.count()); - timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer, std::weak_ptr(task))); + auto timer_task_weak_ptr = std::weak_ptr(task); + task->timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer_task_weak_ptr)); return id; } @@ -143,10 +156,9 @@ void SchedulerImpl::cancel(std::uint32_t task_id) { tasks_.erase(search); } -void SchedulerImpl::execute(const asio::error_code& ec, asio::steady_timer* timer, std::weak_ptr task) { +void SchedulerImpl::execute(const asio::error_code& ec, std::weak_ptr task) { std::shared_ptr timer_task = task.lock(); if (!timer_task) { - delete timer; return; } @@ -168,11 +180,15 @@ void SchedulerImpl::execute(const asio::error_code& ec, asio::steady_timer* time #endif if (timer_task->interval.count()) { + auto& timer = timer_task->timer; timer->expires_at(timer->expiry() + timer_task->interval); - timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, timer, task)); + timer->async_wait(std::bind(&SchedulerImpl::execute, std::placeholders::_1, task)); SPDLOG_DEBUG("Repeated timer-task {} to fire in {}ms", timer_task->task_name, timer_task->interval.count()); } else { - delete timer; + auto scheduler = timer_task->scheduler.lock(); + if (scheduler) { + scheduler->cancel(timer_task->task_id); + } } } diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index 34d676bde..c280ea8ef 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "rocketmq/RocketMQ.h" @@ -39,4 +40,7 @@ class Scheduler { virtual void cancel(std::uint32_t task_id) = 0; }; +using SchedulerPtr = std::weak_ptr; +using SchedulerSharedPtr = std::shared_ptr; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h index 14b5fd254..bcd63d9e5 100644 --- a/src/main/cpp/scheduler/include/SchedulerImpl.h +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -16,6 +16,7 @@ */ #pragma once #include +#include #include #include #include @@ -34,16 +35,21 @@ ROCKETMQ_NAMESPACE_BEGIN struct TimerTask { + std::uint32_t task_id; + std::string task_name; std::function callback; std::chrono::milliseconds interval; - std::string task_name; + std::unique_ptr timer; + SchedulerPtr scheduler; }; -class SchedulerImpl : public Scheduler { +class SchedulerImpl : public std::enable_shared_from_this, public Scheduler { public: SchedulerImpl(); - ~SchedulerImpl() override = default; + explicit SchedulerImpl(std::uint32_t worker_num); + + ~SchedulerImpl() override; void start() override; @@ -70,13 +76,16 @@ class SchedulerImpl : public Scheduler { std::unique_ptr> work_guard_; absl::Mutex start_mtx_; absl::CondVar start_cv_; + std::uint32_t worker_num_{std::thread::hardware_concurrency()}; std::vector threads_; std::atomic state_{State::CREATED}; absl::flat_hash_map> tasks_ GUARDED_BY(tasks_mtx_); absl::Mutex tasks_mtx_; - static void execute(const asio::error_code& ec, asio::steady_timer* timer, std::weak_ptr task); + static void execute(const asio::error_code& ec, std::weak_ptr task); + + void shutdown0(); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp index e88e00dad..eb988cc64 100644 --- a/src/test/cpp/ut/admin/AdminServerTest.cpp +++ b/src/test/cpp/ut/admin/AdminServerTest.cpp @@ -59,6 +59,8 @@ TEST(AdminServerTest, testSetUp) { EXPECT_EQ(spdlog::level::info, logger->level()); admin_server->stop(); + + delete admin_server; } } // namespace admin diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp index d63cabf5a..e48200e41 100644 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp @@ -40,6 +40,8 @@ TEST_F(ClientManagerFactoryTest, testGetClientManager) { ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); EXPECT_TRUE(client_manager); client_manager->start(); + + client_manager->shutdown(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp index 561d26c62..f93fbec0c 100644 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp @@ -24,6 +24,7 @@ #include "DynamicNameServerResolver.h" #include "HttpClientMock.h" #include "NameServerResolverMock.h" +#include "Scheduler.h" #include "SchedulerImpl.h" #include "TopAddressing.h" #include "rocketmq/RocketMQ.h" @@ -49,18 +50,23 @@ class ClientImplTest : public testing::Test { public: void SetUp() override { grpc_init(); + scheduler_ = std::make_shared(); + + http_client_ = absl::make_unique>(); name_server_resolver_ = std::make_shared(endpoint_, std::chrono::seconds(1)); - scheduler_.start(); client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); + + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); + ON_CALL(*client_manager_, start).WillByDefault([&]() { scheduler_->start(); }); + ON_CALL(*client_manager_, shutdown).WillByDefault([&]() { scheduler_->shutdown(); }); + client_ = std::make_shared(group_); client_->withNameServerResolver(name_server_resolver_); } void TearDown() override { grpc_shutdown(); - scheduler_.shutdown(); } protected: @@ -68,15 +74,13 @@ class ClientImplTest : public testing::Test { std::string resource_namespace_{"mq://test"}; std::string group_{"Group-0"}; std::shared_ptr> client_manager_; - SchedulerImpl scheduler_; + SchedulerSharedPtr scheduler_; std::shared_ptr client_; std::shared_ptr name_server_resolver_; + std::unique_ptr> http_client_; }; TEST_F(ClientImplTest, testBasic) { - - auto http_client = absl::make_unique(); - std::string once{"10.0.0.1:9876"}; std::string then{"10.0.0.1:9876;10.0.0.2:9876"}; std::multimap header; @@ -100,8 +104,8 @@ TEST_F(ClientImplTest, testBasic) { cv.SignalAll(); }; - EXPECT_CALL(*http_client, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); - name_server_resolver_->injectHttpClient(std::move(http_client)); + EXPECT_CALL(*http_client_, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); + name_server_resolver_->injectHttpClient(std::move(http_client_)); client_->resourceNamespace(resource_namespace_); client_->start(); @@ -111,9 +115,10 @@ TEST_F(ClientImplTest, testBasic) { cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); } } - EXPECT_TRUE(completed); - // Now that the derivative class has closed its resources, state of ClientImpl should be STOPPING. + ASSERT_TRUE(completed); + + // Now that the derivative class has closed its own resources, state of ClientImpl should be STOPPING. client_->state(State::STOPPING); client_->shutdown(); } diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp index 7aab28175..47a17ae06 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp @@ -174,6 +174,7 @@ TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { } ASSERT_EQ(msg_id, message_id_); producer->shutdown(); + delete send_callback; } TEST_F(DefaultMQProducerUnitTest, testSendMessage) { diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp index 5bf670cfb..5b990524d 100644 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp @@ -20,6 +20,7 @@ #include "ClientManagerFactory.h" #include "ClientManagerMock.h" #include "ProducerImpl.h" +#include "Scheduler.h" #include "SchedulerImpl.h" #include "StaticNameServerResolver.h" #include "TopicRouteData.h" @@ -38,8 +39,11 @@ class ProducerImplTest : public testing::Test { void SetUp() override { grpc_init(); + scheduler_ = std::make_shared(); + scheduler_->start(); name_server_resolver_ = std::make_shared(name_server_list_); client_manager_ = std::make_shared>(); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); producer_ = std::make_shared(group_); producer_->resourceNamespace(resource_namespace_); @@ -60,10 +64,12 @@ class ProducerImplTest : public testing::Test { } void TearDown() override { + scheduler_->shutdown(); grpc_shutdown(); } protected: + SchedulerSharedPtr scheduler_; std::shared_ptr> client_manager_; std::shared_ptr producer_; std::string name_server_list_{"10.0.0.1:9876"}; @@ -87,18 +93,11 @@ class ProducerImplTest : public testing::Test { }; TEST_F(ProducerImplTest, testStartShutdown) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); producer_->start(); producer_->shutdown(); - scheduler.shutdown(); } TEST_F(ProducerImplTest, testSend) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -129,13 +128,9 @@ TEST_F(ProducerImplTest, testSend) { EXPECT_FALSE(ec); EXPECT_TRUE(cb_invoked); producer_->shutdown(); - scheduler.shutdown(); } TEST_F(ProducerImplTest, testSend_WithMessageGroup) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -167,13 +162,9 @@ TEST_F(ProducerImplTest, testSend_WithMessageGroup) { EXPECT_FALSE(ec); EXPECT_TRUE(cb_invoked); producer_->shutdown(); - scheduler.shutdown(); } TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -211,7 +202,6 @@ TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { EXPECT_TRUE(cb_invoked); producer_->shutdown(); - scheduler.shutdown(); } class TestSendCallback : public SendCallback { @@ -237,9 +227,6 @@ class TestSendCallback : public SendCallback { }; TEST_F(ProducerImplTest, testAsyncSend) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); auto mock_resolve_route = [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, @@ -280,7 +267,6 @@ TEST_F(ProducerImplTest, testAsyncSend) { } EXPECT_TRUE(cb_invoked); - scheduler.shutdown(); producer_->shutdown(); } diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp index a71451981..729aac0ee 100644 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp @@ -40,12 +40,12 @@ class PullConsumerImplTest : public testing::Test { public: void SetUp() override { grpc_init(); - + scheduler_ = std::make_shared(); name_server_resolver_ = std::make_shared(name_server_list_); - scheduler_.start(); + scheduler_->start(); client_manager_ = std::make_shared>(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler_)); + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); pull_consumer_ = std::make_shared(group_); @@ -67,7 +67,7 @@ class PullConsumerImplTest : public testing::Test { void TearDown() override { grpc_shutdown(); - scheduler_.shutdown(); + scheduler_->shutdown(); } protected: @@ -79,7 +79,7 @@ class PullConsumerImplTest : public testing::Test { std::string tag_{"TagB"}; std::shared_ptr> client_manager_; std::shared_ptr pull_consumer_; - SchedulerImpl scheduler_; + SchedulerSharedPtr scheduler_; std::string broker_name_{"broker-a"}; int broker_id_{0}; std::string message_body_{"Message Body Content"}; @@ -190,6 +190,7 @@ TEST_F(PullConsumerImplTest, testPull) { EXPECT_FALSE(failure); pull_consumer_->shutdown(); + delete pull_callback; } TEST_F(PullConsumerImplTest, testPull_gRPC_error) { @@ -237,6 +238,7 @@ TEST_F(PullConsumerImplTest, testPull_gRPC_error) { EXPECT_FALSE(success); pull_consumer_->shutdown(); + delete pull_callback; } TEST_F(PullConsumerImplTest, testPull_biz_error) { @@ -285,6 +287,7 @@ TEST_F(PullConsumerImplTest, testPull_biz_error) { EXPECT_TRUE(failure); pull_consumer_->shutdown(); + delete pull_callback; } TEST_F(PullConsumerImplTest, testQueryOffset) { diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp index bf83bdaf5..b84dbea60 100644 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp @@ -24,6 +24,7 @@ #include "InvocationContext.h" #include "MessageAccessor.h" #include "PushConsumerImpl.h" +#include "Scheduler.h" #include "StaticNameServerResolver.h" #include "grpc/grpc.h" #include "rocketmq/MQMessageExt.h" @@ -45,22 +46,26 @@ class PushConsumerImplTest : public testing::Test { void SetUp() override { grpc_init(); - + scheduler_ = std::make_shared(); name_server_resolver_ = std::make_shared(name_server_list_); - client_manager_ = std::make_shared>(); ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); push_consumer_ = std::make_shared(group_); push_consumer_->resourceNamespace(resource_namespace_); push_consumer_->withNameServerResolver(name_server_resolver_); push_consumer_->registerMessageListener(message_listener_.get()); + scheduler_->start(); + + ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); } void TearDown() override { + scheduler_->shutdown(); grpc_shutdown(); } protected: + SchedulerSharedPtr scheduler_; std::string name_server_list_{"10.0.0.1:9876"}; std::shared_ptr name_server_resolver_; std::string resource_namespace_{"mq://test"}; @@ -77,10 +82,6 @@ class PushConsumerImplTest : public testing::Test { }; TEST_F(PushConsumerImplTest, testAck) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb) { std::error_code ec; @@ -118,14 +119,9 @@ TEST_F(PushConsumerImplTest, testAck) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); - scheduler.shutdown(); } TEST_F(PushConsumerImplTest, testNack) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb) { std::error_code ec; @@ -161,14 +157,9 @@ TEST_F(PushConsumerImplTest, testNack) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); - scheduler.shutdown(); } TEST_F(PushConsumerImplTest, testForward) { - SchedulerImpl scheduler; - scheduler.start(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::ReturnRef(scheduler)); - InvocationContext invocation_context; auto forward_cb = @@ -201,7 +192,6 @@ TEST_F(PushConsumerImplTest, testForward) { message.setDelayTimeLevel(delay_level_); push_consumer_->forwardToDeadLetterQueue(message, callback); - { absl::MutexLock lk(&mtx); if (!completed) { @@ -210,7 +200,6 @@ TEST_F(PushConsumerImplTest, testForward) { } EXPECT_TRUE(completed); push_consumer_->shutdown(); - scheduler.shutdown(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 6e223e314..741d831c4 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "SchedulerImpl.h" @@ -28,15 +29,22 @@ ROCKETMQ_NAMESPACE_BEGIN class SchedulerTest : public testing::Test { public: + SchedulerTest() : scheduler(std::make_shared()) { + } + void SetUp() override { + scheduler->start(); + } + + void TearDown() override { + scheduler->shutdown(); } protected: + SchedulerSharedPtr scheduler; }; TEST_F(SchedulerTest, testSingleShot) { - SchedulerImpl scheduler; - scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; @@ -46,7 +54,7 @@ TEST_F(SchedulerTest, testSingleShot) { callback_fire_count++; }; - scheduler.schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); + scheduler->schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); // Wait till callback is executed. { @@ -55,13 +63,9 @@ TEST_F(SchedulerTest, testSingleShot) { cv.Wait(&mtx); } } - - scheduler.shutdown(); } TEST_F(SchedulerTest, testCancel) { - SchedulerImpl scheduler; - scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; @@ -71,16 +75,13 @@ TEST_F(SchedulerTest, testCancel) { callback_fire_count++; }; - std::uint32_t task_id = scheduler.schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); - scheduler.cancel(task_id); + std::uint32_t task_id = scheduler->schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); + scheduler->cancel(task_id); std::this_thread::sleep_for(std::chrono::seconds(2)); ASSERT_EQ(0, callback_fire_count); - scheduler.shutdown(); } TEST_F(SchedulerTest, testPeriodicShot) { - SchedulerImpl scheduler; - scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; @@ -91,17 +92,14 @@ TEST_F(SchedulerTest, testPeriodicShot) { }; std::uintptr_t task_id = - scheduler.schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::seconds(1)); + scheduler->schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::seconds(1)); // Wait till callback is executed. std::this_thread::sleep_for(std::chrono::seconds(5)); ASSERT_TRUE(callback_fire_count >= 4); - scheduler.cancel(task_id); - scheduler.shutdown(); + scheduler->cancel(task_id); } TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { - SchedulerImpl scheduler; - scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; @@ -111,7 +109,7 @@ TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { callback_fire_count++; }; - scheduler.schedule(callback, "single-shot-with-0-delay", std::chrono::milliseconds(0), std::chrono::milliseconds(0)); + scheduler->schedule(callback, "single-shot-with-0-delay", std::chrono::milliseconds(0), std::chrono::milliseconds(0)); // Wait till callback is executed. { @@ -120,13 +118,9 @@ TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { cv.Wait(&mtx); } } - - scheduler.shutdown(); } TEST_F(SchedulerTest, testException) { - SchedulerImpl scheduler; - scheduler.start(); absl::Mutex mtx; absl::CondVar cv; int callback_fire_count{0}; @@ -141,7 +135,7 @@ TEST_F(SchedulerTest, testException) { throw e; }; - scheduler.schedule(callback, "test-exception", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); + scheduler->schedule(callback, "test-exception", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); // Wait till callback is executed. { @@ -152,7 +146,11 @@ TEST_F(SchedulerTest, testException) { } std::this_thread::sleep_for(std::chrono::seconds(3)); +} - scheduler.shutdown(); +TEST(SchedulerLifeCycleTest, testLifeCycle) { + auto scheduler = std::make_shared(); + scheduler->start(); } + ROCKETMQ_NAMESPACE_END \ No newline at end of file From 3b3ef00ef23c1ed9253f363a92db05f8e7e46558 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 21 Oct 2021 16:41:26 +0800 Subject: [PATCH 114/133] Update README.md Fix badge url --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 93c2bfc8d..7ce243853 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CI](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml/badge.svg)](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml) +[![CI](https://github.com/apache/rocketmq-client-cpp/actions/workflows/main.yml/badge.svg)](https://github.com/lizhanhui/rocketmq-client-cpp/actions/workflows/main.yml) ### Introduction Apache RocketMQ supports two styles of APIs to acknowledge messages once they are successfully processed. @@ -89,4 +89,4 @@ if "com_google_googletest" not in native.existing_rules(): 2. CLion + Bazel Plugin - Bazel also has a plugin for CLion. \ No newline at end of file + Bazel also has a plugin for CLion. From 9555dae37c08d9cf1139da27cb6c1aba9f3c6e8f Mon Sep 17 00:00:00 2001 From: aaron ai Date: Thu, 21 Oct 2021 18:41:13 +0800 Subject: [PATCH 115/133] Update rocketmq trace attributes key name (#376) --- src/main/cpp/base/MixAll.cpp | 54 ++++++++++---- src/main/cpp/base/include/MixAll.h | 51 ++++++++++---- src/main/cpp/client/ClientManagerImpl.cpp | 4 +- src/main/cpp/rocketmq/BUILD.bazel | 1 + .../ConsumeStandardMessageService.cpp | 42 +++++------ src/main/cpp/rocketmq/ProducerImpl.cpp | 41 ++++++----- src/main/cpp/scheduler/include/Scheduler.h | 2 +- .../cpp/scheduler/include/SchedulerImpl.h | 1 - src/main/cpp/tracing/BUILD.bazel | 29 ++++++++ src/main/cpp/tracing/TracingUtility.cpp | 70 +++++++++++++++++++ .../cpp/tracing/exporters/OtlpExporter.cpp | 19 ++++- src/main/cpp/tracing/include/TracingUtility.h | 31 ++++++++ src/test/cpp/ut/scheduler/SchedulerTest.cpp | 7 +- 13 files changed, 273 insertions(+), 79 deletions(-) create mode 100644 src/main/cpp/tracing/BUILD.bazel create mode 100644 src/main/cpp/tracing/TracingUtility.cpp create mode 100644 src/main/cpp/tracing/include/TracingUtility.h diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 9a86e3380..70064102a 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -73,21 +73,45 @@ const char* MixAll::SPAN_NAME_AWAIT_CONSUMPTION = "AwaitingConsumption"; const char* MixAll::SPAN_NAME_CONSUME_MESSAGE = "ConsumeMessage"; const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; -// Span attribute name list -const char* MixAll::SPAN_ATTRIBUTE_ACCESS_KEY = "ak"; -const char* MixAll::SPAN_ATTRIBUTE_ARN = "setResourceNamespace"; -const char* MixAll::SPAN_ATTRIBUTE_KEYS = "keys"; -const char* MixAll::SPAN_ATTRIBUTE_MESSAGE_TYPE = "msgType"; -const char* MixAll::SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP = "deliveryTimestamp"; -const char* MixAll::SPAN_ATTRIBUTE_TOPIC = "topic"; -const char* MixAll::SPAN_ATTRIBUTE_GROUP = "group"; -const char* MixAll::SPAN_ATTRIBUTE_MESSAGE_ID = "msgId"; -const char* MixAll::SPAN_ATTRIBUTE_TAG = "tags"; -const char* MixAll::SPAN_ATTRIBUTE_HOST = "host"; -const char* MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME = "attempt"; -const char* MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION = "commitAction"; -const char* MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP = "availableTimestamp"; -const char* MixAll::SPAN_ATTRIBUTE_BATCH_SIZE = "batchSize"; +// Span attributes follows to the opentelemetry specification, refers to: +// https://github.com/open-telemetry/opentelemetry-specification + +// RocketMQ span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE = "messaging.rocketmq.namespace"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG = "messaging.rocketmq.tag"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS = "messaging.rocketmq.keys"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID = "messaging.rocketmq.client_id"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE = "messaging.rocketmq.message_type"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP = "messaging.rocketmq.client_group"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT = "messaging.rocketmq.attempt"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE = "messaging.rocketmq.batch_size"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP = "messaging.rocketmq.delivery_timestamp"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP = "messaging.rocketmq.available_timestamp"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY = "messaging.rocketmq.access_key"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM = "rocketmq"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND = "topic"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL = "RMQ-gRPC"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION = "v1"; + +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE = "normal"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE = "fifo"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE = "delay"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE = "transaction"; + +// Messaging span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM = "messaging.system"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION = "messaging.destination"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND = "messaging.destination_kind"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL = "messaging.protocol"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION = "messaging.protocol_version"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL = "messaging.url"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID = "messaging.message_id"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES = "messaging.message_payload_size_bytes"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION = "messaging.operation"; + +const char* MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME = "host.name"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION = "commitAction"; // Span annotation const char* MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION = "__await_consumption"; diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 1a84c66bc..9a82b3039 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -68,21 +68,42 @@ class MixAll { static const char* SPAN_NAME_CONSUME_MESSAGE; static const char* SPAN_NAME_PULL_MESSAGE; - // Tracing attribute name list - static const char* SPAN_ATTRIBUTE_ACCESS_KEY; - static const char* SPAN_ATTRIBUTE_ARN; - static const char* SPAN_ATTRIBUTE_TOPIC; - static const char* SPAN_ATTRIBUTE_GROUP; - static const char* SPAN_ATTRIBUTE_MESSAGE_ID; - static const char* SPAN_ATTRIBUTE_TAG; - static const char* SPAN_ATTRIBUTE_KEYS; - static const char* SPAN_ATTRIBUTE_HOST; - static const char* SPAN_ATTRIBUTE_MESSAGE_TYPE; - static const char* SPAN_ATTRIBUTE_ATTEMPT_TIME; - static const char* SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP; - static const char* SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION; - static const char* SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP; - static const char* SPAN_ATTRIBUTE_BATCH_SIZE; + // RocketMQ span attribute name list + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP; + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY; + + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM; + static const char* SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION; + + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE; + + // Messaging attribute name list + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_URL; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_ID; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES; + static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION; + + static const char* SPAN_ATTRIBUTE_KEY_HOST_NAME; + static const char* SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION; // Tracing annotation static const char* SPAN_ANNOTATION_AWAIT_CONSUMPTION; diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 227623d6f..db77dc838 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -113,7 +113,7 @@ void ClientManagerImpl::start() { state_.store(State::STARTING, std::memory_order_relaxed); callback_thread_pool_->start(); - + scheduler_->start(); std::weak_ptr client_instance_weak_ptr = shared_from_this(); @@ -169,7 +169,7 @@ void ClientManagerImpl::shutdown() { if (stats_task_id_) { scheduler_->cancel(stats_task_id_); } - + scheduler_->shutdown(); { diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index 20cfe8c8e..6d6dd7296 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -26,6 +26,7 @@ cc_library( deps = [ "//src/main/cpp/client:client_library", "//src/main/cpp/tracing/exporters:otlp_exporter", + "//src/main/cpp/tracing:tracing_utility", "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", "@com_google_absl//absl/types:optional", diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 4376afdf4..b13b34ac0 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -146,20 +146,20 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_AWAIT_CONSUMPTION, nullptr, {&Samplers::always()}); } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, msg.getTags()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, consumer->resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, msg.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, msg.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, consumer->getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, msg.getTags()); const auto& keys = msg.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, msg.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); span.AddAnnotation( MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, @@ -180,21 +180,21 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce } else { span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_CONSUME_MESSAGE); } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, consumer->resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, msg.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, msg.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, consumer->getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, msg.getTags()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, consumer->resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, msg.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, msg.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, consumer->getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, msg.getTags()); const auto& keys = msg.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, msg.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_AVAILABLE_TIMESTAMP, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_BATCH_SIZE, msgs.size()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); spans.emplace_back(std::move(span)); } } diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 034c920dc..5b51fcdfd 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -37,6 +37,7 @@ #include "SendCallbacks.h" #include "SendMessageContext.h" #include "Signature.h" +#include "TracingUtility.h" #include "TransactionImpl.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" @@ -326,24 +327,26 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { } else { span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_SEND_MESSAGE, nullptr, {&Samplers::always()}); } - - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TOPIC, message.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TAG, message.getTags()); + TracingUtility::addUniversalSpanAttributes(message, *this, span); + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, + credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); const auto& keys = callback->message().getKeys(); if (!keys.empty()) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEYS, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); } // Note: attempt-time is 0-based - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ATTEMPT_TIME, 1 + callback->attemptTime()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_DELIVERY_TIMESTAMP, + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); } callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); @@ -413,18 +416,18 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& } else { span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_END_TRANSACTION, nullptr, {&Samplers::always()}); } - - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ACCESS_KEY, credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_ARN, resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_GROUP, getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_MESSAGE_ID, message_id); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_HOST, UtilAll::hostname()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, + credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message_id); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); switch (resolution) { case TransactionState::COMMIT: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "commit"); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "commit"); break; case TransactionState::ROLLBACK: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_TRANSACTION_RESOLUTION, "rollback"); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "rollback"); break; } diff --git a/src/main/cpp/scheduler/include/Scheduler.h b/src/main/cpp/scheduler/include/Scheduler.h index c280ea8ef..65feabb85 100644 --- a/src/main/cpp/scheduler/include/Scheduler.h +++ b/src/main/cpp/scheduler/include/Scheduler.h @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include "rocketmq/RocketMQ.h" diff --git a/src/main/cpp/scheduler/include/SchedulerImpl.h b/src/main/cpp/scheduler/include/SchedulerImpl.h index bcd63d9e5..00066eb4b 100644 --- a/src/main/cpp/scheduler/include/SchedulerImpl.h +++ b/src/main/cpp/scheduler/include/SchedulerImpl.h @@ -16,7 +16,6 @@ */ #pragma once #include -#include #include #include #include diff --git a/src/main/cpp/tracing/BUILD.bazel b/src/main/cpp/tracing/BUILD.bazel new file mode 100644 index 000000000..0fdb6b095 --- /dev/null +++ b/src/main/cpp/tracing/BUILD.bazel @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "tracing_utility", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/tracing/include", + deps = [ + "//api:rocketmq_interface", + "//src/main/cpp/client:client_library", + "@io_opencensus_cpp//opencensus/trace", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/src/main/cpp/tracing/TracingUtility.cpp b/src/main/cpp/tracing/TracingUtility.cpp new file mode 100644 index 000000000..25805db55 --- /dev/null +++ b/src/main/cpp/tracing/TracingUtility.cpp @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TracingUtility.h" +#include "MixAll.h" +#include "rocketmq/CredentialsProvider.h" +#include "spdlog/spdlog.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, ClientConfig& client_config, + opencensus::trace::Span& span) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES, message.getBody().length()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); + switch (message.messageType()) { + case MessageType::FIFO: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE); + break; + case MessageType::DELAY: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE); + break; + case MessageType::TRANSACTION: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE); + break; + default: + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE); + break; + } + std::chrono::system_clock::time_point timestamp = message.deliveryTimestamp(); + auto duration = absl::FromChrono(timestamp.time_since_epoch()); + int64_t timestamp_millis = absl::ToInt64Milliseconds(duration); + if (timestamp_millis > 0) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, timestamp_millis); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND, + MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL, "abc") + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID, client_config.clientId()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, client_config.getGroupName()); + + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, + client_config.credentialsProvider()->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 836ceff84..78aaa264d 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -153,8 +153,8 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter case TraceMode::Develop: { { for (const auto& span : spans) { - SPDLOG_INFO("{} --> {}: {}", absl::FormatTime(span.start_time()), absl::FormatTime(span.end_time()), - span.name().data()); + SPDLOG_INFO("trace span {} --> {}: {}", absl::FormatTime(span.start_time()), + absl::FormatTime(span.end_time()), span.name().data()); for (const auto& event : span.annotations().events()) { for (const auto& attr : event.event().attributes()) { switch (attr.second.type()) { @@ -173,6 +173,21 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } } } + SPDLOG_INFO("Attributes size={}", span.attributes().size()); + for (const auto& attribute : span.attributes()) { + switch (attribute.second.type()) { + case opencensus::trace::AttributeValueRef::Type::kString: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.string_value()); + break; + break; + case opencensus::trace::AttributeValueRef::Type::kBool: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.bool_value()); + break; + case opencensus::trace::AttributeValueRef::Type::kInt: + SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.int_value()); + break; + } + } } } return; diff --git a/src/main/cpp/tracing/include/TracingUtility.h b/src/main/cpp/tracing/include/TracingUtility.h new file mode 100644 index 000000000..f97478068 --- /dev/null +++ b/src/main/cpp/tracing/include/TracingUtility.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/trace/span.h" + +#include "ClientConfig.h" +#include "rocketmq/MQMessage.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class TracingUtility { +public: + static void addUniversalSpanAttributes(const MQMessage& message, ClientConfig&, opencensus::trace::Span& span); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 741d831c4..87b275342 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -29,7 +29,7 @@ ROCKETMQ_NAMESPACE_BEGIN class SchedulerTest : public testing::Test { public: - SchedulerTest() : scheduler(std::make_shared()) { + SchedulerTest() : scheduler(std::make_shared()) { } void SetUp() override { @@ -41,7 +41,7 @@ class SchedulerTest : public testing::Test { } protected: - SchedulerSharedPtr scheduler; + SchedulerSharedPtr scheduler; }; TEST_F(SchedulerTest, testSingleShot) { @@ -75,7 +75,8 @@ TEST_F(SchedulerTest, testCancel) { callback_fire_count++; }; - std::uint32_t task_id = scheduler->schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); + std::uint32_t task_id = + scheduler->schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); scheduler->cancel(task_id); std::this_thread::sleep_for(std::chrono::seconds(2)); ASSERT_EQ(0, callback_fire_count); From bffad4f08a67a876441bdc7d68b6b1d74f42ddf2 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Fri, 22 Oct 2021 14:13:24 +0800 Subject: [PATCH 116/133] Implmenet trace for message sending (#377) --- src/main/cpp/base/MixAll.cpp | 23 +++ src/main/cpp/base/include/MixAll.h | 23 +++ src/main/cpp/client/TracingUtility.cpp | 154 ------------------ src/main/cpp/rocketmq/ProducerImpl.cpp | 20 +-- src/main/cpp/tracing/TracingUtility.cpp | 1 + .../cpp/tracing/exporters/OtlpExporter.cpp | 26 ++- 6 files changed, 80 insertions(+), 167 deletions(-) delete mode 100644 src/main/cpp/client/TracingUtility.cpp diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 70064102a..93d2d38af 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -66,6 +66,13 @@ const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; const char* MixAll::MESSAGE_KEY_SEPARATOR = " "; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE = "cpp"; + +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME = "host.name"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME = "service.name"; +const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME = "rocketmq-client"; + // Span name list const char* MixAll::SPAN_NAME_SEND_MESSAGE = "SendMessage"; const char* MixAll::SPAN_NAME_END_TRANSACTION = "EndTransaction"; @@ -77,6 +84,7 @@ const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; // https://github.com/open-telemetry/opentelemetry-specification // RocketMQ span attribute name list +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION = "messaging.rocketmq.operation"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE = "messaging.rocketmq.namespace"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG = "messaging.rocketmq.tag"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS = "messaging.rocketmq.keys"; @@ -99,6 +107,17 @@ const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE = "fifo"; const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE = "delay"; const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE = "transaction"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION = "send"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_RECEIVE_OPERATION = "receive"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PULL_OPERATION = "pull"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION = "await"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION = "process"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ACK_OPERATION = "ack"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NACK_OPERATION = "nack"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION = "commit"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION = "rollback"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DLQ_OPERATION = "dlq"; + // Messaging span attribute name list const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM = "messaging.system"; const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION = "messaging.destination"; @@ -110,6 +129,10 @@ const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID = "messaging.message_id"; const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES = "messaging.message_payload_size_bytes"; const char* MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION = "messaging.operation"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION = "send"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION = "receive"; +const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION = "process"; + const char* MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME = "host.name"; const char* MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION = "commitAction"; diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 9a82b3039..8a47ecf29 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -61,6 +61,13 @@ class MixAll { static const char* MESSAGE_KEY_SEPARATOR; + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE; + static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE; + + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME; + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME; + static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME; + // Tracing span name list static const char* SPAN_NAME_SEND_MESSAGE; static const char* SPAN_NAME_END_TRANSACTION; @@ -69,6 +76,7 @@ class MixAll { static const char* SPAN_NAME_PULL_MESSAGE; // RocketMQ span attribute name list + static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION; static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE; static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG; static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS; @@ -91,6 +99,17 @@ class MixAll { static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE; static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_RECEIVE_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PULL_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DLQ_OPERATION; + // Messaging attribute name list static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM; static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION; @@ -102,6 +121,10 @@ class MixAll { static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES; static const char* SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION; + static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION; + static const char* SPAN_ATTRIBUTE_KEY_HOST_NAME; static const char* SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION; diff --git a/src/main/cpp/client/TracingUtility.cpp b/src/main/cpp/client/TracingUtility.cpp deleted file mode 100644 index b2f216123..000000000 --- a/src/main/cpp/client/TracingUtility.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifdef ENABLE_TRACING -#include "TracingUtility.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -TracingUtility& TracingUtility::get() { - static TracingUtility tracing_utility; - return tracing_utility; -} - -const std::string TracingUtility::INVALID_TRACE_ID = "00000000000000000000000000000000"; -const std::string TracingUtility::INVALID_SPAN_ID = "0000000000000000"; - -uint8_t TracingUtility::hexToInt(char c) { - if (c >= '0' && c <= '9') { - return (int)(c - '0'); - } else if (c >= 'a' && c <= 'f') { - return (int)(c - 'a' + 10); - } else if (c >= 'A' && c <= 'F') { - return (int)(c - 'A' + 10); - } else { - return -1; - } -} - -void TracingUtility::generateHexFromString(const std::string& string, int bytes, uint8_t* buf) { - const char* str_id = string.data(); - for (int i = 0; i < bytes; i++) { - int tmp = hexToInt(str_id[i]); - if (tmp < 0) { - for (int j = 0; j < bytes / 2; j++) { - buf[j] = 0; - } - return; - } - if (i % 2 == 0) { - buf[i / 2] = tmp * 16; - } else { - buf[i / 2] += tmp; - } - } -} - -trace::TraceId TracingUtility::generateTraceIdFromString(const std::string& trace_id) { - int trace_id_len = kHeaderElementLengths[1]; - uint8_t buf[kTraceIdBytes / 2]; - uint8_t* b_ptr = buf; - TracingUtility::generateHexFromString(trace_id, trace_id_len, b_ptr); - return trace::TraceId(buf); -} - -bool TracingUtility::isValidHex(const std::string& str) { - for (char i : str) { - if (!(i >= '0' && i <= '9') && !(i >= 'a' && i <= 'f')) { - return false; - } - } - return true; -} - -trace::SpanId TracingUtility::generateSpanIdFromString(const std::string& span_id) { - int span_id_len = kHeaderElementLengths[2]; - uint8_t buf[kSpanIdBytes / 2]; - uint8_t* b_ptr = buf; - generateHexFromString(span_id, span_id_len, b_ptr); - return trace::SpanId(buf); -} - -trace::TraceFlags TracingUtility::generateTraceFlagsFromString(std::string trace_flags) { - if (trace_flags.length() > 2) { - return trace::TraceFlags(0); // check for invalid length of flags - } - int tmp1 = hexToInt(trace_flags[0]); - int tmp2 = hexToInt(trace_flags[1]); - if (tmp1 < 0 || tmp2 < 0) { - return trace::TraceFlags(0); // check for invalid char - } - uint8_t buf = tmp1 * 16 + tmp2; - return trace::TraceFlags(buf); -} - -std::string TracingUtility::injectSpanContextToTraceParent(const trace::SpanContext& span_context) { - char trace_id[32]; - trace::TraceId(span_context.trace_id()).ToLowerBase16(trace_id); - char span_id[16]; - trace::SpanId(span_context.span_id()).ToLowerBase16(span_id); - char trace_flags[2]; - trace::TraceFlags(span_context.trace_flags()).ToLowerBase16(trace_flags); - // Note: This is only temporary replacement for appendable string - std::string hex_string = "00-"; - for (char i : trace_id) { - hex_string.push_back(i); - } - hex_string.push_back('-'); - for (char i : span_id) { - hex_string.push_back(i); - } - hex_string.push_back('-'); - for (char trace_flag : trace_flags) { - hex_string.push_back(trace_flag); - } - return hex_string; -} - -// Assumed that all span context is remote. -trace::SpanContext TracingUtility::extractContextFromTraceParent(const std::string& trace_parent) { - if (trace_parent.length() != kHeaderSize || trace_parent[kHeaderElementLengths[0]] != '-' || - trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + 1] != '-' || - trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + 2] != '-') { - // Unresolvable trace_parent header. Returning INVALID span context. - return trace::SpanContext(false, false); - } - std::string version = trace_parent.substr(0, kHeaderElementLengths[0]); - std::string trace_id = trace_parent.substr(kHeaderElementLengths[0] + 1, kHeaderElementLengths[1]); - std::string span_id = - trace_parent.substr(kHeaderElementLengths[0] + kHeaderElementLengths[1] + 2, kHeaderElementLengths[2]); - std::string trace_flags = - trace_parent.substr(kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + 3); - - if (version == "ff" || trace_id == INVALID_TRACE_ID || span_id == INVALID_SPAN_ID) { - return trace::SpanContext(false, false); - } - - // validate ids - if (!TracingUtility::isValidHex(version) || !TracingUtility::isValidHex(trace_id) || - !TracingUtility::isValidHex(span_id) || !TracingUtility::isValidHex(trace_flags)) { - return trace::SpanContext(false, false); - } - - trace::TraceId trace_id_obj = TracingUtility::generateTraceIdFromString(trace_id); - trace::SpanId span_id_obj = TracingUtility::generateSpanIdFromString(span_id); - trace::TraceFlags trace_flags_obj = generateTraceFlagsFromString(trace_flags); - return trace::SpanContext(trace_id_obj, span_id_obj, trace_flags_obj, true); -} - -ROCKETMQ_NAMESPACE_END -#endif \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 5b51fcdfd..870bf4813 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -321,21 +321,18 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = + resourceNamespace() + "/" + message.getTopic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_SEND_MESSAGE, span_context, - {&Samplers::always()}); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); } else { - span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_SEND_MESSAGE, nullptr, {&Samplers::always()}); + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); TracingUtility::addUniversalSpanAttributes(message, *this, span); - - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, - credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); const auto& keys = callback->message().getKeys(); if (!keys.empty()) { span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, @@ -343,7 +340,6 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { } // Note: attempt-time is 0-based span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, diff --git a/src/main/cpp/tracing/TracingUtility.cpp b/src/main/cpp/tracing/TracingUtility.cpp index 25805db55..6446e3feb 100644 --- a/src/main/cpp/tracing/TracingUtility.cpp +++ b/src/main/cpp/tracing/TracingUtility.cpp @@ -55,6 +55,7 @@ void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, Client span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND, MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION); // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL, "abc") diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 78aaa264d..1ebbc22c1 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -18,6 +18,7 @@ #include "InvocationContext.h" #include "MixAll.h" #include "Signature.h" +#include "UtilAll.h" #include "fmt/format.h" #include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" #include "opentelemetry/proto/common/v1/common.pb.h" @@ -333,6 +334,7 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter value->set_int_value(attribute.second.int_value()); break; } + kv->set_allocated_value(value); item->mutable_attributes()->AddAllocated(kv); } @@ -348,6 +350,28 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter instrument_library_span->mutable_spans()->AddAllocated(item); } resource->mutable_instrumentation_library_spans()->AddAllocated(instrument_library_span); + + auto telemetry_sdk_language_kv = new common::KeyValue(); + telemetry_sdk_language_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE); + auto telemetry_sdk_language_value = new common::AnyValue(); + telemetry_sdk_language_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE); + telemetry_sdk_language_kv->set_allocated_value(telemetry_sdk_language_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(telemetry_sdk_language_kv); + + auto host_name_kv = new common::KeyValue(); + host_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME); + auto host_name_value = new common::AnyValue(); + host_name_value->set_string_value(UtilAll::hostname()); + host_name_kv->set_allocated_value(host_name_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(host_name_kv); + + auto service_name_kv = new common::KeyValue(); + service_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME); + auto service_name_value = new common::AnyValue(); + service_name_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME); + service_name_kv->set_allocated_value(service_name_value); + resource->mutable_resource()->mutable_attributes()->AddAllocated(service_name_kv); + request.mutable_resource_spans()->AddAllocated(resource); auto invocation_context = new InvocationContext(); @@ -362,7 +386,7 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } auto callback = [](const InvocationContext* invocation_context) { if (invocation_context->status.ok()) { - SPDLOG_DEBUG("Export tracing spans OK"); + SPDLOG_DEBUG("Export tracing spans OK, target={}", invocation_context->remote_address); } else { SPDLOG_WARN("Failed to export tracing spans to {}, gRPC code:{}, gRPC error message: {}", invocation_context->remote_address, invocation_context->status.error_code(), From 6548babfd7dbcacce86416bfca2850391a7bfd76 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Fri, 22 Oct 2021 18:17:10 +0800 Subject: [PATCH 117/133] Implement trace for standard consumption (#378) --- src/main/cpp/base/MixAll.cpp | 7 +-- src/main/cpp/base/include/MixAll.h | 7 +-- .../ConsumeStandardMessageService.cpp | 44 +++++++++---------- src/main/cpp/rocketmq/ProducerImpl.cpp | 12 ++--- .../cpp/tracing/exporters/OtlpExporter.cpp | 8 ++-- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 93d2d38af..ba69ce494 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -66,6 +66,8 @@ const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; const char* MixAll::MESSAGE_KEY_SEPARATOR = " "; +const char* MixAll::OTLP_NAME_VALUE = "org.apache.rocketmq.message"; + const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE = "cpp"; @@ -73,11 +75,7 @@ const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME = "host.name"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME = "service.name"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME = "rocketmq-client"; -// Span name list -const char* MixAll::SPAN_NAME_SEND_MESSAGE = "SendMessage"; const char* MixAll::SPAN_NAME_END_TRANSACTION = "EndTransaction"; -const char* MixAll::SPAN_NAME_AWAIT_CONSUMPTION = "AwaitingConsumption"; -const char* MixAll::SPAN_NAME_CONSUME_MESSAGE = "ConsumeMessage"; const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; // Span attributes follows to the opentelemetry specification, refers to: @@ -133,7 +131,6 @@ const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION = "send"; const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION = "receive"; const char* MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION = "process"; -const char* MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME = "host.name"; const char* MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION = "commitAction"; // Span annotation diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 8a47ecf29..d25f4b817 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -61,6 +61,8 @@ class MixAll { static const char* MESSAGE_KEY_SEPARATOR; + static const char* OTLP_NAME_VALUE; + static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE; static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE; @@ -68,11 +70,7 @@ class MixAll { static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME; static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME; - // Tracing span name list - static const char* SPAN_NAME_SEND_MESSAGE; static const char* SPAN_NAME_END_TRANSACTION; - static const char* SPAN_NAME_AWAIT_CONSUMPTION; - static const char* SPAN_NAME_CONSUME_MESSAGE; static const char* SPAN_NAME_PULL_MESSAGE; // RocketMQ span attribute name list @@ -125,7 +123,6 @@ class MixAll { static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_RECEIVE_OPERATION; static const char* SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION; - static const char* SPAN_ATTRIBUTE_KEY_HOST_NAME; static const char* SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION; // Tracing annotation diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index b13b34ac0..46ef5751b 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -19,6 +19,7 @@ #include #include +#include "TracingUtility.h" #include "absl/memory/memory.h" #include "absl/strings/str_join.h" #include "absl/time/time.h" @@ -36,6 +37,7 @@ #include "UtilAll.h" #include "rocketmq/ConsumeType.h" #include "rocketmq/MQMessage.h" +#include "rocketmq/MQMessageExt.h" #include "rocketmq/MessageListener.h" ROCKETMQ_NAMESPACE_BEGIN @@ -139,33 +141,30 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_AWAIT_CONSUMPTION, span_context, - &Samplers::always()); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); } else { - span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_AWAIT_CONSUMPTION, nullptr, {&Samplers::always()}); + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); } - - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, - consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, consumer->resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, msg.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, msg.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, consumer->getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, msg.getTags()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); const auto& keys = msg.getKeys(); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); span.AddAnnotation( MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); span.End(); + MessageAccessor::setTraceContext(const_cast(msg), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); } } @@ -175,27 +174,28 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce for (const auto& msg : msgs) { auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_CONSUME_MESSAGE, span_context); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); } else { - span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_CONSUME_MESSAGE); + span = opencensus::trace::Span::StartSpan(span_name); } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, - consumer->credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, consumer->resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, msg.getTopic()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, msg.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, consumer->getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, msg.getTags()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); + TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); const auto& keys = msg.getKeys(); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); spans.emplace_back(std::move(span)); + MessageAccessor::setTraceContext(const_cast(msg), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); } } diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 870bf4813..478022038 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -310,11 +310,6 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { return; } - SendMessageRequest request; - wrapSendMessageRequest(callback->message(), request, callback->messageQueue()); - Metadata metadata; - Signature::sign(this, metadata); - { // Trace Send RPC auto& message = callback->message(); @@ -348,6 +343,12 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); callback->span() = span; } + + SendMessageRequest request; + wrapSendMessageRequest(callback->message(), request, callback->messageQueue()); + Metadata metadata; + Signature::sign(this, metadata); + client_manager_->send(target, metadata, request, callback); } @@ -417,7 +418,6 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message_id); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_HOST_NAME, UtilAll::hostname()); switch (resolution) { case TransactionState::COMMIT: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "commit"); diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index 1ebbc22c1..e1c17351d 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ #include "OtlpExporter.h" +#include "ClientConfigImpl.h" #include "InvocationContext.h" #include "MixAll.h" #include "Signature.h" @@ -223,7 +224,7 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter for (const auto& span : spans) { auto item = new trace::Span(); - span.context().span_id().CopyTo(trace_id_buf); + span.context().trace_id().CopyTo(trace_id_buf); item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); span.context().span_id().CopyTo(span_id_buf); @@ -345,8 +346,9 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter item->mutable_status()->set_message(span.status().error_message()); } - // item->mutable_status()->set_code() - + instrument_library_span->mutable_instrumentation_library()->mutable_name()->assign(MixAll::OTLP_NAME_VALUE); + instrument_library_span->mutable_instrumentation_library()->mutable_version()->assign( + ClientConfigImpl::CLIENT_VERSION); instrument_library_span->mutable_spans()->AddAllocated(item); } resource->mutable_instrumentation_library_spans()->AddAllocated(instrument_library_span); From 925915f1f19be67c0ce205757645c5910b2867b5 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 25 Oct 2021 11:03:11 +0800 Subject: [PATCH 118/133] Remove unused test cases --- src/test/cpp/ut/grpc/BUILD.bazel | 40 ------- src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp | 50 --------- src/test/cpp/ut/grpc/GrpcTimer.cpp | 116 -------------------- 3 files changed, 206 deletions(-) delete mode 100644 src/test/cpp/ut/grpc/BUILD.bazel delete mode 100644 src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp delete mode 100644 src/test/cpp/ut/grpc/GrpcTimer.cpp diff --git a/src/test/cpp/ut/grpc/BUILD.bazel b/src/test/cpp/ut/grpc/BUILD.bazel deleted file mode 100644 index 1383d8874..000000000 --- a/src/test/cpp/ut/grpc/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "grpc_thread_pool_test", - srcs = [ - "GrpcThreadPoolTest.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "@com_github_grpc_grpc//:grpc++", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "grpc_timer_test", - srcs = [ - "GrpcTimer.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "@com_github_grpc_grpc//:grpc++", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp b/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp deleted file mode 100644 index b912d066c..000000000 --- a/src/test/cpp/ut/grpc/GrpcThreadPoolTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "rocketmq/RocketMQ.h" -#include "src/cpp/server/dynamic_thread_pool.h" -#include "src/cpp/server/thread_pool_interface.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(GrpcThreadPoolTest, testSetUp) { - auto thread_pool = std::unique_ptr(grpc::CreateDefaultThreadPool()); - - absl::Mutex mtx; - absl::CondVar cv; - - bool invoked = false; - - auto callback = [&]() { - absl::MutexLock lk(&mtx); - invoked = true; - cv.SignalAll(); - }; - - thread_pool->Add(callback); - - if (!invoked) { - absl::MutexLock lk(&mtx); - cv.WaitWithTimeout(&mtx, absl::Seconds(3)); - } - - ASSERT_TRUE(invoked); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/grpc/GrpcTimer.cpp b/src/test/cpp/ut/grpc/GrpcTimer.cpp deleted file mode 100644 index 06432715d..000000000 --- a/src/test/cpp/ut/grpc/GrpcTimer.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "absl/time/time.h" -#include "grpc/grpc.h" -#include "rocketmq/RocketMQ.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/timer.h" -#include "gtest/gtest.h" -#include -#include -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -struct Timer { - grpc_timer g_timer; - absl::Mutex mtx; - absl::CondVar cv; - bool invoked{false}; -}; - -struct PeriodicTimer { - grpc_timer g_timer; - absl::Duration initial_delay{absl::Seconds(3)}; - absl::Duration interval{absl::Seconds(1)}; - int times{10}; -}; - -class GrpcTimerTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - } - void TearDown() override { - grpc_shutdown(); - } - - static void schedule(void* arg, grpc_error_handle error) { - auto timer = static_cast(arg); - if (error == GRPC_ERROR_CANCELLED) { - std::cout << absl::FormatTime(absl::Now()) << ": job cancelled" << std::endl; - delete timer; - return; - } - - std::cout << "Thread-" << std::this_thread::get_id() << ": Periodic Callback" << std::endl; - grpc_core::ExecCtx exec_ctx; - std::cout << absl::FormatTime(absl::Now()) << ": Timer->times = " << timer->times << std::endl; - if (!--timer->times) { - delete timer; - std::cout << "Delete periodic timer" << std::endl; - return; - } - - grpc_timer_init(&timer->g_timer, grpc_core::ExecCtx::Get()->Now() + 1000, - GRPC_CLOSURE_CREATE(schedule, timer, grpc_schedule_on_exec_ctx)); - } -}; - -TEST_F(GrpcTimerTest, testSetUp) { -} - -TEST_F(GrpcTimerTest, testSingleShot) { - grpc_core::ExecCtx exec_ctx; - Timer timer; - - auto callback = [](void* arg, grpc_error_handle error) { - std::cout << "Callback" << std::endl; - auto t = static_cast(arg); - absl::MutexLock lk(&t->mtx); - t->invoked = true; - t->cv.SignalAll(); - }; - - grpc_timer_init(&timer.g_timer, grpc_core::ExecCtx::Get()->Now() + 500, - GRPC_CLOSURE_CREATE(callback, &timer, grpc_schedule_on_exec_ctx)); - - std::cout << "Waiting callback invocation" << std::endl; - - while (!timer.invoked) { - absl::MutexLock lk(&timer.mtx); - timer.cv.Wait(&timer.mtx); - } - ASSERT_TRUE(timer.invoked); -} - -TEST_F(GrpcTimerTest, testMultiShot) { - grpc_core::ExecCtx exec_ctx; - auto timer = new PeriodicTimer(); - std::cout << absl::FormatTime(absl::Now()) << ": Arm the timer" << std::endl; - std::cout << "Now = " << grpc_core::ExecCtx::Get()->Now() << std::endl; - grpc_timer_init(&timer->g_timer, grpc_core::ExecCtx::Get()->Now() + 3000, - GRPC_CLOSURE_CREATE(schedule, timer, grpc_schedule_on_exec_ctx)); - - absl::SleepFor(absl::Seconds(5)); - grpc_timer_cancel(&timer->g_timer); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file From 8f45618660c8b2f44883165c376c8c8ea6bb8cdd Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 25 Oct 2021 11:35:58 +0800 Subject: [PATCH 119/133] Optimize SchedulerTest --- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp index 87b275342..19a3dae72 100644 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ b/src/test/cpp/ut/scheduler/SchedulerTest.cpp @@ -76,9 +76,9 @@ TEST_F(SchedulerTest, testCancel) { }; std::uint32_t task_id = - scheduler->schedule(callback, "test-cancel", std::chrono::seconds(1), std::chrono::seconds(1)); + scheduler->schedule(callback, "test-cancel", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); scheduler->cancel(task_id); - std::this_thread::sleep_for(std::chrono::seconds(2)); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); ASSERT_EQ(0, callback_fire_count); } @@ -93,9 +93,9 @@ TEST_F(SchedulerTest, testPeriodicShot) { }; std::uintptr_t task_id = - scheduler->schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::seconds(1)); + scheduler->schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::milliseconds(100)); // Wait till callback is executed. - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(std::chrono::milliseconds(600)); ASSERT_TRUE(callback_fire_count >= 4); scheduler->cancel(task_id); } @@ -145,13 +145,5 @@ TEST_F(SchedulerTest, testException) { cv.Wait(&mtx); } } - - std::this_thread::sleep_for(std::chrono::seconds(3)); -} - -TEST(SchedulerLifeCycleTest, testLifeCycle) { - auto scheduler = std::make_shared(); - scheduler->start(); } - ROCKETMQ_NAMESPACE_END \ No newline at end of file From 8a8139296c4c0a9d194b2eeb4aab937e1bfdd360 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 11:49:37 +0800 Subject: [PATCH 120/133] Bump version to 5.0.0 (#379) --- src/main/cpp/client/ClientConfigImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp index c3c3294db..29304e0a2 100644 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ b/src/main/cpp/client/ClientConfigImpl.cpp @@ -29,7 +29,7 @@ ROCKETMQ_NAMESPACE_BEGIN #ifndef CLIENT_VERSION_MAJOR -#define CLIENT_VERSION_MAJOR "1" +#define CLIENT_VERSION_MAJOR "5" #endif #ifndef CLIENT_VERSION_MINOR From 8be2aa7ceda6c3fff2fa2c5591bcca293fdf4ce0 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 13:06:53 +0800 Subject: [PATCH 121/133] Add more attributes for message consumption (#380) --- src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 46ef5751b..956a4cf74 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -150,13 +150,13 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); const auto& keys = msg.getKeys(); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, - absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); span.AddAnnotation( MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, @@ -190,8 +190,7 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, - absl::FormatTime(absl::FromUnixMillis(msg.getStoreTimestamp()))); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); spans.emplace_back(std::move(span)); MessageAccessor::setTraceContext(const_cast(msg), From fe52d1d93288db3bfd986d5e814f8a96df974cef Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 15:57:12 +0800 Subject: [PATCH 122/133] Implement trace for fifo consumption (#381) --- api/rocketmq/MQMessage.h | 9 +- example/rocketmq/BUILD.bazel | 20 ++++ example/rocketmq/ExampleFifoProducer.cpp | 108 ++++++++++++++++++ example/rocketmq/ExampleFifoPushConsumer.cpp | 67 +++++++++++ src/main/cpp/base/MQMessage.cpp | 9 ++ src/main/cpp/base/include/Protocol.h | 1 + .../rocketmq/ConsumeFifoMessageService.cpp | 70 ++++++++++++ src/main/cpp/rocketmq/ProducerImpl.cpp | 7 +- 8 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 example/rocketmq/ExampleFifoProducer.cpp create mode 100644 example/rocketmq/ExampleFifoPushConsumer.cpp diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h index a4e642999..e5d27fa2e 100644 --- a/api/rocketmq/MQMessage.h +++ b/api/rocketmq/MQMessage.h @@ -91,17 +91,13 @@ class MQMessage { void messageType(MessageType message_type); MessageType messageType() const; - void bindMessageGroup(absl::string_view message_group) { - message_group_ = std::string(message_group.data(), message_group.length()); - } + void bindMessageGroup(absl::string_view message_group); void bindMessageQueue(const MQMessageQueue& message_queue) { message_queue_ = message_queue; } - const std::string& messageGroup() const { - return message_group_; - } + const std::string& messageGroup() const; const MQMessageQueue& messageQueue() const { return message_queue_; @@ -113,7 +109,6 @@ class MQMessage { friend class MessageAccessor; private: - std::string message_group_; MQMessageQueue message_queue_; }; diff --git a/example/rocketmq/BUILD.bazel b/example/rocketmq/BUILD.bazel index a2b2dd386..9c7508aae 100644 --- a/example/rocketmq/BUILD.bazel +++ b/example/rocketmq/BUILD.bazel @@ -26,6 +26,16 @@ cc_binary( ], ) +cc_binary( + name = "example_fifo_producer", + srcs = [ + "ExampleFifoProducer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + cc_binary( name = "example_async_producer", srcs = [ @@ -56,6 +66,16 @@ cc_binary( ], ) +cc_binary( + name = "example_fifo_push_consumer", + srcs = [ + "ExampleFifoPushConsumer.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + ], +) + cc_binary( name = "push_consumer_with_custom_executor", srcs = [ diff --git a/example/rocketmq/ExampleFifoProducer.cpp b/example/rocketmq/ExampleFifoProducer.cpp new file mode 100644 index 000000000..c4d9e2304 --- /dev/null +++ b/example/rocketmq/ExampleFifoProducer.cpp @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/DefaultMQProducer.h" +#include +#include +#include +#include + +using namespace rocketmq; + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +int main(int argc, char* argv[]) { + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + DefaultMQProducer producer("TestGroup"); + + const char* topic = "lingchu_test_order_topic"; + const char* name_server = "120.25.100.131:8081"; + + producer.setNamesrvAddr(name_server); + producer.compressBodyThreshold(256); + const char* resource_namespace = "MQ_INST_1080056302921134_BXyTLppt"; + // producer.setRegion("cn-hangzhou-pre"); + producer.setResourceNamespace(resource_namespace); + producer.setCredentialsProvider(std::make_shared()); + + std::atomic_bool stopped; + std::atomic_long count(0); + + auto stats_lambda = [&] { + while (!stopped.load(std::memory_order_relaxed)) { + long cnt = count.load(std::memory_order_relaxed); + while (count.compare_exchange_weak(cnt, 0)) { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "QPS: " << cnt << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + std::string body = randomString(1024 * 4); + std::cout << "Message body size: " << body.length() << std::endl; + + try { + producer.start(); + for (int i = 0; i < 16; ++i) { + std::string sharding_key = "sharding-key-" + std::to_string(i); + MQMessage message; + message.setTopic(topic); + message.setTags("TagA"); + message.setKey("Yuck! Why-plural?"); + message.setBody(body); + + SendResult sendResult = producer.send(message, sharding_key); + std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + count++; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + + stopped.store(true, std::memory_order_relaxed); + if (stats_thread.joinable()) { + stats_thread.join(); + } + + producer.shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExampleFifoPushConsumer.cpp b/example/rocketmq/ExampleFifoPushConsumer.cpp new file mode 100644 index 000000000..df039d7f8 --- /dev/null +++ b/example/rocketmq/ExampleFifoPushConsumer.cpp @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "rocketmq/Logger.h" + +#include "rocketmq/MessageListener.h" +#include "spdlog/spdlog.h" + +#include "rocketmq/DefaultMQPushConsumer.h" + +using namespace rocketmq; + +class SampleMQMessageListener : public FifoMessageListener { +public: + ConsumeMessageResult consumeMessage(const MQMessageExt& message) override { + SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", message.getTopic(), message.getMsgId()); + std::cout << "Consume Message[MsgId=" << message.getMsgId() << "] OK. Body Size: " << message.getBody().size() + << std::endl; + // std::this_thread::sleep_for(std::chrono::seconds(1)); + return ConsumeMessageResult::SUCCESS; + } +}; + +int main(int argc, char* argv[]) { + + Logger& logger = getLogger(); + logger.setLevel(Level::Debug); + logger.init(); + + const char* group_id = "GID_lingchu_test_order"; + const char* topic = "lingchu_test_order_topic"; + const char* resource_namespace = "MQ_INST_1080056302921134_BXyTLppt"; + + DefaultMQPushConsumer push_consumer(group_id); + push_consumer.setResourceNamespace(resource_namespace); + push_consumer.setCredentialsProvider(std::make_shared()); + push_consumer.setNamesrvAddr("120.25.100.131:8081"); + FifoMessageListener* listener = new SampleMQMessageListener(); + push_consumer.setInstanceName("instance_0"); + push_consumer.subscribe(topic, "*"); + push_consumer.registerMessageListener(listener); + push_consumer.setConsumeThreadCount(4); + push_consumer.start(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + + push_consumer.shutdown(); + return EXIT_SUCCESS; +} diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp index c5d096e63..2015a618f 100644 --- a/src/main/cpp/base/MQMessage.cpp +++ b/src/main/cpp/base/MQMessage.cpp @@ -178,4 +178,13 @@ MessageType MQMessage::messageType() const { return impl_->system_attribute_.message_type; } +void MQMessage::bindMessageGroup(absl::string_view message_group) { + impl_->system_attribute_.message_group.append(message_group.data(), message_group.length()); + messageType(MessageType::FIFO); +} + +const std::string& MQMessage::messageGroup() const { + return impl_->system_attribute_.message_group; +} + ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index df51ce151..7d52fd6c7 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -74,6 +74,7 @@ struct SystemAttribute { Resource publisher_group; std::string trace_context; std::string target_endpoint; + std::string message_group; }; ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 99d9b16c8..10519e79e 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -19,10 +19,17 @@ #include #include +#include "TracingUtility.h" +#include "absl/strings/str_join.h" + +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" + #include "ConsumeFifoMessageService.h" #include "MessageAccessor.h" #include "ProcessQueue.h" #include "PushConsumerImpl.h" +#include "rocketmq/MessageListener.h" ROCKETMQ_NAMESPACE_BEGIN @@ -120,6 +127,59 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q SPDLOG_DEBUG("Rate-limit permit acquired"); } + // Record await-consumption-span + { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); + TracingUtility::addUniversalSpanAttributes(message, *consumer, span); + const auto& keys = message.getKeys(); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); + absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(message); + span.AddAnnotation( + MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, + {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, + opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); + span.End(); + MessageAccessor::setTraceContext(const_cast(message), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + } + + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); + } else { + span = opencensus::trace::Span::StartSpan(span_name); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); + TracingUtility::addUniversalSpanAttributes(message, *consumer, span); + const auto& keys = message.getKeys(); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, message.getDeliveryAttempt()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); + MessageAccessor::setTraceContext(const_cast(message), + opencensus::trace::propagation::ToTraceParentHeader(span.context())); + auto steady_start = std::chrono::steady_clock::now(); try { @@ -132,6 +192,16 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q SPDLOG_ERROR("Business FIFO callback raised an exception when consumeMessage"); } + switch (result) { + case ConsumeMessageResult::SUCCESS: + span.SetStatus(opencensus::trace::StatusCode::OK); + break; + case ConsumeMessageResult::FAILURE: + span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); + break; + } + span.End(); + auto duration = std::chrono::steady_clock::now() - steady_start; // Log client consume-time costs diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 478022038..d22f6dc05 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -180,13 +180,14 @@ void ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageR system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); } + system_attribute->set_message_group(message.messageGroup()); + system_attribute->set_message_id(message.getMsgId()); + system_attribute->set_partition_id(message_queue.getQueueId()); + // Forward user-defined-properties for (auto& item : message.getProperties()) { request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); } - - system_attribute->set_message_id(message.getMsgId()); - system_attribute->set_partition_id(message_queue.getQueueId()); SPDLOG_TRACE("SendMessageRequest: {}", request.DebugString()); } From 88998c2f926dc20d46b44aa131e75dc8c53aae85 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 17:01:36 +0800 Subject: [PATCH 123/133] Bugfix: wrong judgement to submit fifo consumption task (#382) --- src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 10519e79e..ebefba658 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -97,7 +97,8 @@ void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& pro if (process_queue_ptr->bindFifoConsumeTask()) { std::vector messages; - if (process_queue_ptr->take(consumer->consumeBatchSize(), messages)) { + process_queue_ptr->take(consumer->consumeBatchSize(), messages); + if (!messages.empty()) { assert(1 == messages.size()); submitConsumeTask0(consumer, process_queue, *messages.begin()); } From 2b0318696b4f0791fe4c428612c6a98126a38c7f Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 25 Oct 2021 17:04:08 +0800 Subject: [PATCH 124/133] Try to submit consume-task for the current process-queue its own task completed --- src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp | 2 +- src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index ebefba658..58ee2ec76 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -262,7 +262,7 @@ void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message.getMsgId()); process_queue_ptr->unbindFifoConsumeTask(); - signalDispatcher(); + submitConsumeTask(process_queue); } } diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h index 8f42cde73..a3712d8cd 100644 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -26,6 +26,11 @@ class ConsumeFifoMessageService : public ConsumeMessageServiceBase, void shutdown() override; + /** + * @brief Entry of ConsumeMessageService + * + * @param process_queue + */ void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; MessageListenerType messageListenerType() override; From b066034388747552091a8f63a951391f5fd34d3e Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 19:43:14 +0800 Subject: [PATCH 125/133] Fill 'messaging.rocketmq.message_keys' attribute in message tracing with value of array (#383) --- src/main/cpp/base/MixAll.cpp | 5 ++-- src/main/cpp/base/include/MixAll.h | 1 + .../rocketmq/ConsumeFifoMessageService.cpp | 6 ----- .../ConsumeStandardMessageService.cpp | 6 ----- src/main/cpp/rocketmq/ProducerImpl.cpp | 5 ---- .../include/ConsumeFifoMessageService.h | 4 ++-- src/main/cpp/tracing/TracingUtility.cpp | 7 ++++++ .../cpp/tracing/exporters/OtlpExporter.cpp | 23 +++++++++++++++++++ 8 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index ba69ce494..4691e6958 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -84,8 +84,8 @@ const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; // RocketMQ span attribute name list const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION = "messaging.rocketmq.operation"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE = "messaging.rocketmq.namespace"; -const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG = "messaging.rocketmq.tag"; -const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS = "messaging.rocketmq.keys"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG = "messaging.rocketmq.message_tag"; +const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS = "messaging.rocketmq.message_keys"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID = "messaging.rocketmq.client_id"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE = "messaging.rocketmq.message_type"; const char* MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP = "messaging.rocketmq.client_group"; @@ -135,6 +135,7 @@ const char* MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION = "commitAction"; // Span annotation const char* MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION = "__await_consumption"; +const char* MixAll::SPAN_ANNOTATION_MESSAGE_KEYS = "__message_keys"; const char* MixAll::SPAN_ANNOTATION_ATTR_START_TIME = "__start_time"; bool MixAll::validate(const MQMessage& message) { diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index d25f4b817..30ff613cf 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -127,6 +127,7 @@ class MixAll { // Tracing annotation static const char* SPAN_ANNOTATION_AWAIT_CONSUMPTION; + static const char* SPAN_ANNOTATION_MESSAGE_KEYS; static const char* SPAN_ANNOTATION_ATTR_START_TIME; template diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 58ee2ec76..76db7a593 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -145,9 +145,6 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); TracingUtility::addUniversalSpanAttributes(message, *consumer, span); - const auto& keys = message.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, - absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(message); span.AddAnnotation( @@ -173,9 +170,6 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); TracingUtility::addUniversalSpanAttributes(message, *consumer, span); - const auto& keys = message.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, - absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, message.getDeliveryAttempt()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); MessageAccessor::setTraceContext(const_cast(message), diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index 956a4cf74..e31b2edda 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -153,9 +153,6 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); - const auto& keys = msg.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, - absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); span.AddAnnotation( @@ -186,9 +183,6 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); - const auto& keys = msg.getKeys(); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, - absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index d22f6dc05..df3fb7566 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -329,11 +329,6 @@ void ProducerImpl::sendImpl(RetrySendCallback* callback) { span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); TracingUtility::addUniversalSpanAttributes(message, *this, span); - const auto& keys = callback->message().getKeys(); - if (!keys.empty()) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS, - absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)); - } // Note: attempt-time is 0-based span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h index a3712d8cd..ea85888cf 100644 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -28,8 +28,8 @@ class ConsumeFifoMessageService : public ConsumeMessageServiceBase, /** * @brief Entry of ConsumeMessageService - * - * @param process_queue + * + * @param process_queue */ void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; diff --git a/src/main/cpp/tracing/TracingUtility.cpp b/src/main/cpp/tracing/TracingUtility.cpp index 6446e3feb..d5b7be0d2 100644 --- a/src/main/cpp/tracing/TracingUtility.cpp +++ b/src/main/cpp/tracing/TracingUtility.cpp @@ -16,6 +16,7 @@ */ #include "TracingUtility.h" #include "MixAll.h" +#include "absl/strings/str_join.h" #include "rocketmq/CredentialsProvider.h" #include "spdlog/spdlog.h" @@ -26,6 +27,12 @@ void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, Client span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES, message.getBody().length()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); + const std::vector& keys = message.getKeys(); + if (!keys.empty()) { + span.AddAnnotation(MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, + {{MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, + absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)}}); + } switch (message.messageType()) { case MessageType::FIFO: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp index e1c17351d..1d24de4a1 100644 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ b/src/main/cpp/tracing/exporters/OtlpExporter.cpp @@ -20,6 +20,7 @@ #include "MixAll.h" #include "Signature.h" #include "UtilAll.h" +#include "absl/strings/str_split.h" #include "fmt/format.h" #include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" #include "opentelemetry/proto/common/v1/common.pb.h" @@ -263,6 +264,28 @@ void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter } continue; } + if (annotation.event().description() == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { + for (const auto& attr : annotation.event().attributes()) { + if (attr.first == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { + assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kString); + std::string message_keys = attr.second.string_value(); + std::vector key_list = absl::StrSplit(message_keys, MixAll::MESSAGE_KEY_SEPARATOR); + auto key_kv = new common::KeyValue(); + key_kv->set_key(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS); + auto key_value = new common::AnyValue(); + auto key_array_value = new common::ArrayValue(); + for (const auto& key : key_list) { + auto value = new common::AnyValue(); + value->set_string_value(key); + key_array_value->mutable_values()->AddAllocated(value); + } + key_value->set_allocated_array_value(key_array_value); + key_kv->set_allocated_value(key_value); + item->mutable_attributes()->AddAllocated(key_kv); + } + } + continue; + } auto ev = new trace::Span::Event(); ev->set_time_unix_nano(absl::ToUnixNanos(annotation.timestamp())); auto attrs = annotation.event().attributes(); From 7c5df129da587787874916e4f20ee7b58036e34c Mon Sep 17 00:00:00 2001 From: aaron ai Date: Mon, 25 Oct 2021 20:27:50 +0800 Subject: [PATCH 126/133] Add 'message' into the arguments of transaction related method (#384) --- src/main/cpp/base/MixAll.cpp | 1 - src/main/cpp/base/include/MixAll.h | 1 - src/main/cpp/rocketmq/ProducerImpl.cpp | 30 +++++++++---------- src/main/cpp/rocketmq/TransactionImpl.cpp | 6 ++-- src/main/cpp/rocketmq/include/ProducerImpl.h | 10 +++---- .../cpp/rocketmq/include/TransactionImpl.h | 7 +++-- 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 4691e6958..e919dac9e 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -76,7 +76,6 @@ const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME = "service.name"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME = "rocketmq-client"; const char* MixAll::SPAN_NAME_END_TRANSACTION = "EndTransaction"; -const char* MixAll::SPAN_NAME_PULL_MESSAGE = "PullMessage"; // Span attributes follows to the opentelemetry specification, refers to: // https://github.com/open-telemetry/opentelemetry-specification diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 30ff613cf..5a45cd147 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -71,7 +71,6 @@ class MixAll { static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME; static const char* SPAN_NAME_END_TRANSACTION; - static const char* SPAN_NAME_PULL_MESSAGE; // RocketMQ span attribute name list static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION; diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index df3fb7566..ea54b753f 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -375,12 +375,11 @@ void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std:: sendImpl(retry_callback); } -bool ProducerImpl::endTransaction0(const std::string& target, const std::string& message_id, - const std::string& transaction_id, TransactionState resolution, - std::string trace_context) { +bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& message, + const std::string& transaction_id, TransactionState resolution) { EndTransactionRequest request; - request.set_message_id(message_id); + request.set_message_id(message.getMsgId()); request.set_transaction_id(transaction_id); request.mutable_group()->set_name(group_name_); request.mutable_group()->set_resource_namespace(resource_namespace_); @@ -401,7 +400,8 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& bool completed = false; bool success = false; // Trace transactional message - opencensus::trace::SpanContext span_context = opencensus::trace::propagation::FromTraceParentHeader(trace_context); + opencensus::trace::SpanContext span_context = + opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); if (span_context.IsValid()) { span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_END_TRANSACTION, span_context, @@ -413,7 +413,7 @@ bool ProducerImpl::endTransaction0(const std::string& target, const std::string& credentialsProvider()->getCredentials().accessKey()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message_id); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); switch (resolution) { case TransactionState::COMMIT: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "commit"); @@ -508,19 +508,17 @@ std::unique_ptr ProducerImpl::prepare(MQMessage& message, std:: return nullptr; } - return std::unique_ptr(new TransactionImpl( - send_result.getMsgId(), send_result.getTransactionId(), send_result.getMessageQueue().serviceAddress(), - send_result.traceContext(), ProducerImpl::shared_from_this())); + return absl::make_unique(message, send_result.getTransactionId(), + send_result.getMessageQueue().serviceAddress(), send_result.traceContext(), + ProducerImpl::shared_from_this()); } -bool ProducerImpl::commit(const std::string& message_id, const std::string& transaction_id, - const std::string& trace_context, const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionState::COMMIT, trace_context); +bool ProducerImpl::commit(const MQMessage& message, const std::string& transaction_id, const std::string& target) { + return endTransaction0(target, message, transaction_id, TransactionState::COMMIT); } -bool ProducerImpl::rollback(const std::string& message_id, const std::string& transaction_id, - const std::string& trace_context, const std::string& target) { - return endTransaction0(target, message_id, transaction_id, TransactionState::ROLLBACK, trace_context); +bool ProducerImpl::rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target) { + return endTransaction0(target, message, transaction_id, TransactionState::ROLLBACK); } void ProducerImpl::asyncPublishInfo(const std::string& topic, @@ -624,7 +622,7 @@ void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transa if (transaction_state_checker_) { TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); const std::string& target_host = MessageAccessor::targetEndpoint(message); - endTransaction0(target_host, message.getMsgId(), transaction_id, state, message.traceContext()); + endTransaction0(target_host, message, transaction_id, state); } else { SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); } diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 21610aa98..11f9ea3c9 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -26,7 +26,7 @@ bool TransactionImpl::commit() { return false; } - return producer->commit(message_id_, transaction_id_, trace_context_, endpoint_); + return producer->commit(message_, transaction_id_, endpoint_); } bool TransactionImpl::rollback() { @@ -34,11 +34,11 @@ bool TransactionImpl::rollback() { if (!producer) { return false; } - return producer->rollback(message_id_, transaction_id_, trace_context_, endpoint_); + return producer->rollback(message_, transaction_id_, endpoint_); } std::string TransactionImpl::messageId() const { - return message_id_; + return message_.getMsgId(); } std::string TransactionImpl::transactionId() const { diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index e28f8a3c0..a2172cef6 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -62,11 +62,9 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t std::unique_ptr prepare(MQMessage& message, std::error_code& ec); - bool commit(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, - const std::string& target); + bool commit(const MQMessage& message, const std::string& transaction_id, const std::string& target); - bool rollback(const std::string& message_id, const std::string& transaction_id, const std::string& trace_context, - const std::string& target); + bool rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target); /** * Check if the RPC client for the target host is isolated or not @@ -156,8 +154,8 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); - bool endTransaction0(const std::string& target, const std::string& message_id, const std::string& transaction_id, - TransactionState resolution, std::string trace_context); + bool endTransaction0(const std::string& target, const MQMessage& message, const std::string& transaction_id, + TransactionState resolution); void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 4b721ed93..844c82796 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -19,6 +19,7 @@ #include #include +#include "rocketmq/MQMessage.h" #include "rocketmq/Transaction.h" ROCKETMQ_NAMESPACE_BEGIN @@ -27,9 +28,9 @@ class ProducerImpl; class TransactionImpl : public Transaction { public: - TransactionImpl(std::string message_id, std::string transaction_id, std::string endpoint, std::string trace_context, + TransactionImpl(MQMessage message, std::string transaction_id, std::string endpoint, std::string trace_context, const std::shared_ptr& producer) - : message_id_(std::move(message_id)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), + : message_(std::move(message)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), trace_context_(std::move(trace_context)), producer_(producer) { } @@ -44,7 +45,7 @@ class TransactionImpl : public Transaction { std::string transactionId() const override; private: - std::string message_id_; + MQMessage message_; std::string transaction_id_; std::string endpoint_; std::string trace_context_; From 995968624540fa46aceb924bcb2d7ca297985853 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Thu, 28 Oct 2021 10:56:32 +0800 Subject: [PATCH 127/133] Implement trace for transaction message (#385) --- .../rocketmq/ExampleTransactionProducer.cpp | 2 ++ src/main/cpp/base/MixAll.cpp | 2 -- src/main/cpp/base/include/MixAll.h | 2 -- src/main/cpp/rocketmq/ProducerImpl.cpp | 27 ++++++++----------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/example/rocketmq/ExampleTransactionProducer.cpp index 1fe5b87ed..1041b9267 100644 --- a/example/rocketmq/ExampleTransactionProducer.cpp +++ b/example/rocketmq/ExampleTransactionProducer.cpp @@ -44,6 +44,8 @@ int main(int argc, char* argv[]) { transaction->commit(); + std::this_thread::sleep_for(std::chrono::minutes(30)); + producer.shutdown(); return EXIT_SUCCESS; diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index e919dac9e..0bf273a6f 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -75,8 +75,6 @@ const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME = "host.name"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME = "service.name"; const char* MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME = "rocketmq-client"; -const char* MixAll::SPAN_NAME_END_TRANSACTION = "EndTransaction"; - // Span attributes follows to the opentelemetry specification, refers to: // https://github.com/open-telemetry/opentelemetry-specification diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index 5a45cd147..ae272dc2e 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -70,8 +70,6 @@ class MixAll { static const char* TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME; static const char* TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME; - static const char* SPAN_NAME_END_TRANSACTION; - // RocketMQ span attribute name list static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION; static const char* SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE; diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index ea54b753f..38bc99e6a 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -373,6 +373,8 @@ void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std:: auto retry_callback = new RetrySendCallback(shared_from_this(), message, max_attempt_times, callback, std::move(list)); sendImpl(retry_callback); + const_cast(message).traceContext( + opencensus::trace::propagation::ToTraceParentHeader(retry_callback->span().context())); } bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& message, @@ -403,25 +405,18 @@ bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& m opencensus::trace::SpanContext span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); + std::string trace_operation_name = TransactionState::COMMIT == resolution + ? MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION + : MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; + std::string span_name = resourceNamespace() + "/" + message.getTopic() + " " + trace_operation_name; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(MixAll::SPAN_NAME_END_TRANSACTION, span_context, - {&Samplers::always()}); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); } else { - span = opencensus::trace::Span::StartSpan(MixAll::SPAN_NAME_END_TRANSACTION, nullptr, {&Samplers::always()}); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, - credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, getGroupName()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); - switch (resolution) { - case TransactionState::COMMIT: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "commit"); - break; - case TransactionState::ROLLBACK: - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_TRANSACTION_RESOLUTION, "rollback"); - break; + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, trace_operation_name); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, trace_operation_name); + TracingUtility::addUniversalSpanAttributes(message, *this, span); absl::Mutex mtx; absl::CondVar cv; From ab3fdf915a633bd2ac3d24fa45819cdccd30d9a8 Mon Sep 17 00:00:00 2001 From: aaron ai Date: Thu, 28 Oct 2021 13:11:57 +0800 Subject: [PATCH 128/133] Enable message tracing in default mode (#388) --- src/main/cpp/client/include/ClientConfig.h | 2 ++ src/main/cpp/client/include/ClientConfigImpl.h | 5 +++-- src/main/cpp/client/mocks/include/ClientConfigMock.h | 1 + src/main/cpp/tracing/exporters/include/OtlpExporter.h | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 669b2205d..97a1ec239 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -45,6 +45,8 @@ class ClientConfig { virtual const std::string& getGroupName() const = 0; virtual std::string clientId() const = 0; + + virtual bool isTracingEnabled() const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h index d2771908a..116eb7e42 100644 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ b/src/main/cpp/client/include/ClientConfigImpl.h @@ -70,9 +70,10 @@ class ClientConfigImpl : virtual public ClientConfig { long_polling_timeout_ = timeout; } - bool isTracingEnabled() { + bool isTracingEnabled() const override { return enable_tracing_.load(); } + void enableTracing(bool enabled) { enable_tracing_.store(enabled); } @@ -137,7 +138,7 @@ class ClientConfigImpl : virtual public ClientConfig { absl::Duration long_polling_timeout_; - std::atomic enable_tracing_{false}; + std::atomic enable_tracing_{true}; static std::string steadyName(); }; diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h index 703e3482c..115e0bf0c 100644 --- a/src/main/cpp/client/mocks/include/ClientConfigMock.h +++ b/src/main/cpp/client/mocks/include/ClientConfigMock.h @@ -33,6 +33,7 @@ class ClientConfigMock : virtual public ClientConfig { MOCK_METHOD(absl::Duration, getLongPollingTimeout, (), (const override)); MOCK_METHOD(const std::string&, getGroupName, (), (const override)); MOCK_METHOD(std::string, clientId, (), (const override)); + MOCK_METHOD(bool, isTracingEnabled, (), (const override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h index 3b6755b9f..f947a3886 100644 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ b/src/main/cpp/tracing/exporters/include/OtlpExporter.h @@ -56,6 +56,9 @@ class OtlpExporter : public std::enable_shared_from_this { public: OtlpExporter(std::weak_ptr client_manager, ClientConfig* client_config) : client_manager_(std::move(client_manager)), client_config_(client_config) { + if (client_config_->isTracingEnabled()) { + mode_ = TraceMode::Grpc; + } } void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { From 571c7b74e4984920dc071fce70bfd06aeca5a707 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Thu, 10 Mar 2022 11:27:40 +0800 Subject: [PATCH 129/133] Use gRPC naming scheme for name server address (#408) * Use gRPC naming scheme for name server address * Pin bazel version to LTS 4.2.0 --- .bazelversion | 1 + example/rocketmq/ExampleProducer.cpp | 2 +- example/rocketmq/ExamplePushConsumer.cpp | 3 +- src/main/cpp/client/ClientManagerImpl.cpp | 3 +- src/main/cpp/rocketmq/ClientImpl.cpp | 32 +++++--- .../rocketmq/DynamicNameServerResolver.cpp | 21 +---- src/main/cpp/rocketmq/NamingScheme.cpp | 81 +++++++++++++++++++ .../cpp/rocketmq/StaticNameServerResolver.cpp | 27 +++---- .../include/DynamicNameServerResolver.h | 9 +-- .../cpp/rocketmq/include/NameServerResolver.h | 6 +- src/main/cpp/rocketmq/include/NamingScheme.h | 46 +++++++++++ .../include/StaticNameServerResolver.h | 26 +++--- .../mocks/include/NameServerResolverMock.h | 6 +- src/test/cpp/ut/rocketmq/BUILD.bazel | 11 +++ .../DynamicNameServerResolverTest.cpp | 15 ++-- src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp | 50 ++++++++++++ .../rocketmq/StaticNameServerResolverTest.cpp | 16 +--- 17 files changed, 258 insertions(+), 97 deletions(-) create mode 100644 .bazelversion create mode 100644 src/main/cpp/rocketmq/NamingScheme.cpp create mode 100644 src/main/cpp/rocketmq/include/NamingScheme.h create mode 100644 src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000..6aba2b245 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +4.2.0 diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp index 2fda81f5c..3e1cfc54c 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/example/rocketmq/ExampleProducer.cpp @@ -51,7 +51,7 @@ int main(int argc, char* argv[]) { DefaultMQProducer producer("TestGroup"); const char* topic = "cpp_sdk_standard"; - const char* name_server = "121.43.42.193:80"; + const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; producer.setNamesrvAddr(name_server); producer.compressBodyThreshold(256); diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp index 8a2e9a604..d45e52af3 100644 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ b/example/rocketmq/ExamplePushConsumer.cpp @@ -49,11 +49,12 @@ int main(int argc, char* argv[]) { const char* group_id = "GID_cpp_sdk_standard"; const char* topic = "cpp_sdk_standard"; const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; + const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; DefaultMQPushConsumer push_consumer(group_id); push_consumer.setResourceNamespace(resource_namespace); push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr("121.43.42.193:80"); + push_consumer.setNamesrvAddr(name_server); MessageListener* listener = new SampleMQMessageListener; push_consumer.setInstanceName("instance_0"); push_consumer.subscribe(topic, "*"); diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index db77dc838..d8a43e559 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -590,7 +590,8 @@ void ClientManagerImpl::addClientObserver(std::weak_ptr client) { void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, std::chrono::milliseconds timeout, const std::function& cb) { - + SPDLOG_DEBUG("Name server connection URL: {}", target_host); + SPDLOG_DEBUG("Query route request: {}", request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host, false); if (!client) { SPDLOG_WARN("Failed to create RPC client for name server[host={}]", target_host); diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 0d22e9593..960296909 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -38,6 +38,7 @@ #include "InvocationContext.h" #include "LoggerImpl.h" #include "MessageAccessor.h" +#include "NamingScheme.h" #include "Signature.h" #include "rocketmq/MQMessageExt.h" #include "rocketmq/MessageListener.h" @@ -160,7 +161,24 @@ void ClientImpl::getRouteFor(const std::string& topic, void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { std::vector> pairs; { - std::vector name_server_list = name_server_resolver_->resolve(); + std::string naming_address = name_server_resolver_->resolve(); + absl::string_view host_port_csv; + + if (absl::StartsWith(naming_address, NamingScheme::DnsPrefix)) { + endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::DnsPrefix); + } else if (absl::StartsWith(naming_address, NamingScheme::IPv4Prefix)) { + endpoints->set_scheme(rmq::AddressScheme::IPv4); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv4Prefix); + } else if (absl::StartsWith(naming_address, NamingScheme::IPv6Prefix)) { + endpoints->set_scheme(rmq::AddressScheme::IPv6); + host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv6Prefix); + } else { + SPDLOG_WARN("Unsupported naming scheme"); + } + + std::vector name_server_list = absl::StrSplit(host_port_csv, ','); + for (const auto& name_server_item : name_server_list) { std::string::size_type pos = name_server_item.rfind(':'); if (std::string::npos == pos) { @@ -179,20 +197,12 @@ void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { address->set_host(host_port.first); endpoints->mutable_addresses()->AddAllocated(address); } - - if (MixAll::isIPv4(pairs.begin()->first)) { - endpoints->set_scheme(rmq::AddressScheme::IPv4); - } else if (absl::StrContains(pairs.begin()->first, ':')) { - endpoints->set_scheme(rmq::AddressScheme::IPv6); - } else { - endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME); - } } } void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { - std::string name_server = name_server_resolver_->current(); + std::string name_server = name_server_resolver_->resolve(); if (name_server.empty()) { SPDLOG_WARN("No name server available"); return; @@ -201,7 +211,7 @@ void ClientImpl::fetchRouteFor(const std::string& topic, auto callback = [this, topic, name_server, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { if (ec) { SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server); - std::string name_server_changed = name_server_resolver_->next(); + std::string name_server_changed = name_server_resolver_->resolve(); if (!name_server_changed.empty()) { SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed); } diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp index b1a961481..f9981bc15 100644 --- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp @@ -23,8 +23,8 @@ #include #include "absl/strings/str_join.h" -#include "spdlog/spdlog.h" +#include "LoggerImpl.h" #include "SchedulerImpl.h" ROCKETMQ_NAMESPACE_BEGIN @@ -69,7 +69,7 @@ DynamicNameServerResolver::DynamicNameServerResolver(absl::string_view endpoint, std::string(remains.data(), remains.length())); } -std::vector DynamicNameServerResolver::resolve() { +std::string DynamicNameServerResolver::resolve() { bool fetch_immediately = false; { absl::MutexLock lk(&name_server_list_mtx_); @@ -84,7 +84,7 @@ std::vector DynamicNameServerResolver::resolve() { { absl::MutexLock lk(&name_server_list_mtx_); - return name_server_list_; + return naming_scheme_.buildAddress(name_server_list_); } } @@ -126,19 +126,4 @@ void DynamicNameServerResolver::shutdown() { scheduler_->shutdown(); } -std::string DynamicNameServerResolver::current() { - absl::MutexLock lk(&name_server_list_mtx_); - if (name_server_list_.empty()) { - return std::string(); - } - - std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size(); - return name_server_list_[index]; -} - -std::string DynamicNameServerResolver::next() { - index_.fetch_add(1, std::memory_order_relaxed); - return current(); -} - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/NamingScheme.cpp b/src/main/cpp/rocketmq/NamingScheme.cpp new file mode 100644 index 000000000..4646af971 --- /dev/null +++ b/src/main/cpp/rocketmq/NamingScheme.cpp @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "NamingScheme.h" + +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const char* NamingScheme::DnsPrefix = "dns:"; +const char* NamingScheme::IPv4Prefix = "ipv4:"; +const char* NamingScheme::IPv6Prefix = "ipv6:"; + +const char* NamingScheme::IPv4Regex = + "(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; + +const char* NamingScheme::IPv6Regex = "((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}"; + +NamingScheme::NamingScheme() : ipv4_pattern_(IPv4Regex), ipv6_pattern_(IPv6Regex) { +} + +std::string NamingScheme::buildAddress(const std::vector& list) { + absl::flat_hash_map ipv4; + absl::flat_hash_map ipv6; + + for (const auto& segment : list) { + std::vector host_port = absl::StrSplit(segment, ':'); + if (2 != host_port.size()) { + continue; + } + + if (re2::RE2::FullMatch(host_port[0], ipv4_pattern_)) { + std::uint32_t port; + if (absl::SimpleAtoi(host_port[1], &port)) { + ipv4.insert_or_assign(host_port[0], port); + } + continue; + } + + if (re2::RE2::FullMatch(host_port[0], ipv6_pattern_)) { + std::uint32_t port; + if (absl::SimpleAtoi(host_port[1], &port)) { + ipv6.insert_or_assign(host_port[0], port); + } + continue; + } + + // Once we find a domain name record, use it as the final result. + host_port.insert(host_port.begin(), "dns"); + return absl::StrJoin(host_port, ":"); + } + + if (!ipv4.empty()) { + return "ipv4:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":")); + } + + if (!ipv6.empty()) { + return "ipv6:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":")); + } + return std::string(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp index 85e865f2c..c7f0804e5 100644 --- a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp +++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp @@ -17,27 +17,22 @@ #include "StaticNameServerResolver.h" #include "absl/strings/str_split.h" -#include -#include -ROCKETMQ_NAMESPACE_BEGIN - -StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) - : name_server_list_(absl::StrSplit(name_server_list, ';')) { -} +#include "LoggerImpl.h" -std::string StaticNameServerResolver::current() { - std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size(); - return name_server_list_[index]; -} +ROCKETMQ_NAMESPACE_BEGIN -std::string StaticNameServerResolver::next() { - index_.fetch_add(1, std::memory_order_relaxed); - return current(); +StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) { + std::vector segments = absl::StrSplit(name_server_list, ';'); + name_server_address_ = naming_scheme_.buildAddress(segments); + if (name_server_address_.empty()) { + SPDLOG_WARN("Failed to create gRPC naming scheme compliant address from {}", + std::string(name_server_list.data(), name_server_list.length())); + } } -std::vector StaticNameServerResolver::resolve() { - return name_server_list_; +std::string StaticNameServerResolver::resolve() { + return name_server_address_; } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h index bac08bf77..5bc2c53f8 100644 --- a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h @@ -30,6 +30,7 @@ #include "absl/synchronization/mutex.h" #include "NameServerResolver.h" +#include "NamingScheme.h" #include "Scheduler.h" #include "TopAddressing.h" @@ -44,11 +45,7 @@ class DynamicNameServerResolver : public NameServerResolver, void shutdown() override; - std::string current() override LOCKS_EXCLUDED(name_server_list_mtx_); - - std::string next() override LOCKS_EXCLUDED(name_server_list_mtx_); - - std::vector resolve() override LOCKS_EXCLUDED(name_server_list_mtx_); + std::string resolve() override LOCKS_EXCLUDED(name_server_list_mtx_); void injectHttpClient(std::unique_ptr http_client); @@ -69,6 +66,8 @@ class DynamicNameServerResolver : public NameServerResolver, bool ssl_{false}; std::unique_ptr top_addressing_; + + NamingScheme naming_scheme_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h index 443f36022..00ec0d91e 100644 --- a/src/main/cpp/rocketmq/include/NameServerResolver.h +++ b/src/main/cpp/rocketmq/include/NameServerResolver.h @@ -31,11 +31,7 @@ class NameServerResolver { virtual void shutdown() = 0; - virtual std::string next() = 0; - - virtual std::string current() = 0; - - virtual std::vector resolve() = 0; + virtual std::string resolve() = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/NamingScheme.h b/src/main/cpp/rocketmq/include/NamingScheme.h new file mode 100644 index 000000000..d9b4f62e7 --- /dev/null +++ b/src/main/cpp/rocketmq/include/NamingScheme.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "re2/re2.h" + +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NamingScheme { +public: + NamingScheme(); + + std::string buildAddress(const std::vector& list); + + static const char* DnsPrefix; + static const char* IPv4Prefix; + static const char* IPv6Prefix; + +private: + static const char* IPv4Regex; + static const char* IPv6Regex; + + re2::RE2 ipv4_pattern_; + re2::RE2 ipv6_pattern_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h index 2648b4a84..b38a86e6a 100644 --- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h +++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h @@ -16,14 +16,10 @@ */ #pragma once -#include -#include -#include - #include "absl/strings/string_view.h" #include "NameServerResolver.h" -#include "rocketmq/RocketMQ.h" +#include "NamingScheme.h" ROCKETMQ_NAMESPACE_BEGIN @@ -37,15 +33,21 @@ class StaticNameServerResolver : public NameServerResolver { void shutdown() override { } - std::string current() override; - - std::string next() override; - - std::vector resolve() override; + std::string resolve() override; private: - std::vector name_server_list_; - std::atomic index_{0}; + /** + * @brief Name server addresses, following gRPC URI schemes described in + * https://github.com/grpc/grpc/blob/master/doc/naming.md + * + * Sample values are: + * dns:[//authority/]host[:port] + * ipv4:address[:port][,address[:port],...] + * ipv6:address[:port][,address[:port],...] + */ + std::string name_server_address_; + + NamingScheme naming_scheme_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h index f572a0445..4f8f7f3fd 100644 --- a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h +++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h @@ -27,11 +27,7 @@ class NameServerResolverMock : public NameServerResolver { MOCK_METHOD(void, shutdown, (), (override)); - MOCK_METHOD(std::string, next, (), (override)); - - MOCK_METHOD(std::string, current, (), (override)); - - MOCK_METHOD((std::vector), resolve, (), (override)); + MOCK_METHOD(std::string, resolve, (), (override)); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel index 0fcfa1bb1..205cee162 100644 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ b/src/test/cpp/ut/rocketmq/BUILD.bazel @@ -250,4 +250,15 @@ cc_test( "//src/main/cpp/rocketmq:rocketmq_library", "@com_google_googletest//:gtest_main", ], +) + +cc_test( + name = "naming_scheme_test", + srcs = [ + "NamingSchemeTest.cpp", + ], + deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", + ], ) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp index f296f93c1..a693fd6aa 100644 --- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp @@ -19,12 +19,14 @@ #include #include #include +#include #include "absl/memory/memory.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include #include "HttpClientMock.h" @@ -66,15 +68,8 @@ class DynamicNameServerResolverTest : public testing::Test { TEST_F(DynamicNameServerResolverTest, testResolve) { auto name_server_list = resolver_->resolve(); - ASSERT_FALSE(name_server_list.empty()); - std::string resolved = absl::StrJoin(name_server_list, ";"); - ASSERT_EQ(name_server_list_, resolved); - - std::string first{"10.0.0.0:9876"}; - EXPECT_EQ(first, resolver_->current()); - - std::string second{"10.0.0.1:9876"}; - EXPECT_EQ(second, resolver_->next()); + std::string result = absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); + ASSERT_EQ(name_server_list, "ipv4:" + result); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp new file mode 100644 index 000000000..336f702b2 --- /dev/null +++ b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NamingScheme.h" +#include "rocketmq/RocketMQ.h" + +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class NamingSchemeTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } + +protected: + NamingScheme naming_scheme_; +}; + +TEST_F(NamingSchemeTest, testBuildAddress) { + std::string address = "www.baidu.com:80"; + std::string result = naming_scheme_.buildAddress({address}); + ASSERT_EQ("dns:www.baidu.com:80", result); + + address = "8.8.8.8:1234"; + result = naming_scheme_.buildAddress({address}); + ASSERT_EQ("ipv4:8.8.8.8:1234", result); + + result = naming_scheme_.buildAddress({address, "4.4.4.4:1234"}); + ASSERT_EQ("ipv4:8.8.8.8:1234,4.4.4.4:1234", result); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp index cf16502f1..0bca7e429 100644 --- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp +++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp @@ -16,6 +16,7 @@ */ #include "StaticNameServerResolver.h" +#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "gtest/gtest.h" @@ -42,18 +43,9 @@ class StaticNameServerResolverTest : public testing::Test { }; TEST_F(StaticNameServerResolverTest, testResolve) { - std::vector segments = absl::StrSplit(name_server_list_, ';'); - ASSERT_EQ(segments, resolver_.resolve()); -} - -TEST_F(StaticNameServerResolverTest, testCurrentNext) { - std::string&& name_server_1 = resolver_.current(); - std::string expected = "10.0.0.1:9876"; - EXPECT_EQ(expected, name_server_1); - - expected = "10.0.0.2:9876"; - std::string&& name_server_2 = resolver_.next(); - EXPECT_EQ(expected, name_server_2); + std::string result = + "ipv4:" + absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); + ASSERT_EQ(result, resolver_.resolve()); } ROCKETMQ_NAMESPACE_END \ No newline at end of file From a44a2c611406e62bd8af66e1a2a7f2070b53f728 Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Fri, 20 May 2022 17:01:16 +0800 Subject: [PATCH 130/133] Adopt protocol v2 (#414) --- .bazelversion | 2 +- .clang-format | 6 +- .gitignore | 5 +- .vscode/settings.json | 13 + WORKSPACE | 5 +- api/BUILD.bazel | 1 + api/rocketmq/AsyncCallback.h | 50 - api/rocketmq/BackoffPolicy.h | 70 ++ api/rocketmq/Configuration.h | 71 ++ .../rocketmq/ConfigurationDefaults.h | 14 +- .../{MessageModel.h => ConsumeResult.h} | 11 +- api/rocketmq/ConsumeType.h | 70 -- api/rocketmq/DefaultMQProducer.h | 154 --- api/rocketmq/DefaultMQPullConsumer.h | 63 - api/rocketmq/DefaultMQPushConsumer.h | 120 -- api/rocketmq/ErrorCode.h | 17 +- .../rocketmq}/FilterExpression.h | 8 +- api/rocketmq/MQMessage.h | 115 -- api/rocketmq/MQMessageExt.h | 60 - api/rocketmq/MQMessageQueue.h | 153 --- api/rocketmq/Message.h | 176 +++ api/rocketmq/MessageListener.h | 43 +- api/rocketmq/OffsetStore.h | 34 - api/rocketmq/Producer.h | 95 ++ api/rocketmq/PullResult.h | 55 - api/rocketmq/PushConsumer.h | 94 ++ api/rocketmq/SendCallback.h | 29 + api/rocketmq/SendReceipt.h | 33 + api/rocketmq/SendResult.h | 137 --- api/rocketmq/SimpleConsumer.h | 111 ++ api/rocketmq/Tracing.h | 27 + api/rocketmq/Transaction.h | 17 +- api/rocketmq/TransactionChecker.h | 28 + .../{MessageType.h => TransactionState.h} | 9 +- bazel/rocketmq_deps.bzl | 81 +- example/rocketmq/ExampleAsyncProducer.cpp | 257 ---- .../rocketmq/ExampleBroadcastPushConsumer.cpp | 65 - example/rocketmq/ExamplePullConsumer.cpp | 54 - example/rocketmq/ExamplePushConsumer.cpp | 69 -- {example/rocketmq => examples}/BUILD.bazel | 142 ++- .../BenchmarkPushConsumer.cpp | 0 examples/ExampleAsyncProducer.cpp | 113 ++ .../ExampleFifoProducer.cpp | 49 +- .../ExampleFifoPushConsumer.cpp | 0 .../rocketmq => examples}/ExampleProducer.cpp | 43 +- examples/ExamplePushConsumer.cpp | 50 + examples/ExampleSimpleConsumer.cpp | 66 ++ .../ExampleTransactionProducer.cpp | 0 .../PushConsumerWithCustomExecutor.cpp | 0 .../PushConsumerWithThrottle.cpp | 0 .../rocketmq => examples}/SqlConsumer.cpp | 0 .../rocketmq => examples}/SqlProducer.cpp | 0 proto/BUILD.bazel | 6 +- proto/apache/rocketmq/v1/definition.proto | 349 ------ proto/apache/rocketmq/v1/service.proto | 531 --------- proto/apache/rocketmq/{v1 => v2}/admin.proto | 12 +- proto/apache/rocketmq/v2/definition.proto | 447 +++++++ proto/apache/rocketmq/v2/service.proto | 436 +++++++ src/main/cpp/admin/include/AdminClient.h | 4 +- src/main/cpp/admin/include/AdminServerImpl.h | 8 +- src/main/cpp/admin/include/AdminServiceImpl.h | 7 +- src/main/cpp/admin/include/ServerCall.h | 4 +- src/main/cpp/base/BUILD.bazel | 1 + .../cpp/base/Configuration.cpp} | 40 +- src/main/cpp/base/ConfigurationDefaults.cpp | 25 + .../CredentialsProvider.cpp | 0 src/main/cpp/base/ErrorCategory.cpp | 13 +- .../{rocketmq => base}/FilterExpression.cpp | 7 +- src/main/cpp/base/MQMessage.cpp | 190 --- src/main/cpp/base/MQMessageExt.cpp | 77 -- src/main/cpp/base/Message.cpp | 89 ++ src/main/cpp/base/MessageAccessor.cpp | 91 -- .../cpp/base/MessageExt.cpp} | 17 +- src/main/cpp/base/MetadataConstants.cpp | 42 +- src/main/cpp/base/MixAll.cpp | 37 +- src/main/cpp/base/Protocol.cpp | 136 ++- src/main/cpp/base/Tracing.cpp | 26 + src/main/cpp/base/include/InvocationContext.h | 2 +- src/main/cpp/base/include/MessageAccessor.h | 61 - src/main/cpp/base/include/MessageExt.h | 31 + src/main/cpp/base/include/MessageImpl.h | 36 - src/main/cpp/base/include/MetadataConstants.h | 3 + src/main/cpp/base/include/MixAll.h | 10 +- src/main/cpp/base/include/Protocol.h | 126 +- src/main/cpp/base/include/RetryPolicy.h | 81 ++ .../include/StsCredentialsProviderImpl.h | 0 src/main/cpp/base/include/SubscriptionEntry.h | 33 + src/main/cpp/base/tests/AssignmentTest.cpp | 23 + src/main/cpp/base/tests/BUILD.bazel | 54 + src/main/cpp/base/tests/ConfigurationTest.cpp | 54 + .../cpp/base/tests/MessageBuilderTest.cpp | 56 + src/main/cpp/base/tests/MessageQueueTest.cpp | 21 + .../cpp/base/tests/MixAllTest.cpp} | 8 +- .../cpp/base/tests/RetryPolicyTest.cpp} | 24 +- src/main/cpp/client/ClientConfigImpl.cpp | 97 -- src/main/cpp/client/ClientManagerFactory.cpp | 12 +- src/main/cpp/client/ClientManagerImpl.cpp | 1056 ++++------------- .../cpp/client/ReceiveMessageStreamReader.cpp | 118 ++ src/main/cpp/client/RpcClientImpl.cpp | 195 ++- .../SessionImpl.cpp} | 39 +- src/main/cpp/client/Signature.cpp | 26 +- src/main/cpp/client/TelemetryBidiReactor.cpp | 339 ++++++ src/main/cpp/client/TlsHelper.cpp | 214 +--- src/main/cpp/client/TopicAssignmentInfo.cpp | 57 +- src/main/cpp/client/TopicPublishInfo.cpp | 154 --- src/main/cpp/client/include/Assignment.h | 51 - src/main/cpp/client/include/Broker.h | 62 - src/main/cpp/client/include/Client.h | 35 +- src/main/cpp/client/include/ClientConfig.h | 50 +- .../cpp/client/include/ClientConfigImpl.h | 146 --- src/main/cpp/client/include/ClientManager.h | 48 +- .../cpp/client/include/ClientManagerImpl.h | 77 +- .../include/InsecureCertificateVerifier.h | 42 + .../Encoding.h => client/include/Metadata.h} | 11 +- src/main/cpp/client/include/Partition.h | 89 -- .../client/include/ReceiveMessageCallback.h | 10 +- .../client/include/ReceiveMessageContext.h | 17 +- .../cpp/client/include/ReceiveMessageResult.h | 4 +- .../include/ReceiveMessageStreamReader.h | 63 + src/main/cpp/client/include/RpcClient.h | 78 +- src/main/cpp/client/include/RpcClientImpl.h | 49 +- .../cpp/client/include/SendMessageContext.h | 16 +- src/main/cpp/client/include/ServiceAddress.h | 124 -- .../DigestType.h => client/include/Session.h} | 12 +- .../main/cpp/client/include/SessionImpl.h | 24 +- src/main/cpp/client/include/Signature.h | 2 +- .../cpp/client/include/TelemetryBidiReactor.h | 121 ++ src/main/cpp/client/include/TlsHelper.h | 33 +- .../cpp/client/include/TopicAssignmentInfo.h | 29 +- src/main/cpp/client/include/TopicRouteData.h | 47 +- src/main/cpp/client/mocks/RpcClientMock.cpp | 30 - .../client/mocks/include/ClientConfigMock.h | 39 - .../client/mocks/include/ClientManagerMock.h | 46 +- .../cpp/client/mocks/include/ClientMock.h | 12 +- .../cpp/client/mocks/include/RpcClientMock.h | 42 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 21 +- src/main/cpp/rocketmq/BUILD.bazel | 4 +- src/main/cpp/rocketmq/ClientImpl.cpp | 518 ++++---- .../rocketmq/ConsumeFifoMessageService.cpp | 166 ++- .../rocketmq/ConsumeMessageServiceBase.cpp | 10 +- .../ConsumeStandardMessageService.cpp | 193 +-- src/main/cpp/rocketmq/DefaultMQProducer.cpp | 253 ---- .../cpp/rocketmq/DefaultMQPullConsumer.cpp | 78 -- .../cpp/rocketmq/DefaultMQPushConsumer.cpp | 134 --- .../rocketmq/MessageGroupQueueSelector.cpp | 3 +- src/main/cpp/rocketmq/NamingScheme.cpp | 12 +- src/main/cpp/rocketmq/ProcessQueueImpl.cpp | 163 +-- src/main/cpp/rocketmq/Producer.cpp | 96 ++ src/main/cpp/rocketmq/ProducerImpl.cpp | 611 +++++----- src/main/cpp/rocketmq/PullConsumerImpl.cpp | 189 --- src/main/cpp/rocketmq/PushConsumer.cpp | 53 + src/main/cpp/rocketmq/PushConsumerImpl.cpp | 434 +++---- .../{SendCallbacks.cpp => SendContext.cpp} | 80 +- src/main/cpp/rocketmq/SimpleConsumer.cpp | 141 +++ src/main/cpp/rocketmq/SimpleConsumerImpl.cpp | 400 +++++++ src/main/cpp/rocketmq/TopicPublishInfo.cpp | 125 ++ src/main/cpp/rocketmq/TransactionImpl.cpp | 10 +- .../include/AsyncReceiveMessageCallback.h | 16 +- .../cpp/rocketmq/include/AwaitPullCallback.h | 58 - src/main/cpp/rocketmq/include/ClientImpl.h | 108 +- .../include/ConsumeFifoMessageService.h | 24 +- .../rocketmq/include/ConsumeMessageService.h | 4 +- .../include/ConsumeMessageServiceBase.h | 9 +- .../include/ConsumeStandardMessageService.h | 18 +- src/main/cpp/rocketmq/include/Consumer.h | 2 + .../include/MessageGroupQueueSelector.h | 9 +- src/main/cpp/rocketmq/include/NamingScheme.h | 6 +- src/main/cpp/rocketmq/include/ProcessQueue.h | 29 +- .../cpp/rocketmq/include/ProcessQueueImpl.h | 86 +- src/main/cpp/rocketmq/include/ProducerImpl.h | 95 +- .../rocketmq/include/PublishInfoCallback.h | 28 + .../cpp/rocketmq/include/PullConsumerImpl.h | 60 - src/main/cpp/rocketmq/include/PushConsumer.h | 64 - .../cpp/rocketmq/include/PushConsumerImpl.h | 131 +- src/main/cpp/rocketmq/include/SendCallbacks.h | 125 -- src/main/cpp/rocketmq/include/SendContext.h | 77 ++ .../cpp/rocketmq/include/SimpleConsumerImpl.h | 89 ++ .../include/TopicPublishInfo.h | 37 +- .../cpp/rocketmq/include/TransactionImpl.h | 37 +- .../mocks/include/ConsumeMessageServiceMock.h | 4 +- .../rocketmq/mocks/include/PushConsumerMock.h | 4 +- .../cpp/rocketmq/tests}/BUILD.bazel | 23 +- .../cpp/rocketmq/tests/SendContextTest.cpp | 17 + src/main/cpp/rocketmq/tests/TimeTest.cpp | 31 + .../scheduler => main/cpp/stats}/BUILD.bazel | 18 +- src/main/cpp/stats/PublishStats.cpp | 56 + .../cpp/stats/include/PublishStats.h} | 40 +- .../api => main/cpp/stats/tests}/BUILD.bazel | 6 +- src/main/cpp/stats/tests/PublishStatsTest.cpp | 98 ++ src/main/cpp/{tracing => trace}/BUILD.bazel | 4 +- .../cpp/{tracing => trace}/TracingUtility.cpp | 49 +- .../include/TracingUtility.h | 5 +- src/main/cpp/tracing/exporters/BUILD.bazel | 34 - .../cpp/tracing/exporters/OtlpExporter.cpp | 428 ------- .../tracing/exporters/include/OtlpExporter.h | 153 --- src/test/cpp/benchmark/BUILD.bazel | 27 - src/test/cpp/benchmark/BenchmarkTest.cpp | 34 - src/test/cpp/it/BUILD.bazel | 51 - src/test/cpp/it/README.md | 4 - src/test/cpp/it/RpcClientTest.cpp | 374 ------ src/test/cpp/it/TopAddressingTest.cpp | 167 --- src/test/cpp/it/TopicPublishInfoTest.cpp | 175 --- src/test/cpp/ut/BUILD | 1 - src/test/cpp/ut/BUILD.bazel | 17 - src/test/cpp/ut/README.md | 3 - src/test/cpp/ut/admin/AdminServerTest.cpp | 67 -- src/test/cpp/ut/admin/BUILD.bazel | 28 - src/test/cpp/ut/base/BUILD.bazel | 153 --- src/test/cpp/ut/base/HistogramTest.cpp | 59 - src/test/cpp/ut/base/HostInfoTest.cpp | 65 - src/test/cpp/ut/base/HttpClientTest.cpp | 67 -- .../cpp/ut/base/InvocationContextTest.cpp | 37 - src/test/cpp/ut/base/MQMessageExtTest.cpp | 68 -- src/test/cpp/ut/base/MQMessageTest.cpp | 60 - src/test/cpp/ut/base/MixAllTest.cpp | 120 -- src/test/cpp/ut/base/RateLimiterTest.cpp | 69 -- src/test/cpp/ut/base/ThreadPoolTest.cpp | 76 -- src/test/cpp/ut/base/TopAddressingTest.cpp | 74 -- .../cpp/ut/base/UniqueIdGeneratorTest.cpp | 46 - src/test/cpp/ut/base/UtilAllTest.cpp | 91 -- src/test/cpp/ut/client/BUILD.bazel | 86 -- .../ut/client/ClientManagerFactoryTest.cpp | 47 - src/test/cpp/ut/client/ClientManagerTest.cpp | 357 ------ src/test/cpp/ut/client/RpcClientTest.cpp | 82 -- src/test/cpp/ut/client/TlsHelperTest.cpp | 31 - .../cpp/ut/client/TopicAssignmentInfoTest.cpp | 110 -- src/test/cpp/ut/client/TracingUtilityTest.cpp | 50 - src/test/cpp/ut/concurrent/BUILD.bazel | 28 - .../cpp/ut/concurrent/CountdownLatchTest.cpp | 49 - src/test/cpp/ut/logger/LoggerTest.cpp | 109 -- src/test/cpp/ut/remoting/BUILD.bazel | 73 -- src/test/cpp/ut/remoting/BrokerDataTest.cpp | 44 - .../remoting/QueryRouteRequestHeaderTest.cpp | 36 - src/test/cpp/ut/remoting/QueueDataTest.cpp | 38 - .../cpp/ut/remoting/RemotingCommandTest.cpp | 62 - .../cpp/ut/remoting/TopicRouteDataTest.cpp | 36 - .../cpp/ut/rocketmq/AwaitPullCallbackTest.cpp | 87 -- src/test/cpp/ut/rocketmq/BUILD.bazel | 264 ----- src/test/cpp/ut/rocketmq/ClientImplTest.cpp | 126 -- .../ConsumeStandardMessageServiceTest.cpp | 117 -- .../ut/rocketmq/CredentialsProviderTest.cpp | 68 -- .../cpp/ut/rocketmq/DefaultMQProducerTest.cpp | 212 ---- .../DynamicNameServerResolverTest.cpp | 75 -- src/test/cpp/ut/rocketmq/ExecutorTest.cpp | 157 --- .../MessageGroupQueueSelectorTest.cpp | 41 - src/test/cpp/ut/rocketmq/MessageTest.cpp | 26 - src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp | 50 - src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp | 365 ------ src/test/cpp/ut/rocketmq/ProducerImplTest.cpp | 273 ----- .../cpp/ut/rocketmq/PullConsumerImplTest.cpp | 423 ------- .../cpp/ut/rocketmq/PushConsumerImplTest.cpp | 205 ---- .../cpp/ut/rocketmq/SendCallbacksTest.cpp | 43 - .../rocketmq/StaticNameServerResolverTest.cpp | 51 - .../StsCredentialsProviderImplTest.cpp | 76 -- .../cpp/ut/rocketmq/include/MQClientTest.h | 105 -- src/test/cpp/ut/scheduler/SchedulerTest.cpp | 149 --- src/test/cpp/ut/tracing/exporters/BUILD.bazel | 40 - .../tracing/exporters/OtlpExportersTest.cpp | 70 -- .../ut/tracing/exporters/SpanContextTest.cpp | 25 - tools/trouble_shooting.sh | 2 + tools/use_clang.sh | 2 + 261 files changed, 6772 insertions(+), 15046 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 api/rocketmq/AsyncCallback.h create mode 100644 api/rocketmq/BackoffPolicy.h create mode 100644 api/rocketmq/Configuration.h rename src/main/cpp/client/include/OrphanTransactionCallback.h => api/rocketmq/ConfigurationDefaults.h (81%) rename api/rocketmq/{MessageModel.h => ConsumeResult.h} (93%) delete mode 100644 api/rocketmq/ConsumeType.h delete mode 100644 api/rocketmq/DefaultMQProducer.h delete mode 100644 api/rocketmq/DefaultMQPullConsumer.h delete mode 100644 api/rocketmq/DefaultMQPushConsumer.h rename {src/main/cpp/rocketmq/include => api/rocketmq}/FilterExpression.h (85%) delete mode 100644 api/rocketmq/MQMessage.h delete mode 100644 api/rocketmq/MQMessageExt.h delete mode 100644 api/rocketmq/MQMessageQueue.h create mode 100644 api/rocketmq/Message.h delete mode 100644 api/rocketmq/OffsetStore.h create mode 100644 api/rocketmq/Producer.h delete mode 100644 api/rocketmq/PullResult.h create mode 100644 api/rocketmq/PushConsumer.h create mode 100644 api/rocketmq/SendCallback.h create mode 100644 api/rocketmq/SendReceipt.h delete mode 100644 api/rocketmq/SendResult.h create mode 100644 api/rocketmq/SimpleConsumer.h create mode 100644 api/rocketmq/Tracing.h create mode 100644 api/rocketmq/TransactionChecker.h rename api/rocketmq/{MessageType.h => TransactionState.h} (90%) delete mode 100644 example/rocketmq/ExampleAsyncProducer.cpp delete mode 100644 example/rocketmq/ExampleBroadcastPushConsumer.cpp delete mode 100644 example/rocketmq/ExamplePullConsumer.cpp delete mode 100644 example/rocketmq/ExamplePushConsumer.cpp rename {example/rocketmq => examples}/BUILD.bazel (51%) rename {example/rocketmq => examples}/BenchmarkPushConsumer.cpp (100%) create mode 100644 examples/ExampleAsyncProducer.cpp rename {example/rocketmq => examples}/ExampleFifoProducer.cpp (66%) rename {example/rocketmq => examples}/ExampleFifoPushConsumer.cpp (100%) rename {example/rocketmq => examples}/ExampleProducer.cpp (68%) create mode 100644 examples/ExamplePushConsumer.cpp create mode 100644 examples/ExampleSimpleConsumer.cpp rename {example/rocketmq => examples}/ExampleTransactionProducer.cpp (100%) rename {example/rocketmq => examples}/PushConsumerWithCustomExecutor.cpp (100%) rename {example/rocketmq => examples}/PushConsumerWithThrottle.cpp (100%) rename {example/rocketmq => examples}/SqlConsumer.cpp (100%) rename {example/rocketmq => examples}/SqlProducer.cpp (100%) delete mode 100644 proto/apache/rocketmq/v1/definition.proto delete mode 100644 proto/apache/rocketmq/v1/service.proto rename proto/apache/rocketmq/{v1 => v2}/admin.proto (86%) create mode 100644 proto/apache/rocketmq/v2/definition.proto create mode 100644 proto/apache/rocketmq/v2/service.proto rename src/{test/cpp/ut/rocketmq/FilterExpressionTest.cpp => main/cpp/base/Configuration.cpp} (54%) create mode 100644 src/main/cpp/base/ConfigurationDefaults.cpp rename src/main/cpp/{rocketmq => base}/CredentialsProvider.cpp (100%) rename src/main/cpp/{rocketmq => base}/FilterExpression.cpp (89%) delete mode 100644 src/main/cpp/base/MQMessage.cpp delete mode 100644 src/main/cpp/base/MQMessageExt.cpp create mode 100644 src/main/cpp/base/Message.cpp delete mode 100644 src/main/cpp/base/MessageAccessor.cpp rename src/{test/cpp/ut/api/ErrorCodeTest.cpp => main/cpp/base/MessageExt.cpp} (75%) create mode 100644 src/main/cpp/base/Tracing.cpp delete mode 100644 src/main/cpp/base/include/MessageAccessor.h create mode 100644 src/main/cpp/base/include/MessageExt.h delete mode 100644 src/main/cpp/base/include/MessageImpl.h create mode 100644 src/main/cpp/base/include/RetryPolicy.h rename src/main/cpp/{rocketmq => base}/include/StsCredentialsProviderImpl.h (100%) create mode 100644 src/main/cpp/base/include/SubscriptionEntry.h create mode 100644 src/main/cpp/base/tests/AssignmentTest.cpp create mode 100644 src/main/cpp/base/tests/BUILD.bazel create mode 100644 src/main/cpp/base/tests/ConfigurationTest.cpp create mode 100644 src/main/cpp/base/tests/MessageBuilderTest.cpp create mode 100644 src/main/cpp/base/tests/MessageQueueTest.cpp rename src/{test/cpp/ut/rocketmq/ConsumerTest.cpp => main/cpp/base/tests/MixAllTest.cpp} (87%) rename src/{test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp => main/cpp/base/tests/RetryPolicyTest.cpp} (65%) delete mode 100644 src/main/cpp/client/ClientConfigImpl.cpp create mode 100644 src/main/cpp/client/ReceiveMessageStreamReader.cpp rename src/main/cpp/{rocketmq/AwaitPullCallback.cpp => client/SessionImpl.cpp} (57%) create mode 100644 src/main/cpp/client/TelemetryBidiReactor.cpp delete mode 100644 src/main/cpp/client/TopicPublishInfo.cpp delete mode 100644 src/main/cpp/client/include/Assignment.h delete mode 100644 src/main/cpp/client/include/Broker.h delete mode 100644 src/main/cpp/client/include/ClientConfigImpl.h create mode 100644 src/main/cpp/client/include/InsecureCertificateVerifier.h rename src/main/cpp/{base/include/Encoding.h => client/include/Metadata.h} (89%) delete mode 100644 src/main/cpp/client/include/Partition.h rename api/rocketmq/MQSelector.h => src/main/cpp/client/include/ReceiveMessageContext.h (74%) create mode 100644 src/main/cpp/client/include/ReceiveMessageStreamReader.h delete mode 100644 src/main/cpp/client/include/ServiceAddress.h rename src/main/cpp/{base/include/DigestType.h => client/include/Session.h} (91%) rename api/rocketmq/LocalTransactionStateChecker.h => src/main/cpp/client/include/SessionImpl.h (66%) create mode 100644 src/main/cpp/client/include/TelemetryBidiReactor.h delete mode 100644 src/main/cpp/client/mocks/RpcClientMock.cpp delete mode 100644 src/main/cpp/client/mocks/include/ClientConfigMock.h delete mode 100644 src/main/cpp/rocketmq/DefaultMQProducer.cpp delete mode 100644 src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp delete mode 100644 src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp create mode 100644 src/main/cpp/rocketmq/Producer.cpp delete mode 100644 src/main/cpp/rocketmq/PullConsumerImpl.cpp create mode 100644 src/main/cpp/rocketmq/PushConsumer.cpp rename src/main/cpp/rocketmq/{SendCallbacks.cpp => SendContext.cpp} (51%) create mode 100644 src/main/cpp/rocketmq/SimpleConsumer.cpp create mode 100644 src/main/cpp/rocketmq/SimpleConsumerImpl.cpp create mode 100644 src/main/cpp/rocketmq/TopicPublishInfo.cpp delete mode 100644 src/main/cpp/rocketmq/include/AwaitPullCallback.h create mode 100644 src/main/cpp/rocketmq/include/PublishInfoCallback.h delete mode 100644 src/main/cpp/rocketmq/include/PullConsumerImpl.h delete mode 100644 src/main/cpp/rocketmq/include/PushConsumer.h delete mode 100644 src/main/cpp/rocketmq/include/SendCallbacks.h create mode 100644 src/main/cpp/rocketmq/include/SendContext.h create mode 100644 src/main/cpp/rocketmq/include/SimpleConsumerImpl.h rename src/main/cpp/{client => rocketmq}/include/TopicPublishInfo.h (56%) rename src/{test/cpp/ut/logger => main/cpp/rocketmq/tests}/BUILD.bazel (74%) create mode 100644 src/main/cpp/rocketmq/tests/SendContextTest.cpp create mode 100644 src/main/cpp/rocketmq/tests/TimeTest.cpp rename src/{test/cpp/ut/scheduler => main/cpp/stats}/BUILD.bazel (71%) create mode 100644 src/main/cpp/stats/PublishStats.cpp rename src/{test/cpp/ut/client/ClientTest.cpp => main/cpp/stats/include/PublishStats.h} (62%) rename src/{test/cpp/ut/api => main/cpp/stats/tests}/BUILD.bazel (90%) create mode 100644 src/main/cpp/stats/tests/PublishStatsTest.cpp rename src/main/cpp/{tracing => trace}/BUILD.bazel (92%) rename src/main/cpp/{tracing => trace}/TracingUtility.cpp (71%) rename src/main/cpp/{tracing => trace}/include/TracingUtility.h (82%) delete mode 100644 src/main/cpp/tracing/exporters/BUILD.bazel delete mode 100644 src/main/cpp/tracing/exporters/OtlpExporter.cpp delete mode 100644 src/main/cpp/tracing/exporters/include/OtlpExporter.h delete mode 100644 src/test/cpp/benchmark/BUILD.bazel delete mode 100644 src/test/cpp/benchmark/BenchmarkTest.cpp delete mode 100644 src/test/cpp/it/BUILD.bazel delete mode 100644 src/test/cpp/it/README.md delete mode 100644 src/test/cpp/it/RpcClientTest.cpp delete mode 100644 src/test/cpp/it/TopAddressingTest.cpp delete mode 100644 src/test/cpp/it/TopicPublishInfoTest.cpp delete mode 100644 src/test/cpp/ut/BUILD delete mode 100644 src/test/cpp/ut/BUILD.bazel delete mode 100644 src/test/cpp/ut/README.md delete mode 100644 src/test/cpp/ut/admin/AdminServerTest.cpp delete mode 100644 src/test/cpp/ut/admin/BUILD.bazel delete mode 100644 src/test/cpp/ut/base/BUILD.bazel delete mode 100644 src/test/cpp/ut/base/HistogramTest.cpp delete mode 100644 src/test/cpp/ut/base/HostInfoTest.cpp delete mode 100644 src/test/cpp/ut/base/HttpClientTest.cpp delete mode 100644 src/test/cpp/ut/base/InvocationContextTest.cpp delete mode 100644 src/test/cpp/ut/base/MQMessageExtTest.cpp delete mode 100644 src/test/cpp/ut/base/MQMessageTest.cpp delete mode 100644 src/test/cpp/ut/base/MixAllTest.cpp delete mode 100644 src/test/cpp/ut/base/RateLimiterTest.cpp delete mode 100644 src/test/cpp/ut/base/ThreadPoolTest.cpp delete mode 100644 src/test/cpp/ut/base/TopAddressingTest.cpp delete mode 100644 src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp delete mode 100644 src/test/cpp/ut/base/UtilAllTest.cpp delete mode 100644 src/test/cpp/ut/client/BUILD.bazel delete mode 100644 src/test/cpp/ut/client/ClientManagerFactoryTest.cpp delete mode 100644 src/test/cpp/ut/client/ClientManagerTest.cpp delete mode 100644 src/test/cpp/ut/client/RpcClientTest.cpp delete mode 100644 src/test/cpp/ut/client/TlsHelperTest.cpp delete mode 100644 src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp delete mode 100644 src/test/cpp/ut/client/TracingUtilityTest.cpp delete mode 100644 src/test/cpp/ut/concurrent/BUILD.bazel delete mode 100644 src/test/cpp/ut/concurrent/CountdownLatchTest.cpp delete mode 100644 src/test/cpp/ut/logger/LoggerTest.cpp delete mode 100644 src/test/cpp/ut/remoting/BUILD.bazel delete mode 100644 src/test/cpp/ut/remoting/BrokerDataTest.cpp delete mode 100644 src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp delete mode 100644 src/test/cpp/ut/remoting/QueueDataTest.cpp delete mode 100644 src/test/cpp/ut/remoting/RemotingCommandTest.cpp delete mode 100644 src/test/cpp/ut/remoting/TopicRouteDataTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/BUILD.bazel delete mode 100644 src/test/cpp/ut/rocketmq/ClientImplTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/ExecutorTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/MessageTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/ProducerImplTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp delete mode 100644 src/test/cpp/ut/rocketmq/include/MQClientTest.h delete mode 100644 src/test/cpp/ut/scheduler/SchedulerTest.cpp delete mode 100644 src/test/cpp/ut/tracing/exporters/BUILD.bazel delete mode 100644 src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp delete mode 100644 src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp create mode 100644 tools/trouble_shooting.sh create mode 100755 tools/use_clang.sh diff --git a/.bazelversion b/.bazelversion index 6aba2b245..af8c8ec7c 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -4.2.0 +4.2.2 diff --git a/.clang-format b/.clang-format index 6345fe113..fb63bcfb3 100644 --- a/.clang-format +++ b/.clang-format @@ -1,11 +1,14 @@ --- +BasedOnStyle: Google +IndentWidth: 2 +--- Language: Cpp AccessModifierOffset: -2 +BinPackParameters: false ColumnLimit: 120 DerivePointerAlignment: false PointerAlignment: Left SortIncludes: true -IndentWidth: 2 NamespaceIndentation: Inner AlwaysBreakTemplateDeclarations: true AllowShortCaseLabelsOnASingleLine: false @@ -13,6 +16,7 @@ AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: false IndentCaseBlocks: false IndentCaseLabels: true +ReflowComments: true ... --- diff --git a/.gitignore b/.gitignore index f3ec0b2d6..23e0e933d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ bazel-testlogs cmake-build-debug .clwb compile_commands.json -.vscode logs _build cmake-build-debug-coverage @@ -15,3 +14,7 @@ cmake-build-debug-coverage external bazel-apache_rocketmq_client_cpp bazel-rocketmq-client-cpp +/external +/bazel-* +/compile_commands.json +/.cache/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..cb613f96a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "C_Cpp.intelliSenseEngine": "Disabled", + "C_Cpp.autocomplete": "Disabled", // So you don't get autocomplete from both extensions. + "C_Cpp.errorSquiggles": "Disabled", // So you don't get error squiggles from both extensions (clangd's seem to be more reliable anyway). + "clangd.arguments": [ + "-log=verbose", + "-pretty", + "--background-index", + "--header-insertion=never", + "--compile-commands-dir=${workspaceFolder}/", + "--query-driver=/**/*", + ] +} \ No newline at end of file diff --git a/WORKSPACE b/WORKSPACE index 86484441d..644b5d7a9 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -14,4 +14,7 @@ rules_proto_grpc_repos() load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() -rules_proto_toolchains() \ No newline at end of file +rules_proto_toolchains() + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") +hedron_compile_commands_setup() \ No newline at end of file diff --git a/api/BUILD.bazel b/api/BUILD.bazel index b7eb594a7..f5d75d558 100644 --- a/api/BUILD.bazel +++ b/api/BUILD.bazel @@ -23,5 +23,6 @@ cc_library( visibility = ["//visibility:public"], deps = [ "@com_google_absl//absl/strings", + "@io_opencensus_cpp//opencensus/trace", ], ) \ No newline at end of file diff --git a/api/rocketmq/AsyncCallback.h b/api/rocketmq/AsyncCallback.h deleted file mode 100644 index 8004ba027..000000000 --- a/api/rocketmq/AsyncCallback.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "MQClientException.h" -#include "PullResult.h" -#include "SendResult.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class AsyncCallback { -public: - virtual ~AsyncCallback() = default; -}; - -class SendCallback : public AsyncCallback { -public: - ~SendCallback() override = default; - - virtual void onSuccess(SendResult& send_result) noexcept = 0; - - virtual void onFailure(const std::error_code& ec) noexcept = 0; -}; - -class PullCallback : public AsyncCallback { -public: - ~PullCallback() override = default; - - virtual void onSuccess(const PullResult& pull_result) noexcept = 0; - - virtual void onFailure(const std::error_code& ec) noexcept = 0; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/BackoffPolicy.h b/api/rocketmq/BackoffPolicy.h new file mode 100644 index 000000000..89aa7d17d --- /dev/null +++ b/api/rocketmq/BackoffPolicy.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "RocketMQ.h" +#include +#include +#include + +ROCKETMQ_NAMESPACE_BEGIN + +class BackoffPolicy { +public: + virtual ~BackoffPolicy() = default; + + virtual std::size_t maxAttempts() const = 0; + + virtual std::chrono::milliseconds backoff(std::size_t attempt) const = 0; +}; + +class ExponentialBackoffPolicy : public BackoffPolicy { +public: + ExponentialBackoffPolicy(std::size_t max_attempts, std::chrono::milliseconds initial_backoff, + std::chrono::milliseconds max_backoff, std::size_t multiplier) + : max_attempts_(max_attempts), initial_backoff_(initial_backoff), max_backoff_(max_backoff), + multiplier_(multiplier) { + } + + std::size_t maxAttempts() const override { + return max_attempts_; + } + + std::chrono::milliseconds backoff(std::size_t attempt) const override { + if (attempt <= 1) { + return initial_backoff_; + } + + if (attempt >= max_attempts_) { + return max_backoff_; + } + + auto duration = initial_backoff_ * static_cast(std::pow(multiplier_, attempt)); + if (duration > max_backoff_) { + return max_backoff_; + } + return duration; + } + +private: + std::size_t max_attempts_; + std::chrono::milliseconds initial_backoff_; + std::chrono::milliseconds max_backoff_; + std::size_t multiplier_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Configuration.h b/api/rocketmq/Configuration.h new file mode 100644 index 000000000..90cdf7d27 --- /dev/null +++ b/api/rocketmq/Configuration.h @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "ConfigurationDefaults.h" +#include "CredentialsProvider.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConfigurationBuilder; + +class Configuration { +public: + static ConfigurationBuilder newBuilder(); + + const std::string& endpoints() const { + return endpoints_; + } + + CredentialsProviderPtr credentialsProvider() const { + return credentials_provider_; + } + + std::chrono::milliseconds requestTimeout() const { + return request_timeout_; + } + +protected: + friend class ConfigurationBuilder; + + Configuration() = default; + +private: + std::string endpoints_; + CredentialsProviderPtr credentials_provider_; + std::chrono::milliseconds request_timeout_{ConfigurationDefaults::RequestTimeout}; +}; + +class ConfigurationBuilder { +public: + ConfigurationBuilder& withEndpoints(std::string endpoints); + + ConfigurationBuilder& withCredentialsProvider(std::shared_ptr provider); + + ConfigurationBuilder& withRequestTimeout(std::chrono::milliseconds request_timeout); + + Configuration build(); + +private: + Configuration configuration_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/OrphanTransactionCallback.h b/api/rocketmq/ConfigurationDefaults.h similarity index 81% rename from src/main/cpp/client/include/OrphanTransactionCallback.h rename to api/rocketmq/ConfigurationDefaults.h index 0e78db11c..08a256765 100644 --- a/src/main/cpp/client/include/OrphanTransactionCallback.h +++ b/api/rocketmq/ConfigurationDefaults.h @@ -16,15 +16,19 @@ */ #pragma once -#include "rocketmq/MQMessage.h" +#include + +#include "RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN -class OrphanTransactionCallback { +class ConfigurationDefaults { public: - virtual ~OrphanTransactionCallback() = default; - - virtual void onOrphanTransaction(const MQMessage& message) = 0; + /** + * @brief Default request-timeout: 3.0 seconds. + * + */ + static const std::chrono::milliseconds RequestTimeout; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageModel.h b/api/rocketmq/ConsumeResult.h similarity index 93% rename from api/rocketmq/MessageModel.h rename to api/rocketmq/ConsumeResult.h index 2422d32b9..5dcdf77ca 100644 --- a/api/rocketmq/MessageModel.h +++ b/api/rocketmq/ConsumeResult.h @@ -16,16 +16,15 @@ */ #pragma once -#include "RocketMQ.h" - #include +#include "RocketMQ.h" + ROCKETMQ_NAMESPACE_BEGIN -enum class MessageModel : int8_t -{ - BROADCASTING, - CLUSTERING, +enum class ConsumeResult : uint8_t { + SUCCESS = 0, + FAILURE = 1 }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ConsumeType.h b/api/rocketmq/ConsumeType.h deleted file mode 100644 index 4cbf5a2fa..000000000 --- a/api/rocketmq/ConsumeType.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include - -#include "MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum ConsumeType -{ - CONSUME_ACTIVELY, - CONSUME_PASSIVELY, -}; - -enum ConsumeFromWhere -{ - CONSUME_FROM_LAST_OFFSET, - CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST, - CONSUME_FROM_MIN_OFFSET, - CONSUME_FROM_MAX_OFFSET, - CONSUME_FROM_FIRST_OFFSET, - CONSUME_FROM_TIMESTAMP, -}; - -enum ConsumeInitialMode -{ - MIN, - MAX, -}; - -enum QueryOffsetPolicy : uint8_t -{ - BEGINNING = 0, - END = 1, - TIME_POINT = 2, -}; - -struct OffsetQuery { - MQMessageQueue message_queue; - QueryOffsetPolicy policy; - std::chrono::system_clock::time_point time_point; -}; - -struct PullMessageQuery { - MQMessageQueue message_queue; - int64_t offset; - int32_t batch_size; - std::chrono::system_clock::duration await_time; - std::chrono::system_clock::duration timeout; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQProducer.h b/api/rocketmq/DefaultMQProducer.h deleted file mode 100644 index 97306d538..000000000 --- a/api/rocketmq/DefaultMQProducer.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include - -#include "AsyncCallback.h" -#include "CredentialsProvider.h" -#include "ErrorCode.h" -#include "LocalTransactionStateChecker.h" -#include "Logger.h" -#include "MQMessage.h" -#include "MQSelector.h" -#include "SendResult.h" - -ROCKETMQ_NAMESPACE_BEGIN - -/** - * This class employs pointer-to-implementation paradigm to achieve the goal of stable ABI. - * Refer https://en.cppreference.com/w/cpp/language/pimpl for an explanation. - */ -class ProducerImpl; - -class DefaultMQProducer { -public: - explicit DefaultMQProducer(const std::string& group_name); - - ~DefaultMQProducer() = default; - - void start(); - - void shutdown(); - - /** - * Acquire previous send timeout in milliseconds. - * @return Send timeout in milliseconds - */ - std::chrono::milliseconds getSendMsgTimeout() const; - - /** - * Set default send message timeout in milliseconds. - * @param timeout_millis Timeout used when sending messages. - */ - void setSendMsgTimeout(std::chrono::milliseconds timeout); - - SendResult send(MQMessage& message, const std::string& message_group); - - /** - * Send message in synchronous manner. - * @param message Message to send. - * @param filter_active_broker Do NOT rely on this parameter. it has been deprecated. - */ - SendResult send(const MQMessage& message, bool filter_active_broker = true); - - SendResult send(const MQMessage& message, std::error_code& ec) noexcept; - - SendResult send(MQMessage& message, const MQMessageQueue& message_queue); - SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg); - - /** - * @brief This function is deprecated. - * - * @param message - * @param selector - * @param arg - * @param retry_times retry_times is ignored with respect to member retry_times setting. - * @param select_active_broker - * @return SendResult - */ - SendResult send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, - bool select_active_broker = false); - - /** - * Send message in asynchronous manner. - * @param message Message to send. - * @param send_callback Callback to execute on completion of message sending. - * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. - */ - void send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker = false); - void send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback); - void send(MQMessage& message, MessageQueueSelector* selector, void* arg, SendCallback* send_callback); - - /** - * send message in Oneway(The implementation is simply ignore the result of send message in synchronous). - * @param message Message to send. - * @param select_active_broker Do NOT rely on this parameter. it has been deprecated. - */ - void sendOneway(const MQMessage& message, bool select_active_broker = false); - void sendOneway(MQMessage& message, const MQMessageQueue& message_queue); - void sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg); - - void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); - - void setNamesrvAddr(const std::string& name_server_address_list); - - void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); - - void setGroupName(const std::string& group_name); - - void setInstanceName(const std::string& instance_name); - - void enableTracing(bool enabled); - - bool isTracingEnabled(); - - /** - * Number of attempts before claiming a send action as failure. By default, 3 attempts will be performed for sync and - * async send methods. - * - * @return - */ - int getMaxAttemptTimes() const; - - void setMaxAttemptTimes(int max_attempt_times); - - std::vector getTopicMessageQueueInfo(const std::string& topic); - - void setUnitName(std::string unit_name); - - const std::string& getUnitName(); - - uint32_t compressBodyThreshold() const; - void compressBodyThreshold(uint32_t threshold); - - void setResourceNamespace(const std::string& resource_namespace); - - void setCredentialsProvider(CredentialsProviderPtr credentials_provider); - - void setRegion(const std::string& region); - - TransactionPtr prepare(MQMessage& message); - -private: - std::shared_ptr impl_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/DefaultMQPullConsumer.h b/api/rocketmq/DefaultMQPullConsumer.h deleted file mode 100644 index 9bbfc6031..000000000 --- a/api/rocketmq/DefaultMQPullConsumer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include -#include - -#include "AsyncCallback.h" -#include "ConsumeType.h" -#include "CredentialsProvider.h" -#include "MQMessageExt.h" -#include "MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PullConsumerImpl; - -class DefaultMQPullConsumer { -public: - explicit DefaultMQPullConsumer(const std::string& group_name); - - void start(); - - void shutdown(); - - std::future> queuesFor(const std::string& topic); - - std::future queryOffset(const OffsetQuery& query); - - bool pull(const PullMessageQuery& request, PullResult& pull_result); - - void pull(const PullMessageQuery& request, PullCallback* callback); - - void setResourceNamespace(const std::string& resource_namespace); - - void setNamesrvAddr(const std::string& name_srv); - - void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); - - void setCredentialsProvider(std::shared_ptr credentials_provider); - -private: - std::shared_ptr impl_; -}; - -ROCKETMQ_NAMESPACE_END diff --git a/api/rocketmq/DefaultMQPushConsumer.h b/api/rocketmq/DefaultMQPushConsumer.h deleted file mode 100644 index 0693b8d3c..000000000 --- a/api/rocketmq/DefaultMQPushConsumer.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "AsyncCallback.h" -#include "ConsumeType.h" -#include "CredentialsProvider.h" -#include "ExpressionType.h" -#include "Logger.h" -#include "MQMessageQueue.h" -#include "MessageListener.h" -#include "rocketmq/Executor.h" -#include "rocketmq/MessageModel.h" -#include "rocketmq/OffsetStore.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PushConsumerImpl; - -class DefaultMQPushConsumer { -public: - explicit DefaultMQPushConsumer(const std::string& group_name); - - ~DefaultMQPushConsumer() = default; - - void start(); - - void shutdown(); - - void subscribe(const std::string& topic, const std::string& expression, - ExpressionType expression_type = ExpressionType::TAG); - - void setConsumeFromWhere(ConsumeFromWhere policy); - - void registerMessageListener(MessageListener* listener); - - void setNamesrvAddr(const std::string& name_srv); - - void setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint); - - void setGroupName(const std::string& group_name); - - void setConsumeThreadCount(int thread_count); - - void setInstanceName(const std::string& instance_name); - - int getProcessQueueTableSize(); - - void setUnitName(std::string unit_name); - - const std::string& getUnitName() const; - - void enableTracing(bool enabled); - - bool isTracingEnabled(); - - /** - * SDK of this version always uses asynchronous IO operation. As such, this - * function is no-op to keep backward compatibility. - */ - void setAsyncPull(bool); - - /** - * Maximum number of messages passed to each callback. - * @param batch_size Batch size - */ - void setConsumeMessageBatchMaxSize(int batch_size); - - /** - * Lifecycle of executor is managed by external application. Passed-in - * executor should remain valid after consumer start and before stopping. - * @param executor Executor pool used to invoke consume callback. - */ - void setCustomExecutor(const Executor& executor); - - /** - * This function sets maximum number of message that may be consumed per - * second. - * @param topic Topic to control - * @param threshold Threshold before throttling is enforced. - */ - void setThrottle(const std::string& topic, uint32_t threshold); - - /** - * Set abstract-resource-namespace, in which canonical name of topic, group - * remains unique. - * @param resource_namespace Abstract resource namespace. - */ - void setResourceNamespace(const std::string& resource_namespace); - - void setCredentialsProvider(CredentialsProviderPtr credentials_provider); - - void setMessageModel(MessageModel message_model); - - std::string groupName() const; - - void setOffsetStore(std::unique_ptr offset_store); - -private: - std::shared_ptr impl_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/ErrorCode.h b/api/rocketmq/ErrorCode.h index c3ebb7a39..5068c83c2 100644 --- a/api/rocketmq/ErrorCode.h +++ b/api/rocketmq/ErrorCode.h @@ -24,8 +24,7 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class ErrorCode : int -{ +enum class ErrorCode : int { Success = 0, /** @@ -34,6 +33,11 @@ enum class ErrorCode : int */ IllegalState = 1, + /** + * @brief Broker has processed the request but is not going to return any content. + */ + NoContent = 204, + /** * @brief Bad configuration. For example, negative max-attempt-times. * @@ -48,6 +52,11 @@ enum class ErrorCode : int */ BadRequest = 400, + /** + * @brief To publish FIFO messages, only synchronous API is supported. + */ + BadRequestAsyncPubFifoMessage = 40001, + /** * @brief Authentication failed. Possibly caused by invalid credentials. * @@ -68,6 +77,10 @@ enum class ErrorCode : int */ NotFound = 404, + TopicNotFound = 404001, + + GroupNotFound = 404002, + /** * @brief Timeout when connecting, reading from or writing to brokers. * diff --git a/src/main/cpp/rocketmq/include/FilterExpression.h b/api/rocketmq/FilterExpression.h similarity index 85% rename from src/main/cpp/rocketmq/include/FilterExpression.h rename to api/rocketmq/FilterExpression.h index 51cd89a00..19e910a48 100644 --- a/src/main/cpp/rocketmq/include/FilterExpression.h +++ b/api/rocketmq/FilterExpression.h @@ -18,8 +18,8 @@ #include -#include "rocketmq/ExpressionType.h" -#include "rocketmq/MQMessageExt.h" +#include "ExpressionType.h" +#include "Message.h" ROCKETMQ_NAMESPACE_BEGIN @@ -27,14 +27,14 @@ ROCKETMQ_NAMESPACE_BEGIN * Server supported message filtering expression. At present, two types are supported: tag and SQL92. */ struct FilterExpression { - explicit FilterExpression(std::string expression, ExpressionType expression_type = ExpressionType::TAG) + FilterExpression(std::string expression, ExpressionType expression_type = ExpressionType::TAG) : content_(std::move(expression)), type_(expression_type), version_(std::chrono::steady_clock::now()) { if (ExpressionType::TAG == type_ && content_.empty()) { content_ = WILD_CARD_TAG; } } - bool accept(const MQMessageExt& message) const; + bool accept(const Message& message) const; std::string content_; ExpressionType type_; diff --git a/api/rocketmq/MQMessage.h b/api/rocketmq/MQMessage.h deleted file mode 100644 index e5d27fa2e..000000000 --- a/api/rocketmq/MQMessage.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "absl/strings/string_view.h" - -#include "MQMessageQueue.h" -#include "MessageType.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MessageImpl; - -class MessageAccessor; - -class MQMessage { -public: - MQMessage(); - MQMessage(const std::string& topic, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& body); - MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, const std::string& body); - - virtual ~MQMessage(); - - MQMessage(const MQMessage& other); - MQMessage& operator=(const MQMessage& other); - - const std::string& getMsgId() const; - - void setProperty(const std::string& name, const std::string& value); - std::string getProperty(const std::string& name) const; - - const std::string& getTopic() const; - void setTopic(const std::string& topic); - void setTopic(const char* data, int len); - - std::string getTags() const; - void setTags(const std::string& tags); - - const std::vector& getKeys() const; - - /** - * @brief Add a unique key for the message - * TODO: a message may be associated with multiple keys. setKey, actually mean - * attach the given key to the message. Better rename it. - * @param key Unique key in perspective of bussiness logic. - */ - void setKey(const std::string& key); - void setKeys(const std::vector& keys); - - int getDelayTimeLevel() const; - void setDelayTimeLevel(int level); - - const std::string& traceContext() const; - void traceContext(const std::string& trace_context); - - std::string getBornHost() const; - - std::chrono::system_clock::time_point deliveryTimestamp() const; - - const std::string& getBody() const; - void setBody(const char* data, int len); - void setBody(const std::string& body); - - uint32_t bodyLength() const; - - const std::map& getProperties() const; - void setProperties(const std::map& properties); - - void messageType(MessageType message_type); - MessageType messageType() const; - - void bindMessageGroup(absl::string_view message_group); - - void bindMessageQueue(const MQMessageQueue& message_queue) { - message_queue_ = message_queue; - } - - const std::string& messageGroup() const; - - const MQMessageQueue& messageQueue() const { - return message_queue_; - } - -protected: - MessageImpl* impl_; - - friend class MessageAccessor; - -private: - MQMessageQueue message_queue_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageExt.h b/api/rocketmq/MQMessageExt.h deleted file mode 100644 index ed95484fc..000000000 --- a/api/rocketmq/MQMessageExt.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "MQMessage.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MessageAccessor; - -class MQMessageExt : public MQMessage { -public: - MQMessageExt(); - - MQMessageExt(const MQMessageExt& other); - - MQMessageExt& operator=(const MQMessageExt& other); - - int32_t getQueueId() const; - - /** - * @return Milli-seconds since epoch in perspective of system_clock. - */ - int64_t getBornTimestamp() const; - - std::chrono::system_clock::time_point bornTimestamp() const; - - std::chrono::system_clock::time_point storeTimestamp() const; - int64_t getStoreTimestamp() const; - - std::string getStoreHost() const; - - int64_t getQueueOffset() const; - - int32_t getDeliveryAttempt() const; - - const std::string& receiptHandle() const; - - bool operator==(const MQMessageExt& other); - - friend class MessageAccessor; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQMessageQueue.h b/api/rocketmq/MQMessageQueue.h deleted file mode 100644 index ec62b3d8f..000000000 --- a/api/rocketmq/MQMessageQueue.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include - -#include "RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MQMessageQueue { -public: - MQMessageQueue() = default; - - MQMessageQueue(std::string topic, std::string broker_name, int queue_id) - : topic_(std::move(topic)), broker_name_(std::move(broker_name)), queue_id_(queue_id) { - } - - MQMessageQueue(const MQMessageQueue& other) { - this->operator=(other); - } - - MQMessageQueue(MQMessageQueue&& other) noexcept { - topic_ = std::move(other.topic_); - broker_name_ = std::move(other.broker_name_); - queue_id_ = other.queue_id_; - service_address_ = other.service_address_; - } - - MQMessageQueue& operator=(const MQMessageQueue& other) { - if (this == &other) { - return *this; - } - - topic_ = other.topic_; - broker_name_ = other.broker_name_; - queue_id_ = other.queue_id_; - service_address_ = other.service_address_; - return *this; - } - - const std::string& getTopic() const { - return topic_; - } - - void setTopic(const std::string& topic) { - topic_ = topic; - } - - const std::string& getBrokerName() const { - return broker_name_; - } - - void setBrokerName(const std::string& broker_name) { - broker_name_ = broker_name; - } - - int getQueueId() const { - return queue_id_; - } - - void setQueueId(int queue_id) { - queue_id_ = queue_id; - } - - bool operator!=(const MQMessageQueue& rhs) const { - return topic_ != rhs.topic_ || broker_name_ != rhs.broker_name_ || queue_id_ != rhs.queue_id_; - } - - bool operator==(const MQMessageQueue& mq) const { - return topic_ == mq.topic_ && broker_name_ == mq.broker_name_ && queue_id_ == mq.queue_id_; - } - - bool operator<(const MQMessageQueue& mq) const { - if (topic_ != mq.topic_) { - return topic_ < mq.topic_; - } - - if (broker_name_ != mq.broker_name_) { - return broker_name_ < mq.broker_name_; - } - - return queue_id_ < mq.queue_id_; - } - - operator bool() const { - return !topic_.empty() && !broker_name_.empty() && queue_id_ >= 0; - } - - int compareTo(const MQMessageQueue& mq) const { - if (this == &mq) { - return 0; - } - - if (this->operator<(mq)) { - return -1; - } - - if (mq.operator<(*this)) { - return 1; - } - - return 0; - } - - std::string simpleName() const { - return topic_ + "_" + broker_name_ + "_" + std::to_string(queue_id_); - } - - std::string toString() const { - std::stringstream ss; - ss << "MessageQueue [topic=" << topic_ << ", brokerName=" << broker_name_ << ", queueId=" << queue_id_ << "]"; - return ss.str(); - } - - template - friend H AbslHashValue(H h, const MQMessageQueue& mq) { - return H::combine(std::move(h), mq.topic_, mq.broker_name_, mq.queue_id_); - } - - const std::string& serviceAddress() const { - return service_address_; - } - - void serviceAddress(std::string service_address) { - service_address_ = std::move(service_address); - } - -private: - std::string topic_; - std::string broker_name_; - int queue_id_{0}; - - std::string service_address_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Message.h b/api/rocketmq/Message.h new file mode 100644 index 000000000..0537fa4d6 --- /dev/null +++ b/api/rocketmq/Message.h @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RocketMQ.h" +#include "absl/types/optional.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageBuilder; + +/** + * @brief Extension is intended for internal use only, which is subject to change without notice. + */ +struct Extension { + std::chrono::system_clock::time_point store_time{std::chrono::system_clock::now()}; + std::string store_host; + std::chrono::system_clock::time_point delivery_timepoint{std::chrono::system_clock::now()}; + std::uint16_t delivery_attempt{0}; + std::chrono::system_clock::time_point decode_time{std::chrono::system_clock::now()}; + std::chrono::system_clock::duration invisible_period{0}; + std::string receipt_handle; + std::string target_endpoint; + std::int32_t queue_id{0}; + std::int64_t offset{0}; + std::string nonce; + std::string transaction_id; +}; + +class Message { +public: + const std::string& id() const { + return id_; + } + + const std::string& topic() const { + return topic_; + } + + absl::optional tag() const { + if (tag_.empty()) { + return {}; + } + return absl::make_optional(tag_); + } + + const std::vector& keys() const { + return keys_; + } + + absl::optional traceContext() const { + if (trace_context_.empty()) { + return {}; + } + return absl::make_optional(trace_context_); + } + + const std::string& bornHost() const { + return born_host_; + } + + const std::chrono::system_clock::time_point& bornTime() const { + return born_time_; + } + + absl::optional deliveryTimestamp() const { + return delivery_timestamp_; + } + + const std::string& body() const { + return body_; + } + + const std::unordered_map& properties() const { + return properties_; + } + + absl::optional group() const { + if (group_.empty()) { + return {}; + } + return absl::make_optional(group_); + } + + const Extension& extension() const { + return extension_; + } + + Extension& mutableExtension() { + return extension_; + } + + static MessageBuilder newBuilder(); + +protected: + friend std::unique_ptr absl::make_unique(); + friend class MessageBuilder; + + Message(); + +private: + std::string id_; + std::string topic_; + std::string tag_; + std::vector keys_; + std::string trace_context_; + std::string born_host_; + std::chrono::system_clock::time_point born_time_{std::chrono::system_clock::now()}; + absl::optional delivery_timestamp_; + std::string body_; + std::unordered_map properties_; + std::string group_; + Extension extension_; +}; + +using MessagePtr = std::unique_ptr; +using MessageConstPtr = std::unique_ptr; +using MessageConstSharedPtr = std::shared_ptr; +using MessageSharedPtr = std::shared_ptr; + +class MessageBuilder { +public: + MessageBuilder(); + + MessageBuilder& withTopic(std::string topic); + + MessageBuilder& withTag(std::string tag); + + MessageBuilder& withKeys(std::vector keys); + + MessageBuilder& withTraceContext(std::string trace_context); + + MessageBuilder& withBody(std::string body); + + MessageBuilder& withGroup(std::string group); + + MessageBuilder& withProperties(std::unordered_map properties); + + MessageConstPtr build(); + +private: + friend class ClientManagerImpl; + + MessageBuilder& withId(std::string id); + + MessageBuilder& withBornTime(std::chrono::system_clock::time_point born_time); + + MessageBuilder& withBornHost(std::string born_host); + + MessagePtr message_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageListener.h b/api/rocketmq/MessageListener.h index 5fdf59d2b..cd5ef056e 100644 --- a/api/rocketmq/MessageListener.h +++ b/api/rocketmq/MessageListener.h @@ -16,46 +16,13 @@ */ #pragma once -#include "MQMessageExt.h" -#include "MQMessageQueue.h" +#include -ROCKETMQ_NAMESPACE_BEGIN - -enum class ConsumeMessageResult : uint8_t -{ - SUCCESS = 0, - FAILURE = 1 -}; - -enum class MessageListenerType : uint8_t -{ - STANDARD = 0, - FIFO = 1 -}; - -class MessageListener { -public: - virtual ~MessageListener() = default; +#include "ConsumeResult.h" +#include "Message.h" - virtual MessageListenerType listenerType() = 0; -}; - -class StandardMessageListener : public MessageListener { -public: - virtual ConsumeMessageResult consumeMessage(const std::vector& msgs) = 0; - - MessageListenerType listenerType() override { - return MessageListenerType::STANDARD; - } -}; - -class FifoMessageListener : public MessageListener { -public: - MessageListenerType listenerType() override { - return MessageListenerType::FIFO; - } +ROCKETMQ_NAMESPACE_BEGIN - virtual ConsumeMessageResult consumeMessage(const MQMessageExt& msgs) = 0; -}; +using MessageListener = std::function; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/OffsetStore.h b/api/rocketmq/OffsetStore.h deleted file mode 100644 index 514d5da84..000000000 --- a/api/rocketmq/OffsetStore.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class OffsetStore { -public: - virtual ~OffsetStore() = default; - - virtual void load() = 0; - - virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; - - virtual bool readOffset(const MQMessageQueue& message_queue, int64_t& offset) = 0; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Producer.h b/api/rocketmq/Producer.h new file mode 100644 index 000000000..cb70065b5 --- /dev/null +++ b/api/rocketmq/Producer.h @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include "Configuration.h" +#include "ErrorCode.h" +#include "Logger.h" +#include "Message.h" +#include "SendCallback.h" +#include "SendReceipt.h" +#include "TransactionChecker.h" + +ROCKETMQ_NAMESPACE_BEGIN + +/** + * This class employs pointer-to-implementation paradigm to achieve the goal of stable ABI. + * Refer https://en.cppreference.com/w/cpp/language/pimpl for an explanation. + */ +class ProducerImpl; + +class ProducerBuilder; + +/** + * @brief + * + */ +class Producer { +public: + static ProducerBuilder newBuilder(); + + /** + * @brief Send message synchronously. + * + * @param message Message to publish. Note the pointer, std::unique_ptr, is 'moved' into. + * @param ec Error code and message + * @return SendReceipt Receipt of the pub action if successful, which holds an identifier for the message. + */ + SendReceipt send(MessageConstPtr message, std::error_code& ec) noexcept; + + /** + * @brief Send message in asynchronous manner. + * + * @param message Message to send. Note the pointer, std::unique_ptr, is 'moved' into. + * @param callback Callback to execute on completion. + */ + void send(MessageConstPtr message, const SendCallback& callback) noexcept; + +private: + explicit Producer(std::shared_ptr impl) : impl_(std::move(impl)) { + } + + void start(); + + std::shared_ptr impl_; + + friend class ProducerBuilder; +}; + +class ProducerBuilder { +public: + ProducerBuilder(); + + ProducerBuilder& withConfiguration(Configuration configuration); + + ProducerBuilder& withTopics(const std::vector& topics); + + ProducerBuilder& withTransactionChecker(const TransactionChecker& checker); + + Producer build(); + +private: + std::shared_ptr impl_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/PullResult.h b/api/rocketmq/PullResult.h deleted file mode 100644 index 2e91ec4c8..000000000 --- a/api/rocketmq/PullResult.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "MQMessageExt.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PullResult { -public: - PullResult(int64_t min, int64_t max, int64_t next, std::vector messages) - : min_(min), max_(max), next_(next), messages_(std::move(messages)) { - } - - int64_t min() const { - return min_; - } - - int64_t max() const { - return max_; - } - - int64_t next() const { - return next_; - } - - const std::vector& messages() const { - return messages_; - } - -private: - int64_t min_; - int64_t max_; - int64_t next_; - std::vector messages_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/PushConsumer.h b/api/rocketmq/PushConsumer.h new file mode 100644 index 000000000..500c34b0f --- /dev/null +++ b/api/rocketmq/PushConsumer.h @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "Configuration.h" +#include "CredentialsProvider.h" +#include "Executor.h" +#include "ExpressionType.h" +#include "FilterExpression.h" +#include "Logger.h" +#include "MessageListener.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerImpl; +class PushConsumerBuilder; + +class PushConsumer { +public: + static PushConsumerBuilder newBuilder(); + + void subscribe(std::string topic, FilterExpression filter_expression); + + void unsubscribe(const std::string& topic); + +private: + friend class PushConsumerBuilder; + + PushConsumer(std::shared_ptr impl) + : impl_(std::move(impl)) { + } + + std::shared_ptr impl_; +}; + +class PushConsumerBuilder { +public: + PushConsumerBuilder() : configuration_(Configuration::newBuilder().build()) {} + + PushConsumerBuilder &withConfiguration(Configuration configuration) { + configuration_ = std::move(configuration); + return *this; + } + + PushConsumerBuilder &withGroup(std::string group) { + group_ = std::move(group); + return *this; + } + + PushConsumerBuilder &withConsumeThreads(std::size_t consume_thread) { + consume_thread_ = consume_thread; + return *this; + } + + PushConsumerBuilder &withListener(MessageListener listener) { + listener_ = std::move(listener); + return *this; + } + + PushConsumerBuilder &subscribe(std::string topic, + FilterExpression filter_expression) { + subscriptions_.insert({topic, filter_expression}); + return *this; + } + + PushConsumer build(); + +private: + std::string group_; + Configuration configuration_; + std::size_t consume_thread_; + MessageListener listener_; + + std::unordered_map subscriptions_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SendCallback.h b/api/rocketmq/SendCallback.h new file mode 100644 index 000000000..8468f6354 --- /dev/null +++ b/api/rocketmq/SendCallback.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "RocketMQ.h" +#include "SendReceipt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using SendCallback = std::function; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SendReceipt.h b/api/rocketmq/SendReceipt.h new file mode 100644 index 000000000..06a01ccbf --- /dev/null +++ b/api/rocketmq/SendReceipt.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct SendReceipt { + std::string message_id; + + std::string transaction_id; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SendResult.h b/api/rocketmq/SendResult.h deleted file mode 100644 index b491cc629..000000000 --- a/api/rocketmq/SendResult.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum class SendStatus : int8_t -{ - SEND_OK, - - // Deprecated. Remove in the next release. - SEND_FLUSH_DISK_TIMEOUT, - - // Deprecated. Remove in the next release. - SEND_FLUSH_SLAVE_TIMEOUT, - - // Deprecated. Remove in the next release. - SEND_SLAVE_NOT_AVAILABLE -}; - -class SendResult { -public: - SendResult() = default; - - SendResult(const SendStatus& send_status, std::string msg_id, MQMessageQueue message_queue, long long queue_offset, - std::string region_id) - : send_status_(send_status), message_id_(std::move(msg_id)), message_queue_(std::move(message_queue)), - queue_offset_(queue_offset), region_id_(std::move(region_id)) { - } - - SendResult(const SendResult& other) { - this->operator=(other); - } - - SendResult& operator=(const SendResult& other) { - if (this == &other) { - return *this; - } - - send_status_ = other.send_status_; - message_id_ = other.message_id_; - message_queue_ = other.message_queue_; - queue_offset_ = other.queue_offset_; - transaction_id_ = other.transaction_id_; - region_id_ = other.region_id_; - return *this; - } - - const std::string& getMsgId() const { - return message_id_; - } - - void setMsgId(const std::string& msg_id) { - message_id_ = msg_id; - } - - const std::string& getRegionId() const { - return region_id_; - } - - void setRegionId(const std::string& region_id) { - region_id_ = region_id; - } - - SendStatus getSendStatus() const { - return send_status_; - } - - void setSendStatus(const SendStatus& send_status) { - send_status_ = send_status; - } - - MQMessageQueue& getMessageQueue() const { - return message_queue_; - } - - void setMessageQueue(const MQMessageQueue& message_queue) { - message_queue_ = message_queue; - } - - long long getQueueOffset() const { - return queue_offset_; - } - - void setQueueOffset(long long queue_offset) { - queue_offset_ = queue_offset; - } - - const std::string& getTransactionId() const { - return transaction_id_; - } - - void setTransactionId(const std::string& transaction_id) { - transaction_id_ = transaction_id; - } - - const std::string& traceContext() const { - return trace_context_; - } - - void traceContext(std::string trace_context) { - trace_context_ = std::move(trace_context); - } - - operator bool() { - return !message_id_.empty(); - } - -private: - SendStatus send_status_{SendStatus::SEND_OK}; - std::string message_id_; - mutable MQMessageQueue message_queue_; - long long queue_offset_{0}; - std::string transaction_id_; - std::string region_id_; - std::string trace_context_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/SimpleConsumer.h b/api/rocketmq/SimpleConsumer.h new file mode 100644 index 000000000..0f7030fd6 --- /dev/null +++ b/api/rocketmq/SimpleConsumer.h @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Configuration.h" +#include "FilterExpression.h" +#include "Message.h" +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using ReceiveCallback = std::function&)>; + +using AckCallback = std::function; + +using ChangeInvisibleDurationCallback = std::function; + +class SimpleConsumerImpl; + +class SimpleConsumerBuilder; + +class SimpleConsumer { +public: + ~SimpleConsumer(); + + static SimpleConsumerBuilder newBuilder(); + + void subscribe(std::string topic, FilterExpression filter_expression); + + void unsubscribe(const std::string& topic); + + void receive(std::size_t limit, + std::chrono::milliseconds invisible_duration, + std::error_code& ec, + std::vector& messages); + + void asyncReceive(std::size_t limit, std::chrono::milliseconds invisible_duration, ReceiveCallback callback); + + void ack(const Message& message, std::error_code& ec); + + void asyncAck(const Message& message, AckCallback callback); + + void changeInvisibleDuration(const Message& message, std::chrono::milliseconds duration, std::error_code& ec); + + void asyncChangeInvisibleDuration(const Message& message, + std::chrono::milliseconds duration, + ChangeInvisibleDurationCallback callback); + +private: + std::shared_ptr impl_; + + SimpleConsumer(std::string group); + + void start(); + + friend class SimpleConsumerBuilder; +}; + +class SimpleConsumerBuilder { +public: + SimpleConsumerBuilder(); + + SimpleConsumerBuilder& withGroup(std::string group) { + group_ = std::move(group); + return *this; + } + + SimpleConsumerBuilder& subscribe(std::string topic, FilterExpression expression) { + subscriptions_.insert({std::move(topic), std::move(expression)}); + return *this; + } + + SimpleConsumerBuilder& withConfiguration(Configuration configuration) { + configuration_ = std::move(configuration); + return *this; + } + + SimpleConsumer build(); + +private: + // Group name the consumer belongs to + std::string group_; + + Configuration configuration_; + + std::unordered_map subscriptions_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/Tracing.h b/api/rocketmq/Tracing.h new file mode 100644 index 000000000..bc1236210 --- /dev/null +++ b/api/rocketmq/Tracing.h @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/trace/sampler.h" + +#include "RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +opencensus::trace::Sampler* traceSampler() __attribute__((weak)); + +ROCKETMQ_NAMESPACE_END diff --git a/api/rocketmq/Transaction.h b/api/rocketmq/Transaction.h index 9a135a68f..2728d12ce 100644 --- a/api/rocketmq/Transaction.h +++ b/api/rocketmq/Transaction.h @@ -34,17 +34,18 @@ class Transaction { virtual bool rollback() = 0; - virtual std::string messageId() const = 0; + virtual const std::string& topic() const = 0; - virtual std::string transactionId() const = 0; -}; + virtual const std::string& messageId() const = 0; -using TransactionPtr = std::unique_ptr; + virtual const std::string& transactionId() const = 0; + + virtual const std::string& traceContext() const = 0; -enum class TransactionState : int8_t -{ - COMMIT = 0, - ROLLBACK = 1, + virtual const std::string& endpoint() const = 0; }; +using TransactionPtr = std::unique_ptr; +using TransactionConstPtr = std::unique_ptr; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/TransactionChecker.h b/api/rocketmq/TransactionChecker.h new file mode 100644 index 000000000..604e7bd5f --- /dev/null +++ b/api/rocketmq/TransactionChecker.h @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include "Message.h" +#include "TransactionState.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using TransactionChecker = std::function; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MessageType.h b/api/rocketmq/TransactionState.h similarity index 90% rename from api/rocketmq/MessageType.h rename to api/rocketmq/TransactionState.h index 8d4ea261b..decdf2178 100644 --- a/api/rocketmq/MessageType.h +++ b/api/rocketmq/TransactionState.h @@ -22,12 +22,9 @@ ROCKETMQ_NAMESPACE_BEGIN -enum class MessageType : int8_t -{ - NORMAL = 0, - FIFO = 1, - DELAY = 2, - TRANSACTION = 3, +enum class TransactionState : int8_t { + COMMIT = 0, + ROLLBACK = 1, }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index d593bee28..7282dd7fe 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -9,6 +9,17 @@ def rocketmq_deps(): actual = "@com_github_opentelemetry//api:api", ) + if "rules_python" not in native.existing_rules(): + http_archive( + name = "rules_python", + sha256 = "cdf6b84084aad8f10bf20b46b77cb48d83c319ebe6458a18e9d2cebf57807cdd", + strip_prefix = "rules_python-0.8.1", + urls = [ + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/rules-python/rules_python-0.8.1.tar.gz", + "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.8.1.tar.gz", + ], + ) + if "com_google_googletest" not in native.existing_rules(): http_archive( name = "com_google_googletest", @@ -55,60 +66,47 @@ def rocketmq_deps(): if "com_google_protobuf" not in native.existing_rules(): http_archive( name = "com_google_protobuf", - sha256 = "36f81e03a0702f8f935fffd5a486dac1c0fc6d4bae1cd02c7a32448ad6e63bcb", - strip_prefix = "protobuf-3.17.2", + sha256 = "8b28fdd45bab62d15db232ec404248901842e5340299a57765e48abe8a80d930", + strip_prefix = "protobuf-3.20.1", urls = [ - "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.17.2.tar.gz", + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/protobuf/protobuf-3.20.1.tar.gz", + "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.20.1.tar.gz", ], ) if "rules_proto_grpc" not in native.existing_rules(): http_archive( name = "rules_proto_grpc", - sha256 = "7954abbb6898830cd10ac9714fbcacf092299fda00ed2baf781172f545120419", - strip_prefix = "rules_proto_grpc-3.1.1", - urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/3.1.1.tar.gz"], + sha256 = "507e38c8d95c7efa4f3b1c0595a8e8f139c885cb41a76cab7e20e4e67ae87731", + strip_prefix = "rules_proto_grpc-4.1.1", + urls = [ + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/rules_proto_grpc/rules_proto_grpc-4.1.1.tar.gz", + "https://github.com/rules-proto-grpc/rules_proto_grpc/archive/refs/tags/4.1.1.tar.gz" + ], ) if "com_google_absl" not in native.existing_rules(): http_archive( name = "com_google_absl", - sha256 = "59b862f50e710277f8ede96f083a5bb8d7c9595376146838b9580be90374ee1f", - strip_prefix = "abseil-cpp-20210324.2", + sha256 = "dcf71b9cba8dc0ca9940c4b316a0c796be8fab42b070bb6b7cab62b48f0e66c4", + strip_prefix = "abseil-cpp-20211102.0", urls = [ - "https://github.com/abseil/abseil-cpp/archive/refs/tags/20210324.2.tar.gz", + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/abseil/abseil-cpp-20211102.0.tar.gz", + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz", ], ) if "com_github_grpc_grpc" not in native.existing_rules(): http_archive( name = "com_github_grpc_grpc", - strip_prefix = "grpc-1.39.0", - sha256 = "b16992aa1c949c10d5d5ce2a62f9d99fa7de77da2943e643fb66dcaf075826d6", - urls = ["https://github.com/grpc/grpc/archive/v1.39.0.tar.gz"], - ) - - if "io_opentelemetry_cpp" not in native.existing_rules(): - http_archive( - name = "io_opentelemetry_cpp", - sha256 = "24ba9b83f6cb8ba717ae30ebc570f5e8d0569008aee3c8b9a7ce6e4e1a5115b7", - strip_prefix = "opentelemetry-cpp-1.0.0-rc4", + strip_prefix = "grpc-1.46.0", + sha256 = "67423a4cd706ce16a88d1549297023f0f9f0d695a96dd684adc21e67b021f9bc", urls = [ - "https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v1.0.0-rc4.tar.gz", + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/grpc/grpc-1.46.0.tar.gz", + "https://github.com/grpc/grpc/archive/refs/tags/v1.46.0.tar.gz", ], ) - maybe( - http_archive, - name = "com_github_opentelemetry_proto", - build_file = "@io_opentelemetry_cpp//bazel:opentelemetry_proto.BUILD", - sha256 = "08f090570e0a112bfae276ba37e9c45bf724b64d902a7a001db33123b840ebd6", - strip_prefix = "opentelemetry-proto-0.6.0", - urls = [ - "https://github.com/open-telemetry/opentelemetry-proto/archive/v0.6.0.tar.gz", - ], - ) - maybe( http_archive, name = "asio", @@ -137,4 +135,25 @@ def rocketmq_deps(): "https://github.com/googleapis/googleapis/archive/af7fb72df59a814221b123a4d1acb3f6c3e6cc95.zip" ], strip_prefix = "googleapis-af7fb72df59a814221b123a4d1acb3f6c3e6cc95", + ) + + maybe( + http_archive, + name = "hedron_compile_commands", + sha256 = "4f69ccafa253825d93191977dcbcecee74e576aadbd21f1cfb25a19111ecdf21", + urls = [ + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/bazel-compile-commands-extractor/bazel-compile-commands-extractor-1.0.tar.gz", + "https://github.com/lizhanhui/bazel-compile-commands-extractor/archive/refs/tags/v1.0.tar.gz", + ], + strip_prefix = "bazel-compile-commands-extractor-1.0", + ) + + maybe( + http_archive, + name = "rules_swift", + urls = [ + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/rules_swift/rules_swift-0.27.0.tar.gz", + "https://github.com/bazelbuild/rules_swift/archive/refs/tags/0.27.0.tar.gz", + ], + strip_prefix = "rules_swift-0.27.0", ) \ No newline at end of file diff --git a/example/rocketmq/ExampleAsyncProducer.cpp b/example/rocketmq/ExampleAsyncProducer.cpp deleted file mode 100644 index 083cb34a0..000000000 --- a/example/rocketmq/ExampleAsyncProducer.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/DefaultMQProducer.h" -#include "rocketmq/ErrorCode.h" -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace rocketmq; - -int getAndReset(std::atomic_int& counter) { - int current; - while (true) { - current = counter.load(std::memory_order_relaxed); - if (counter.compare_exchange_weak(current, 0, std::memory_order_relaxed)) { - break; - } - } - return current; -} - -const std::string& alphaNumeric() { - static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - return alpha_numeric; -} - -std::string randomString(std::string::size_type len) { - std::string result; - result.reserve(len); - std::random_device rd; - std::mt19937 generator(rd()); - std::string source(alphaNumeric()); - std::string::size_type generated = 0; - while (generated < len) { - std::shuffle(source.begin(), source.end(), generator); - std::string::size_type delta = std::min({len - generated, source.length()}); - result.append(source.substr(0, delta)); - generated += delta; - } - return result; -} - -template -class RateLimiter { -public: - explicit RateLimiter(int permit) : permits_{0}, interval_(1000 / PARTITION), stopped_(false) { - int avg = permit / PARTITION; - for (auto& i : partition_) { - i = avg; - } - - int r = permit % PARTITION; - - if (r) { - int step = PARTITION / r; - for (int i = 0; i < r; ++i) { - partition_[i * step]++; - } - } - - auto lambda_tick = [this]() { - while (!stopped_) { - tick(); - } - }; - - thread_tick_ = std::thread(lambda_tick); - } - - ~RateLimiter() { - stopped_.store(true, std::memory_order_relaxed); - if (thread_tick_.joinable()) { - thread_tick_.join(); - } - } - - std::array& partition() { - return permits_; - } - - int slot() { - auto current = std::chrono::steady_clock::now(); - long ms = std::chrono::duration_cast(current.time_since_epoch()).count(); - return ms / interval_ % PARTITION; - } - - void acquire() { - int idx = slot(); - { - std::unique_lock lk(mtx_); - if (permits_[idx] > 0) { - --permits_[idx]; - return; - } - - // Reuse quota of the past half of the cycle - for (int j = 1; j <= PARTITION / 2; ++j) { - int index = idx - j; - if (index < 0) { - index += PARTITION; - } - if (permits_[index] > 0) { - --permits_[index]; - return; - } - } - - cv_.wait(lk, [this]() { - int idx = slot(); - return permits_[idx] > 0; - }); - idx = slot(); - --permits_[idx]; - } - } - - void tick() { - std::this_thread::sleep_for(std::chrono::milliseconds(1000 / PARTITION)); - int idx = slot(); - { - std::unique_lock lk(mtx_); - permits_[idx] = partition_[idx]; - cv_.notify_all(); - } - } - -private: - std::array partition_; - std::array permits_; - int interval_; - std::mutex mtx_; - std::condition_variable cv_; - - std::atomic_bool stopped_; - std::thread thread_tick_; -}; - -class SampleSendCallback : public rocketmq::SendCallback { -public: - SampleSendCallback(std::atomic_int& counter, std::atomic_int& error) : counter_(counter), error_(error) { - } - - void onSuccess(SendResult& send_result) noexcept override { - counter_.fetch_add(1, std::memory_order_relaxed); - } - - void onFailure(const std::error_code& ec) noexcept override { - error_.fetch_add(1, std::memory_order_relaxed); - } - -private: - std::atomic_int& counter_; - std::atomic_int& error_; -}; - -int main(int argc, char* argv[]) { - std::string::size_type body_size = 1024; - int duration = 300; - int qps = 1000; - - if (argc > 1) { - body_size = std::stoi(argv[1]); - } - - if (argc > 2) { - duration = std::stoi(argv[2]); - } - - if (argc > 3) { - qps = std::stoi(argv[3]); - } - - Logger& logger = getLogger(); - logger.setLevel(Level::Info); - logger.init(); - - const char* topic = "cpp_sdk_standard"; - const char* tag = "TagA"; - DefaultMQProducer producer("TestGroup"); - producer.setNamesrvAddr("47.98.116.189:80"); - producer.compressBodyThreshold(256); - const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; - producer.setCredentialsProvider(std::make_shared()); - producer.setResourceNamespace(resource_namespace); - producer.setRegion("cn-hangzhou-pre"); - MQMessage message; - message.setTopic(topic); - message.setTags(tag); - message.setKey("Yuck! Why-plural?"); - message.setBody(randomString(body_size)); - - std::cout << "Message Body: " << message.getBody() << std::endl; - - std::atomic_bool stopped(false); - - auto lambda = [&stopped, duration]() { - std::cout << "Benchmark will stop in " << duration << " seconds!" << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(duration)); - stopped.store(true); - }; - - std::thread t(lambda); - - std::atomic_int success(0); - std::atomic_int error(0); - - auto stats_lambda = [&stopped, &success, &error]() { - while (!stopped) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - std::cout << "Success:" << getAndReset(success) << ", Error: " << getAndReset(error) << std::endl; - } - }; - - std::thread stats_thread(stats_lambda); - - RateLimiter<10> rate_limiter(qps); - SampleSendCallback send_callback(success, error); - - try { - producer.start(); - while (!stopped) { - rate_limiter.acquire(); - producer.send(message, &send_callback, true); - } - } catch (...) { - std::cerr << "Ah...No!!!" << std::endl; - } - - if (stats_thread.joinable()) { - stats_thread.join(); - } - - if (t.joinable()) { - t.join(); - } - producer.shutdown(); - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/example/rocketmq/ExampleBroadcastPushConsumer.cpp b/example/rocketmq/ExampleBroadcastPushConsumer.cpp deleted file mode 100644 index 529f1e603..000000000 --- a/example/rocketmq/ExampleBroadcastPushConsumer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/DefaultMQPushConsumer.h" - -#include "rocketmq/Logger.h" -#include "spdlog/spdlog.h" - -#include -#include -#include -#include - -using namespace rocketmq; - -class SampleMQMessageListener : public StandardMessageListener { -public: - ConsumeMessageResult consumeMessage(const std::vector& msgs) override { - for (const MQMessageExt& msg : msgs) { - SPDLOG_INFO("Receive a message. MessageId={}", msg.getMsgId()); - std::cout << "Received a message. MessageId: " << msg.getMsgId() << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - return ConsumeMessageResult::SUCCESS; - } -}; - -int main(int argc, char* argv[]) { - Logger& logger = getLogger(); - logger.setLevel(Level::Debug); - logger.init(); - - const char* cid = "GID_cpp_sdk_standard"; - const char* topic = "cpp_sdk_standard"; - const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; - - DefaultMQPushConsumer push_consumer(cid); - push_consumer.setMessageModel(MessageModel::BROADCASTING); - push_consumer.setResourceNamespace(resource_namespace); - push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr("121.43.42.193:80"); - MessageListener* listener = new SampleMQMessageListener; - push_consumer.setGroupName(cid); - push_consumer.subscribe(topic, "*"); - push_consumer.registerMessageListener(listener); - push_consumer.start(); - - std::this_thread::sleep_for(std::chrono::minutes(60)); - - push_consumer.shutdown(); - return EXIT_SUCCESS; -} diff --git a/example/rocketmq/ExamplePullConsumer.cpp b/example/rocketmq/ExamplePullConsumer.cpp deleted file mode 100644 index 729963361..000000000 --- a/example/rocketmq/ExamplePullConsumer.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "rocketmq/CredentialsProvider.h" -#include "rocketmq/DefaultMQPullConsumer.h" -#include "rocketmq/Logger.h" - -int main(int argc, char* argv[]) { - const char* group = "GID_group003"; - const char* topic = "yc001"; - const char* resource_namespace = "MQ_INST_1973281269661160_BXmPlOA6"; - const char* name_server_list = "ipv4:11.165.223.199:9876"; - - rocketmq::Logger& logger = rocketmq::getLogger(); - logger.setLevel(rocketmq::Level::Debug); - logger.init(); - - rocketmq::DefaultMQPullConsumer pull_consumer(group); - pull_consumer.setResourceNamespace(resource_namespace); - pull_consumer.setCredentialsProvider(std::make_shared()); - pull_consumer.setNamesrvAddr(name_server_list); - pull_consumer.start(); - - std::future> future = pull_consumer.queuesFor(topic); - auto queues = future.get(); - - for (const auto& queue : queues) { - rocketmq::OffsetQuery offset_query; - offset_query.message_queue = queue; - offset_query.policy = rocketmq::QueryOffsetPolicy::BEGINNING; - auto offset_future = pull_consumer.queryOffset(offset_query); - int64_t offset = offset_future.get(); - std::cout << "offset: " << offset << std::endl; - } - - pull_consumer.shutdown(); - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp deleted file mode 100644 index d45e52af3..000000000 --- a/example/rocketmq/ExamplePushConsumer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "rocketmq/Logger.h" - -#include "spdlog/spdlog.h" - -#include "rocketmq/DefaultMQPushConsumer.h" - -using namespace rocketmq; - -class SampleMQMessageListener : public StandardMessageListener { -public: - ConsumeMessageResult consumeMessage(const std::vector& msgs) override { - for (const MQMessageExt& msg : msgs) { - SPDLOG_INFO("Consume message[Topic={}, MessageId={}] OK", msg.getTopic(), msg.getMsgId()); - std::cout << "Consume Message[MsgId=" << msg.getMsgId() << "] OK. Body Size: " << msg.getBody().size() - << std::endl; - // std::this_thread::sleep_for(std::chrono::seconds(1)); - } - return ConsumeMessageResult::SUCCESS; - } -}; - -int main(int argc, char* argv[]) { - - Logger& logger = getLogger(); - logger.setLevel(Level::Debug); - logger.init(); - - const char* group_id = "GID_cpp_sdk_standard"; - const char* topic = "cpp_sdk_standard"; - const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; - const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; - - DefaultMQPushConsumer push_consumer(group_id); - push_consumer.setResourceNamespace(resource_namespace); - push_consumer.setCredentialsProvider(std::make_shared()); - push_consumer.setNamesrvAddr(name_server); - MessageListener* listener = new SampleMQMessageListener; - push_consumer.setInstanceName("instance_0"); - push_consumer.subscribe(topic, "*"); - push_consumer.registerMessageListener(listener); - push_consumer.setConsumeThreadCount(4); - push_consumer.start(); - - std::this_thread::sleep_for(std::chrono::minutes(30)); - - push_consumer.shutdown(); - return EXIT_SUCCESS; -} diff --git a/example/rocketmq/BUILD.bazel b/examples/BUILD.bazel similarity index 51% rename from example/rocketmq/BUILD.bazel rename to examples/BUILD.bazel index 9c7508aae..dfa114a2d 100644 --- a/example/rocketmq/BUILD.bazel +++ b/examples/BUILD.bazel @@ -47,15 +47,25 @@ cc_binary( ) cc_binary( - name = "sql_producer", + name = "example_simple_consumer", srcs = [ - "SqlProducer.cpp", + "ExampleSimpleConsumer.cpp", ], deps = [ "//src/main/cpp/rocketmq:rocketmq_library", ], ) +# cc_binary( +# name = "sql_producer", +# srcs = [ +# "SqlProducer.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) + cc_binary( name = "example_push_consumer", srcs = [ @@ -66,82 +76,62 @@ cc_binary( ], ) -cc_binary( - name = "example_fifo_push_consumer", - srcs = [ - "ExampleFifoPushConsumer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) +# cc_binary( +# name = "example_fifo_push_consumer", +# srcs = [ +# "ExampleFifoPushConsumer.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) -cc_binary( - name = "push_consumer_with_custom_executor", - srcs = [ - "PushConsumerWithCustomExecutor.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) - -cc_binary( - name = "push_consumer_with_throttle", - srcs = [ - "PushConsumerWithThrottle.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) - -cc_binary( - name = "sql_consumer", - srcs = [ - "SqlConsumer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) +# cc_binary( +# name = "push_consumer_with_custom_executor", +# srcs = [ +# "PushConsumerWithCustomExecutor.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) -cc_binary( - name = "example_pull_consumer", - srcs = [ - "ExamplePullConsumer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) +# cc_binary( +# name = "push_consumer_with_throttle", +# srcs = [ +# "PushConsumerWithThrottle.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) -cc_binary( - name = "benchmark_push_consumer", - srcs = [ - "BenchmarkPushConsumer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) +# cc_binary( +# name = "sql_consumer", +# srcs = [ +# "SqlConsumer.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) -cc_binary( - name = "example_broadcast_push_consumer", - srcs = [ - "ExampleBroadcastPushConsumer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) +# cc_binary( +# name = "benchmark_push_consumer", +# srcs = [ +# "BenchmarkPushConsumer.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) -cc_binary( - name = "example_transaction_producer", - srcs = [ - "ExampleTransactionProducer.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) \ No newline at end of file +# cc_binary( +# name = "example_transaction_producer", +# srcs = [ +# "ExampleTransactionProducer.cpp", +# ], +# deps = [ +# "//src/main/cpp/rocketmq:rocketmq_library", +# ], +# ) \ No newline at end of file diff --git a/example/rocketmq/BenchmarkPushConsumer.cpp b/examples/BenchmarkPushConsumer.cpp similarity index 100% rename from example/rocketmq/BenchmarkPushConsumer.cpp rename to examples/BenchmarkPushConsumer.cpp diff --git a/examples/ExampleAsyncProducer.cpp b/examples/ExampleAsyncProducer.cpp new file mode 100644 index 000000000..2d28d74dd --- /dev/null +++ b/examples/ExampleAsyncProducer.cpp @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "rocketmq/Message.h" +#include "rocketmq/Producer.h" + +using namespace ROCKETMQ_NAMESPACE; + +const std::string& alphaNumeric() { + static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + return alpha_numeric; +} + +std::string randomString(std::string::size_type len) { + std::string result; + result.reserve(len); + std::random_device rd; + std::mt19937 generator(rd()); + std::string source(alphaNumeric()); + std::string::size_type generated = 0; + while (generated < len) { + std::shuffle(source.begin(), source.end(), generator); + std::string::size_type delta = std::min({len - generated, source.length()}); + result.append(source.substr(0, delta)); + generated += delta; + } + return result; +} + +int main(int argc, char* argv[]) { + const char* topic = "cpp_sdk_standard"; + const char* name_server = "11.166.42.94:8081"; + + auto producer = + Producer::newBuilder().withConfiguration(Configuration::newBuilder().withEndpoints(name_server).build()).build(); + + std::atomic_bool stopped; + std::atomic_long count(0); + + auto stats_lambda = [&] { + while (!stopped.load(std::memory_order_relaxed)) { + long cnt = count.load(std::memory_order_relaxed); + while (count.compare_exchange_weak(cnt, 0)) { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "QPS: " << cnt << std::endl; + } + }; + + std::thread stats_thread(stats_lambda); + + std::string body = randomString(1024 * 4); + std::cout << "Message body size: " << body.length() << std::endl; + + std::size_t total = 256; + std::size_t completed = 0; + std::mutex mtx; + std::condition_variable cv; + + try { + auto send_callback = [&](const std::error_code& ec, const SendReceipt& receipt) { + std::unique_lock lk(mtx); + completed++; + count++; + std::cout << "Message[id=" << receipt.message_id << "] sent" << std::endl; + if (completed >= total) { + cv.notify_all(); + } + }; + + for (std::size_t i = 0; i < total; ++i) { + auto message = Message::newBuilder().withTopic(topic).withTag("TagA").withKeys({"Key-0"}).withBody(body).build(); + producer.send(std::move(message), send_callback); + } + + { + std::unique_lock lk(mtx); + cv.wait(lk, [&]() { return completed >= total; }); + } + } catch (...) { + std::cerr << "Ah...No!!!" << std::endl; + } + stopped.store(true, std::memory_order_relaxed); + if (stats_thread.joinable()) { + stats_thread.join(); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExampleFifoProducer.cpp b/examples/ExampleFifoProducer.cpp similarity index 66% rename from example/rocketmq/ExampleFifoProducer.cpp rename to examples/ExampleFifoProducer.cpp index c4d9e2304..947ae5dc6 100644 --- a/example/rocketmq/ExampleFifoProducer.cpp +++ b/examples/ExampleFifoProducer.cpp @@ -14,13 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "rocketmq/DefaultMQProducer.h" #include #include #include #include +#include -using namespace rocketmq; +#include "rocketmq/Message.h" +#include "rocketmq/Producer.h" + +using namespace ROCKETMQ_NAMESPACE; const std::string& alphaNumeric() { static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); @@ -44,21 +47,11 @@ std::string randomString(std::string::size_type len) { } int main(int argc, char* argv[]) { - Logger& logger = getLogger(); - logger.setLevel(Level::Debug); - logger.init(); - - DefaultMQProducer producer("TestGroup"); - - const char* topic = "lingchu_test_order_topic"; - const char* name_server = "120.25.100.131:8081"; + const char* topic = "cpp_sdk_standard"; + const char* name_server = "11.166.42.94:8081"; - producer.setNamesrvAddr(name_server); - producer.compressBodyThreshold(256); - const char* resource_namespace = "MQ_INST_1080056302921134_BXyTLppt"; - // producer.setRegion("cn-hangzhou-pre"); - producer.setResourceNamespace(resource_namespace); - producer.setCredentialsProvider(std::make_shared()); + auto producer = + Producer::newBuilder().withConfiguration(Configuration::newBuilder().withEndpoints(name_server).build()).build(); std::atomic_bool stopped; std::atomic_long count(0); @@ -80,19 +73,18 @@ int main(int argc, char* argv[]) { std::cout << "Message body size: " << body.length() << std::endl; try { - producer.start(); - for (int i = 0; i < 16; ++i) { - std::string sharding_key = "sharding-key-" + std::to_string(i); - MQMessage message; - message.setTopic(topic); - message.setTags("TagA"); - message.setKey("Yuck! Why-plural?"); - message.setBody(body); - - SendResult sendResult = producer.send(message, sharding_key); - std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + for (int i = 0; i < 256; ++i) { + auto message = Message::newBuilder() + .withTopic(topic) + .withTag("TagA") + .withKeys({"Key-0"}) + .withBody(body) + .withGroup("message-group-0") + .build(); + std::error_code ec; + SendReceipt send_receipt = producer.send(std::move(message), ec); + std::cout << "Message-ID: " << send_receipt.message_id << std::endl; count++; - std::this_thread::sleep_for(std::chrono::seconds(1)); } } catch (...) { std::cerr << "Ah...No!!!" << std::endl; @@ -103,6 +95,5 @@ int main(int argc, char* argv[]) { stats_thread.join(); } - producer.shutdown(); return EXIT_SUCCESS; } \ No newline at end of file diff --git a/example/rocketmq/ExampleFifoPushConsumer.cpp b/examples/ExampleFifoPushConsumer.cpp similarity index 100% rename from example/rocketmq/ExampleFifoPushConsumer.cpp rename to examples/ExampleFifoPushConsumer.cpp diff --git a/example/rocketmq/ExampleProducer.cpp b/examples/ExampleProducer.cpp similarity index 68% rename from example/rocketmq/ExampleProducer.cpp rename to examples/ExampleProducer.cpp index 3e1cfc54c..fa86af73a 100644 --- a/example/rocketmq/ExampleProducer.cpp +++ b/examples/ExampleProducer.cpp @@ -14,13 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "rocketmq/DefaultMQProducer.h" #include #include #include #include +#include -using namespace rocketmq; +#include "rocketmq/Message.h" +#include "rocketmq/Producer.h" + +using namespace ROCKETMQ_NAMESPACE; const std::string& alphaNumeric() { static std::string alpha_numeric("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); @@ -44,21 +47,11 @@ std::string randomString(std::string::size_type len) { } int main(int argc, char* argv[]) { - Logger& logger = getLogger(); - logger.setLevel(Level::Debug); - logger.init(); - - DefaultMQProducer producer("TestGroup"); - const char* topic = "cpp_sdk_standard"; - const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80"; + const char* name_server = "11.166.42.94:8081"; - producer.setNamesrvAddr(name_server); - producer.compressBodyThreshold(256); - const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7"; - producer.setRegion("cn-hangzhou-pre"); - producer.setResourceNamespace(resource_namespace); - producer.setCredentialsProvider(std::make_shared()); + auto producer = + Producer::newBuilder().withConfiguration(Configuration::newBuilder().withEndpoints(name_server).build()).build(); std::atomic_bool stopped; std::atomic_long count(0); @@ -80,28 +73,22 @@ int main(int argc, char* argv[]) { std::cout << "Message body size: " << body.length() << std::endl; try { - producer.start(); - for (int i = 0; i < 16; ++i) { - MQMessage message; - message.setTopic(topic); - message.setTags("TagA"); - message.setKey("Yuck! Why-plural?"); - message.setBody(body); - - SendResult sendResult = producer.send(message); - std::cout << sendResult.getMessageQueue().simpleName() << ": " << sendResult.getMsgId() << std::endl; + for (int i = 0; i < 256; ++i) { + auto message = Message::newBuilder().withTopic(topic).withTag("TagA").withKeys({"Key-0"}).withBody(body).build(); + std::error_code ec; + SendReceipt send_receipt = producer.send(std::move(message), ec); + std::cout << "Message-ID: " << send_receipt.message_id << std::endl; count++; - std::this_thread::sleep_for(std::chrono::seconds(1)); } } catch (...) { std::cerr << "Ah...No!!!" << std::endl; } - stopped.store(true, std::memory_order_relaxed); if (stats_thread.joinable()) { stats_thread.join(); } - producer.shutdown(); + // std::this_thread::sleep_for(std::chrono::seconds(1)); + return EXIT_SUCCESS; } \ No newline at end of file diff --git a/examples/ExamplePushConsumer.cpp b/examples/ExamplePushConsumer.cpp new file mode 100644 index 000000000..00954ffea --- /dev/null +++ b/examples/ExamplePushConsumer.cpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include "rocketmq/Logger.h" +#include "rocketmq/PushConsumer.h" +#include "spdlog/spdlog.h" + +using namespace ROCKETMQ_NAMESPACE; + +int main(int argc, char* argv[]) { + const char* topic = "cpp_sdk_standard"; + const char* name_server = "11.166.42.94:8081"; + const char* group = "GID_cpp_sdk_standard"; + std::string tag = "*"; + + auto listener = [](const Message& message) { return ConsumeResult::SUCCESS; }; + + auto push_consumer = PushConsumer::newBuilder() + .withGroup(group) + .withConfiguration(Configuration::newBuilder() + .withEndpoints(name_server) + .withRequestTimeout(std::chrono::seconds(3)) + .build()) + .withConsumeThreads(4) + .withListener(listener) + .subscribe(topic, tag) + .build(); + + std::this_thread::sleep_for(std::chrono::minutes(30)); + + return EXIT_SUCCESS; +} diff --git a/examples/ExampleSimpleConsumer.cpp b/examples/ExampleSimpleConsumer.cpp new file mode 100644 index 000000000..dc7671874 --- /dev/null +++ b/examples/ExampleSimpleConsumer.cpp @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "rocketmq/SimpleConsumer.h" + +using namespace ROCKETMQ_NAMESPACE; + +int main(int argc, char* argv[]) { + const char* group = "ExampleSimpleGroup"; + const char* topic = "cpp_sdk_standard"; + const char* name_server = "11.166.42.94:8081"; + std::string tag = "*"; + + auto simple_consumer = SimpleConsumer::newBuilder() + .withGroup(group) + .withConfiguration(Configuration::newBuilder().withEndpoints(name_server).build()) + .subscribe(topic, tag) + .build(); + std::vector messages; + std::error_code ec; + simple_consumer.receive(4, std::chrono::seconds(3), ec, messages); + + if (ec) { + std::cerr << "Failed to receive messages. Cause: " << ec.message() << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Received " << messages.size() << " messages" << std::endl; + std::size_t i = 0; + for (const auto& message : messages) { + std::cout << "Received a message[topic=" << message->topic() << ", message-id=" << message->id() + << ", receipt-handle='" << message->extension().receipt_handle << "']" << std::endl; + + std::error_code ec; + if (++i % 2 == 0) { + simple_consumer.ack(*message, ec); + if (ec) { + std::cerr << "Failed to ack message. Cause: " << ec.message() << std::endl; + } + } else { + simple_consumer.changeInvisibleDuration(*message, std::chrono::milliseconds(100), ec); + if (ec) { + std::cerr << "Failed to change invisible duration of message. Cause: " << ec.message() << std::endl; + } + } + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/example/rocketmq/ExampleTransactionProducer.cpp b/examples/ExampleTransactionProducer.cpp similarity index 100% rename from example/rocketmq/ExampleTransactionProducer.cpp rename to examples/ExampleTransactionProducer.cpp diff --git a/example/rocketmq/PushConsumerWithCustomExecutor.cpp b/examples/PushConsumerWithCustomExecutor.cpp similarity index 100% rename from example/rocketmq/PushConsumerWithCustomExecutor.cpp rename to examples/PushConsumerWithCustomExecutor.cpp diff --git a/example/rocketmq/PushConsumerWithThrottle.cpp b/examples/PushConsumerWithThrottle.cpp similarity index 100% rename from example/rocketmq/PushConsumerWithThrottle.cpp rename to examples/PushConsumerWithThrottle.cpp diff --git a/example/rocketmq/SqlConsumer.cpp b/examples/SqlConsumer.cpp similarity index 100% rename from example/rocketmq/SqlConsumer.cpp rename to examples/SqlConsumer.cpp diff --git a/example/rocketmq/SqlProducer.cpp b/examples/SqlProducer.cpp similarity index 100% rename from example/rocketmq/SqlProducer.cpp rename to examples/SqlProducer.cpp diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel index 3416bf41e..80a2b6d53 100644 --- a/proto/BUILD.bazel +++ b/proto/BUILD.bazel @@ -21,7 +21,7 @@ load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library", "cpp_grpc_compile") proto_library( name = "apache_rocketmq_definition", srcs = [ - "apache/rocketmq/v1/definition.proto", + "apache/rocketmq/v2/definition.proto", ], deps = [ "@com_google_protobuf//:empty_proto", @@ -38,7 +38,7 @@ proto_library( proto_library( name = "apache_rocketmq_admin", srcs = [ - "apache/rocketmq/v1/admin.proto", + "apache/rocketmq/v2/admin.proto", ], deps = [ ":apache_rocketmq_definition", @@ -51,7 +51,7 @@ proto_library( proto_library( name = "apache_rocketmq_service", srcs = [ - "apache/rocketmq/v1/service.proto", + "apache/rocketmq/v2/service.proto", ], deps = [ ":apache_rocketmq_definition", diff --git a/proto/apache/rocketmq/v1/definition.proto b/proto/apache/rocketmq/v1/definition.proto deleted file mode 100644 index b6cb24beb..000000000 --- a/proto/apache/rocketmq/v1/definition.proto +++ /dev/null @@ -1,349 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/duration.proto"; - -package apache.rocketmq.v1; - -option java_multiple_files = true; -option java_package = "apache.rocketmq.v1"; -option java_generate_equals_and_hash = true; -option java_string_check_utf8 = true; -option java_outer_classname = "MQDomain"; - -enum Permission { - NONE = 0; - READ = 1; - WRITE = 2; - READ_WRITE = 3; - - reserved 4 to 64; -} - -enum FilterType { - TAG = 0; - SQL = 1; - - reserved 2 to 64; -} - -message FilterExpression { - FilterType type = 1; - string expression = 2; - - reserved 3 to 64; -} - -// Dead lettering is done on a best effort basis. The same message might be -// dead lettered multiple times. -// -// If validation on any of the fields fails at subscription creation/update, -// the create/update subscription request will fail. -message DeadLetterPolicy { - // The maximum number of delivery attempts for any message. - // - // This field will be honored on a best effort basis. - // - // If this parameter is 0, a default value of 16 is used. - int32 max_delivery_attempts = 1; - - reserved 2 to 64; -} - -message Resource { - string resource_namespace = 1; - - // Resource name identifier, which remains unique within the abstract resource - // namespace. - string name = 2; - - reserved 3 to 64; -} - -enum ConsumeModel { - CLUSTERING = 0; - BROADCASTING = 1; - - reserved 2 to 64; -} - -message ProducerData { - Resource group = 1; - - reserved 2 to 64; -} - -enum ConsumePolicy { - RESUME = 0; - PLAYBACK = 1; - DISCARD = 2; - TARGET_TIMESTAMP = 3; - - reserved 4 to 64; -} - -enum ConsumeMessageType { - ACTIVE = 0; - PASSIVE = 1; - - reserved 2 to 64; -} - -message ConsumerData { - Resource group = 1; - - repeated SubscriptionEntry subscriptions = 2; - - ConsumeModel consume_model = 3; - - ConsumePolicy consume_policy = 4; - - DeadLetterPolicy dead_letter_policy = 5; - - ConsumeMessageType consume_type = 6; - - reserved 7 to 64; -} - -message SubscriptionEntry { - Resource topic = 1; - FilterExpression expression = 2; - - reserved 3 to 64; -} - -enum AddressScheme { - IPv4 = 0; - IPv6 = 1; - DOMAIN_NAME = 2; - - reserved 3 to 64; -} - -message Address { - string host = 1; - int32 port = 2; - - reserved 3 to 64; -} - -message Endpoints { - AddressScheme scheme = 1; - repeated Address addresses = 2; - - reserved 3 to 64; -} - -message Broker { - // Name of the broker - string name = 1; - - // Broker index. Canonically, index = 0 implies that the broker is playing - // leader role while brokers with index > 0 play follower role. - int32 id = 2; - - // Address of the broker, complying with the following scheme - // 1. dns:[//authority/]host[:port] - // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses - // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses - Endpoints endpoints = 3; - - reserved 4 to 64; -} - -message Partition { - Resource topic = 1; - int32 id = 2; - Permission permission = 3; - Broker broker = 4; - - reserved 5 to 64; -} - -enum MessageType { - NORMAL = 0; - - // Sequenced message - FIFO = 1; - - // Messages that are delivered after the specified duration. - DELAY = 2; - - // Messages that are transactional. Only committed messages are delivered to - // subscribers. - TRANSACTION = 3; - - reserved 4 to 64; -} - -enum DigestType { - // CRC algorithm achieves goal of detecting random data error with lowest - // computation overhead. - CRC32 = 0; - - // MD5 algorithm achieves good balance between collision rate and computation - // overhead. - MD5 = 1; - - // SHA-family has substantially fewer collision with fair amount of - // computation. - SHA1 = 2; - - reserved 3 to 64; -} - -// When publishing messages to or subscribing messages from brokers, clients -// shall include or validate digests of message body to ensure data integrity. -// -// For message publishment, when an invalid digest were detected, brokers need -// respond client with BAD_REQUEST. -// -// For messags subscription, when an invalid digest were detected, consumers -// need to handle this case according to message type: -// 1) Standard messages should be negatively acknowledged instantly, causing -// immediate re-delivery; 2) FIFO messages require special RPC, to re-fetch -// previously acquired messages batch; -// -// Message consumption model also affects how invalid digest are handled. When -// messages are consumed in broadcasting way, -// TODO: define semantics of invalid-digest-when-broadcasting. -message Digest { - DigestType type = 1; - string checksum = 2; - - reserved 3 to 64; -} - -enum Encoding { - IDENTITY = 0; - GZIP = 1; - - reserved 2 to 64; -} - -message SystemAttribute { - // Tag - string tag = 1; - - // Message keys - repeated string keys = 2; - - // Message identifier, client-side generated, remains unique. - // if message_id is empty, the send message request will be aborted with - // status `INVALID_ARGUMENT` - string message_id = 3; - - // Message body digest - Digest body_digest = 4; - - // Message body encoding. Candidate options are identity, gzip, snappy etc. - Encoding body_encoding = 5; - - // Message type, normal, FIFO or transactional. - MessageType message_type = 6; - - // Message born time-point. - google.protobuf.Timestamp born_timestamp = 7; - - // Message born host. Valid options are IPv4, IPv6 or client host domain name. - string born_host = 8; - - // Time-point at which the message is stored in the broker. - google.protobuf.Timestamp store_timestamp = 9; - - // The broker that stores this message. It may be name, IP or arbitrary - // identifier that uniquely identify the broker. - string store_host = 10; - - oneof timed_delivery { - // Time-point at which broker delivers to clients. - google.protobuf.Timestamp delivery_timestamp = 11; - - // Level-based delay strategy. - int32 delay_level = 12; - } - - // If a message is acquired by way of POP, this field holds the receipt. - // Clients use the receipt to acknowledge or negatively acknowledge the - // message. - string receipt_handle = 13; - - // Partition identifier in which a message is physically stored. - int32 partition_id = 14; - - // Partition offset at which a message is stored. - int64 partition_offset = 15; - - // Period of time servers would remain invisible once a message is acquired. - google.protobuf.Duration invisible_period = 16; - - // Business code may failed to process messages for the moment. Hence, clients - // may request servers to deliver them again using certain back-off strategy, - // the attempt is 1 not 0 if message is delivered first time. - int32 delivery_attempt = 17; - - // Message producer load-balance group if applicable. - Resource producer_group = 18; - - string message_group = 19; - - // Trace context. - string trace_context = 20; - - // Delay time of first recover orphaned transaction request from server. - google.protobuf.Duration orphaned_transaction_recovery_period = 21; - - reserved 22 to 64; -} - -message Message { - - Resource topic = 1; - - // User defined key-value pairs. - // If user_attribute contains the reserved keys by RocketMQ, - // the send message request will be aborted with status `INVALID_ARGUMENT`. - // See below links for the reserved keys - // https://github.com/apache/rocketmq/blob/master/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java#L58 - map user_attribute = 2; - - SystemAttribute system_attribute = 3; - - bytes body = 4; - - reserved 5 to 64; -} - -message Assignment { - Partition Partition = 1; - - reserved 2 to 64; -} - -enum QueryOffsetPolicy { - // Use this option if client wishes to playback all existing messages. - BEGINNING = 0; - - // Use this option if client wishes to skip all existing messages. - END = 1; - - // Use this option if time-based seek is targeted. - TIME_POINT = 2; - - reserved 3 to 64; -} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/service.proto b/proto/apache/rocketmq/v1/service.proto deleted file mode 100644 index 9d8e3b090..000000000 --- a/proto/apache/rocketmq/v1/service.proto +++ /dev/null @@ -1,531 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -import "google/protobuf/duration.proto"; -import "google/protobuf/timestamp.proto"; -import "google/rpc/error_details.proto"; -import "google/rpc/status.proto"; - -import "apache/rocketmq/v1/definition.proto"; - -package apache.rocketmq.v1; - -option java_multiple_files = true; -option java_package = "apache.rocketmq.v1"; -option java_generate_equals_and_hash = true; -option java_string_check_utf8 = true; -option java_outer_classname = "MQService"; - -message ResponseCommon { - google.rpc.Status status = 1; - google.rpc.RequestInfo request_info = 2; - google.rpc.Help help = 3; - google.rpc.RetryInfo retry_info = 4; - google.rpc.DebugInfo debug_info = 5; - google.rpc.ErrorInfo error_info = 6; - - reserved 7 to 64; -} - -// Topics are destination of messages to publish to or subscribe from. Similar -// to domain names, they will be addressable after resolution through the -// provided access point. -// -// Access points are usually the addresses of name servers, which fulfill -// service discovery, load-balancing and other auxillary services. Name servers -// receive periodic heartbeats from affiliate brokers and erase those which -// failed to maintain alive status. -// -// Name servers answer queries of QueryRouteRequest, responding clients with -// addressable partitions, which they may directly publish messages to or -// subscribe messages from. -// -// QueryRouteRequest shall include source endpoints, aka, configured -// access-point, which annotates tenant-id, instance-id or other -// vendor-specific settings. Purpose-built name servers may respond customized -// results based on these particular requirements. -message QueryRouteRequest { - Resource topic = 1; - - Endpoints endpoints = 2; - - reserved 3 to 64; -} - -message QueryRouteResponse { - ResponseCommon common = 1; - - repeated Partition partitions = 2; - - reserved 3 to 64; -} - -message SendMessageRequest { - Message message = 1; - Partition partition = 2; - - reserved 3 to 64; -} - -message SendMessageResponse { - ResponseCommon common = 1; - string message_id = 2; - string transaction_id = 3; - - reserved 4 to 64; -} - -message QueryAssignmentRequest { - Resource topic = 1; - Resource group = 2; - string client_id = 3; - - // Service access point - Endpoints endpoints = 4; - - reserved 5 to 64; -} - -message QueryAssignmentResponse { - ResponseCommon common = 1; - repeated Assignment assignments = 2; - - reserved 3 to 64; -} - -message ReceiveMessageRequest { - Resource group = 1; - string client_id = 2; - Partition partition = 3; - FilterExpression filter_expression = 4; - ConsumePolicy consume_policy = 5; - google.protobuf.Timestamp initialization_timestamp = 6; - int32 batch_size = 7; - google.protobuf.Duration invisible_duration = 8; - google.protobuf.Duration await_time = 9; - bool fifo_flag = 10; - - reserved 11 to 64; -} - -message ReceiveMessageResponse { - ResponseCommon common = 1; - repeated Message messages = 2; - google.protobuf.Timestamp delivery_timestamp = 3; - google.protobuf.Duration invisible_duration = 4; - - reserved 5 to 64; -} - -message AckMessageRequest { - Resource group = 1; - Resource topic = 2; - string client_id = 3; - oneof handle { - string receipt_handle = 4; - int64 offset = 5; - } - string message_id = 6; - - reserved 7 to 64; -} - -message AckMessageResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message NackMessageRequest { - Resource group = 1; - Resource topic = 2; - string client_id = 3; - string receipt_handle = 4; - string message_id = 5; - int32 delivery_attempt = 6; - int32 max_delivery_attempts = 7; - - reserved 8 to 64; -} - -message NackMessageResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message ForwardMessageToDeadLetterQueueRequest { - Resource group = 1; - Resource topic = 2; - string client_id = 3; - string receipt_handle = 4; - string message_id = 5; - int32 delivery_attempt = 6; - int32 max_delivery_attempts = 7; - - reserved 8 to 64; -} - -message ForwardMessageToDeadLetterQueueResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message HeartbeatRequest { - string client_id = 1; - oneof client_data { - ProducerData producer_data = 2; - ConsumerData consumer_data = 3; - } - bool fifo_flag = 4; - - reserved 5 to 64; -} - -message HeartbeatResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message HealthCheckRequest { - Resource group = 1; - string client_host = 2; - - reserved 3 to 64; -} - -message HealthCheckResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message EndTransactionRequest { - Resource group = 1; - string message_id = 2; - string transaction_id = 3; - enum TransactionResolution { - COMMIT = 0; - ROLLBACK = 1; - } - TransactionResolution resolution = 4; - enum Source { - CLIENT = 0; - SERVER_CHECK = 1; - } - Source source = 5; - string trace_context = 6; - - reserved 7 to 64; -} - -message EndTransactionResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message QueryOffsetRequest { - Partition partition = 1; - QueryOffsetPolicy policy = 2; - google.protobuf.Timestamp time_point = 3; - - reserved 4 to 64; -} - -message QueryOffsetResponse { - ResponseCommon common = 1; - int64 offset = 2; - - reserved 3 to 64; -} - -message PullMessageRequest { - Resource group = 1; - Partition partition = 2; - int64 offset = 3; - int32 batch_size = 4; - google.protobuf.Duration await_time = 5; - FilterExpression filter_expression = 6; - string client_id = 7; - - reserved 8 to 64; -} - -message PullMessageResponse { - ResponseCommon common = 1; - int64 min_offset = 2; - int64 next_offset = 3; - int64 max_offset = 4; - repeated Message messages = 5; - - reserved 6 to 64; -} - -message NoopCommand { - reserved 1 to 64; -} - -message PrintThreadStackTraceCommand { - string command_id = 1; - - reserved 2 to 64; -} - -message ReportThreadStackTraceRequest { - string command_id = 1; - string thread_stack_trace = 2; - - reserved 3 to 64; -} - -message ReportThreadStackTraceResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message VerifyMessageConsumptionCommand { - string command_id = 1; - Message message = 2; - - reserved 3 to 64; -} - -message ReportMessageConsumptionResultRequest { - string command_id = 1; - google.rpc.Status status = 2; - - reserved 3 to 64; -} - -message ReportMessageConsumptionResultResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -message RecoverOrphanedTransactionCommand { - Message orphaned_transactional_message = 1; - string transaction_id = 2; - - reserved 3 to 64; -} - -message PollCommandRequest { - string client_id = 1; - repeated Resource topics = 2; - oneof group { - Resource producer_group = 3; - Resource consumer_group = 4; - } - - reserved 5 to 64; -} - -message PollCommandResponse { - oneof type { - // Default command when no new command need to be delivered. - NoopCommand noop_command = 1; - // Request client to print thread stack trace. - PrintThreadStackTraceCommand print_thread_stack_trace_command = 2; - // Request client to verify the consumption of the appointed message. - VerifyMessageConsumptionCommand verify_message_consumption_command = 3; - // Request client to recover the orphaned transaction message. - RecoverOrphanedTransactionCommand recover_orphaned_transaction_command = 4; - } - - reserved 5 to 64; -} - -message NotifyClientTerminationRequest { - oneof group { - Resource producer_group = 1; - Resource consumer_group = 2; - } - string client_id = 3; - - reserved 4 to 64; -} - -message NotifyClientTerminationResponse { - ResponseCommon common = 1; - - reserved 2 to 64; -} - -// For all the RPCs in MessagingService, the following error handling policies -// apply: -// -// If the request doesn't bear a valid authentication credential, return a -// response with common.status.code == `UNAUTHENTICATED`. If the authenticated -// user is not granted with sufficient permission to execute the requested -// operation, return a response with common.status.code == `PERMISSION_DENIED`. -// If the per-user-resource-based quota is exhausted, return a response with -// common.status.code == `RESOURCE_EXHAUSTED`. If any unexpected server-side -// errors raise, return a response with common.status.code == `INTERNAL`. -service MessagingService { - - // Querys the route entries of the requested topic in the perspective of the - // given endpoints. On success, servers should return a collection of - // addressable partitions. Note servers may return customized route entries - // based on endpoints provided. - // - // If the requested topic doesn't exist, returns `NOT_FOUND`. - // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. - rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { - } - - // Producer or consumer sends HeartbeatRequest to servers periodically to - // keep-alive. Additionally, it also reports client-side configuration, - // including topic subscription, load-balancing group name, etc. - // - // Returns `OK` if success. - // - // If a client specifies a language that is not yet supported by servers, - // returns `INVALID_ARGUMENT` - rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { - } - - // Checks the health status of message server, returns `OK` if services are - // online and serving. Clients may use this RPC to detect availability of - // messaging service, and take isolation actions when necessary. - rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) { - } - - // Delivers messages to brokers. - // Clients may further: - // 1. Refine a message destination to topic partition which fulfills parts of - // FIFO semantic; - // 2. Flag a message as transactional, which keeps it invisible to consumers - // until it commits; - // 3. Time a message, making it invisible to consumers till specified - // time-point; - // 4. And more... - // - // Returns message-id or transaction-id with status `OK` on success. - // - // If the destination topic doesn't exist, returns `NOT_FOUND`. - rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { - } - - // Querys the assigned partition route info of a topic for current consumer, - // the returned assignment result is descided by server-side load balacner. - // - // If the corresponding topic doesn't exist, returns `NOT_FOUND`. - // If the specific endpoints is emtpy, returns `INVALID_ARGUMENT`. - rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { - } - - // Receives messages from the server in batch manner, returns a set of - // messages if success. The received messages should be acked or uacked after - // processed. - // - // If the pending concurrent receive requests exceed the quota of the given - // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, - // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic - // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new - // message in the specific topic, returns `OK` with an empty message set. - // Please note that client may suffer from false empty responses. - rpc ReceiveMessage(ReceiveMessageRequest) returns (ReceiveMessageResponse) { - } - - // Acknowledges the message associated with the `receipt_handle` or `offset` - // in the `AckMessageRequest`, it means the message has been successfully - // processed. Returns `OK` if the message server remove the relevant message - // successfully. - // - // If the given receipt_handle is illegal or out of date, returns - // `INVALID_ARGUMENT`. - rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { - } - - // Signals that the message has not been successfully processed. The message - // server should resend the message follow the retry policy defined at - // server-side. - // - // If the corresponding topic or consumer group doesn't exist, returns - // `NOT_FOUND`. - rpc NackMessage(NackMessageRequest) returns (NackMessageResponse) { - } - - // Forwards one message to dead letter queue if the DeadLetterPolicy is - // triggered by this message at client-side, return `OK` if success. - rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) - returns (ForwardMessageToDeadLetterQueueResponse) { - } - - // Commits or rollback one transactional message. - rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { - } - - // Querys the offset of the specific partition, returns the offset with `OK` - // if success. The message server should maintain a numerical offset for each - // message in a parition. - rpc QueryOffset(QueryOffsetRequest) returns (QueryOffsetResponse) { - } - - // Pulls messages from the specific partition, returns a set of messages with - // next pull offset. The pulled messages can't be acked or nacked, while the - // client is responsible for manage offesets for consumer, typically update - // consume offset to local memory or a third-party storage service. - // - // If the pending concurrent receive requests exceed the quota of the given - // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, - // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic - // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new - // message in the specific topic, returns `OK` with an empty message set. - // Please note that client may suffer from false empty responses. - rpc PullMessage(PullMessageRequest) returns (PullMessageResponse) { - } - - // Multiplexing RPC(s) for various polling requests, which issue different - // commands to client. - // - // Sometimes client may need to receive and process the command from server. - // To prevent the complexity of streaming RPC(s), a unary RPC using - // long-polling is another solution. - // - // To mark the request-response of corresponding command, `command_id` in - // message is recorded in the subsequent RPC(s). For example, after receiving - // command of printing thread stack trace, client would send - // `ReportMessageConsumptionResultRequest` to server, which contain both of - // the stack trace and `command_id`. - // - // At same time, `NoopCommand` is delivered from server when no new command is - // needed, it is essential for client to maintain the ping-pong. - // - rpc PollCommand(PollCommandRequest) returns (PollCommandResponse) { - } - - // After receiving the corresponding polling command, the thread stack trace - // is reported to the server. - rpc ReportThreadStackTrace(ReportThreadStackTraceRequest) returns (ReportThreadStackTraceResponse) { - } - - // After receiving the corresponding polling command, the consumption result - // of appointed message is reported to the server. - rpc ReportMessageConsumptionResult(ReportMessageConsumptionResultRequest) - returns (ReportMessageConsumptionResultResponse) { - } - - // Notify the server that the client is terminated. - rpc NotifyClientTermination(NotifyClientTerminationRequest) returns (NotifyClientTerminationResponse) { - } -} \ No newline at end of file diff --git a/proto/apache/rocketmq/v1/admin.proto b/proto/apache/rocketmq/v2/admin.proto similarity index 86% rename from proto/apache/rocketmq/v1/admin.proto rename to proto/apache/rocketmq/v2/admin.proto index 554207b90..7dbb7027d 100644 --- a/proto/apache/rocketmq/v1/admin.proto +++ b/proto/apache/rocketmq/v2/admin.proto @@ -15,11 +15,12 @@ syntax = "proto3"; -package apache.rocketmq.v1; +package apache.rocketmq.v2; option cc_enable_arenas = true; +option csharp_namespace = "Apache.Rocketmq.V2"; option java_multiple_files = true; -option java_package = "apache.rocketmq.v1"; +option java_package = "apache.rocketmq.v2"; option java_generate_equals_and_hash = true; option java_string_check_utf8 = true; option java_outer_classname = "MQAdmin"; @@ -35,11 +36,8 @@ message ChangeLogLevelRequest { Level level = 1; } -message ChangeLogLevelResponse { - string remark = 1; -} +message ChangeLogLevelResponse { string remark = 1; } service Admin { - rpc ChangeLogLevel(ChangeLogLevelRequest) returns (ChangeLogLevelResponse) { - } + rpc ChangeLogLevel(ChangeLogLevelRequest) returns (ChangeLogLevelResponse) {} } \ No newline at end of file diff --git a/proto/apache/rocketmq/v2/definition.proto b/proto/apache/rocketmq/v2/definition.proto new file mode 100644 index 000000000..6565a21b9 --- /dev/null +++ b/proto/apache/rocketmq/v2/definition.proto @@ -0,0 +1,447 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + +package apache.rocketmq.v2; + +option csharp_namespace = "Apache.Rocketmq.V2"; +option java_multiple_files = true; +option java_package = "apache.rocketmq.v2"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; +option java_outer_classname = "MQDomain"; + +enum TransactionResolution { + TRANSACTION_RESOLUTION_UNSPECIFIED = 0; + COMMIT = 1; + ROLLBACK = 2; +} + +enum TransactionSource { + SOURCE_UNSPECIFIED = 0; + SOURCE_CLIENT = 1; + SOURCE_SERVER_CHECK = 2; +} + +enum Permission { + PERMISSION_UNSPECIFIED = 0; + NONE = 1; + READ = 2; + WRITE = 3; + READ_WRITE = 4; +} + +enum FilterType { + FILTER_TYPE_UNSPECIFIED = 0; + TAG = 1; + SQL = 2; +} + +message FilterExpression { + FilterType type = 1; + string expression = 2; +} + +message RetryPolicy { + int32 max_attempts = 1; + oneof strategy { + ExponentialBackoff exponential_backoff = 2; + CustomizedBackoff customized_backoff = 3; + } +} + +// https://en.wikipedia.org/wiki/Exponential_backoff +message ExponentialBackoff { + google.protobuf.Duration initial = 1; + google.protobuf.Duration max = 2; + float multiplier = 3; +} + +message CustomizedBackoff { + // To support classic backoff strategy which is arbitary defined by end users. + // Typical values are: `1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h` + repeated google.protobuf.Duration next = 1; +} + +message Resource { + string resource_namespace = 1; + + // Resource name identifier, which remains unique within the abstract resource + // namespace. + string name = 2; +} + +message SubscriptionEntry { + Resource topic = 1; + FilterExpression expression = 2; +} + +enum AddressScheme { + ADDRESS_SCHEME_UNSPECIFIED = 0; + IPv4 = 1; + IPv6 = 2; + DOMAIN_NAME = 3; +} + +message Address { + string host = 1; + int32 port = 2; +} + +message Endpoints { + AddressScheme scheme = 1; + repeated Address addresses = 2; +} + +message Broker { + // Name of the broker + string name = 1; + + // Broker index. Canonically, index = 0 implies that the broker is playing + // leader role while brokers with index > 0 play follower role. + int32 id = 2; + + // Address of the broker, complying with the following scheme + // 1. dns:[//authority/]host[:port] + // 2. ipv4:address[:port][,address[:port],...] – IPv4 addresses + // 3. ipv6:address[:port][,address[:port],...] – IPv6 addresses + Endpoints endpoints = 3; +} + +message MessageQueue { + Resource topic = 1; + int32 id = 2; + Permission permission = 3; + Broker broker = 4; + repeated MessageType accept_message_types = 5; +} + +enum MessageType { + MESSAGE_TYPE_UNSPECIFIED = 0; + + NORMAL = 1; + + // Sequenced message + FIFO = 2; + + // Messages that are delivered after the specified duration. + DELAY = 3; + + // Messages that are transactional. Only committed messages are delivered to + // subscribers. + TRANSACTION = 4; +} + +enum DigestType { + DIGEST_TYPE_UNSPECIFIED = 0; + + // CRC algorithm achieves goal of detecting random data error with lowest + // computation overhead. + CRC32 = 1; + + // MD5 algorithm achieves good balance between collision rate and computation + // overhead. + MD5 = 2; + + // SHA-family has substantially fewer collision with fair amount of + // computation. + SHA1 = 3; +} + +// When publishing messages to or subscribing messages from brokers, clients +// shall include or validate digests of message body to ensure data integrity. +// +// For message publishing, when an invalid digest were detected, brokers need +// respond client with BAD_REQUEST. +// +// For messages subscription, when an invalid digest were detected, consumers +// need to handle this case according to message type: +// 1) Standard messages should be negatively acknowledged instantly, causing +// immediate re-delivery; 2) FIFO messages require special RPC, to re-fetch +// previously acquired messages batch; +// +// Message consumption model also affects how invalid digest are handled. When +// messages are consumed in broadcasting way, +// TODO: define semantics of invalid-digest-when-broadcasting. +message Digest { + DigestType type = 1; + string checksum = 2; +} + +enum ClientType { + CLIENT_TYPE_UNSPECIFIED = 0; + PRODUCER = 1; + PUSH_CONSUMER = 2; + SIMPLE_CONSUMER = 3; +} + +enum Encoding { + ENCODING_UNSPECIFIED = 0; + + IDENTITY = 1; + + GZIP = 2; +} + +message SystemProperties { + // Tag, which is optional. + optional string tag = 1; + + // Message keys + repeated string keys = 2; + + // Message identifier, client-side generated, remains unique. + // if message_id is empty, the send message request will be aborted with + // status `INVALID_ARGUMENT` + string message_id = 3; + + // Message body digest + Digest body_digest = 4; + + // Message body encoding. Candidate options are identity, gzip, snappy etc. + Encoding body_encoding = 5; + + // Message type, normal, FIFO or transactional. + MessageType message_type = 6; + + // Message born time-point. + google.protobuf.Timestamp born_timestamp = 7; + + // Message born host. Valid options are IPv4, IPv6 or client host domain name. + string born_host = 8; + + // Time-point at which the message is stored in the broker, which is absent + // for message publishing. + optional google.protobuf.Timestamp store_timestamp = 9; + + // The broker that stores this message. It may be broker name, IP or arbitrary + // identifier that uniquely identify the server. + string store_host = 10; + + // Time-point at which broker delivers to clients, which is optional. + optional google.protobuf.Timestamp delivery_timestamp = 11; + + // If a message is acquired by way of POP, this field holds the receipt, + // which is absent for message publishing. + // Clients use the receipt to acknowledge or negatively acknowledge the + // message. + optional string receipt_handle = 12; + + // Message queue identifier in which a message is physically stored. + int32 queue_id = 13; + + // Message-queue offset at which a message is stored, which is absent for + // message publishing. + optional int64 queue_offset = 14; + + // Period of time servers would remain invisible once a message is acquired. + optional google.protobuf.Duration invisible_duration = 15; + + // Business code may failed to process messages for the moment. Hence, clients + // may request servers to deliver them again using certain back-off strategy, + // the attempt is 1 not 0 if message is delivered first time, and it is absent + // for message publishing. + optional int32 delivery_attempt = 16; + + // Define the group name of message in the same topic, which is optional. + optional string message_group = 17; + + // Trace context for each message, which is optional. + optional string trace_context = 18; + + // If a transactional message stay unresolved for more than + // `transaction_orphan_threshold`, it would be regarded as an + // orphan. Servers that manages orphan messages would pick up + // a capable publisher to resolve + optional google.protobuf.Duration orphaned_transaction_recovery_duration = 19; +} + +message Message { + Resource topic = 1; + + // User defined key-value pairs. + // If user_properties contain the reserved keys by RocketMQ, + // the send message request will be aborted with status `INVALID_ARGUMENT`. + // See below links for the reserved keys + // https://github.com/apache/rocketmq/blob/master/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java#L58 + map user_properties = 2; + + SystemProperties system_properties = 3; + + bytes body = 4; +} + +message Assignment { + MessageQueue message_queue = 1; +} + +message SendReceipt { + string message_id = 1; + string transaction_id = 2; + int64 offset = 3; +} + +enum Code { + // Success. + OK = 0; + // Format of access point is illegal. + ILLEGAL_ACCESS_POINT = 1; + // Format of topic is illegal. + ILLEGAL_TOPIC = 2; + // Format of consumer group is illegal. + ILLEGAL_CONSUMER_GROUP = 3; + // Format of message tag is illegal. + ILLEGAL_MESSAGE_TAG = 4; + // Format of message key is illegal. + ILLEGAL_MESSAGE_KEY = 5; + // Size of message keys exceeds the threshold. + MESSAGE_KEYS_TOO_LARGE = 6; + // Format of message group is illegal. + ILLEGAL_MESSAGE_GROUP = 7; + // Format of message property key is illegal. + ILLEGAL_MESSAGE_PROPERTY_KEY = 8; + // Message properties total size exceeds the threshold. + MESSAGE_PROPERTIES_TOO_LARGE = 9; + // Message body size exceeds the threshold. + MESSAGE_BODY_TOO_LARGE = 10; + + // User does not have the permission to operate. + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403 + FORBIDDEN = 403; + + // Code indicates that the client request has not been completed + // because it lacks valid authentication credentials for the + // requested resource. + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 + UNAUTHORIZED = 401; + + // Topic resource does not exist. + TOPIC_NOT_FOUND = 13; + + // Consumer group resource does not exist. + CONSUMER_GROUP_NOT_FOUND = 14; + + // Not allowed to verify message. Chances are that you are verifying + // a FIFO message, as is violating FIFO semantics. + VERIFY_MESSAGE_FORBIDDEN = 15; + + // Failed to consume message. + FAILED_TO_CONSUME_MESSAGE = 16; + + // Message is corrupted. + MESSAGE_CORRUPTED = 17; + + // Too many requests are made in short period of duration. + // Requests are throttled. + TOO_MANY_REQUESTS = 18; + + // Expired receipt-handle is used when trying to acknowledge or change + // invisible duration of a message + RECEIPT_HANDLE_EXPIRED = 19; + + // Message property is not match the message type. + MESSAGE_PROPERTY_DOES_NOT_MATCH_MESSAGE_TYPE = 20; + + // Format of message id is illegal. + ILLEGAL_MESSAGE_ID = 21; + + // Transaction id is invalid. + INVALID_TRANSACTION_ID = 22; + + // Format of filter expression is illegal. + ILLEGAL_FILTER_EXPRESSION = 23; + + // Receipt handle of message is invalid. + INVALID_RECEIPT_HANDLE = 24; + + // Message persistence timeout. + MASTER_PERSISTENCE_TIMEOUT = 25; + + // Slave persistence timeout. + SLAVE_PERSISTENCE_TIMEOUT = 26; + + // The HA-mechanism is not working now. + HA_NOT_AVAILABLE = 27; + + // Operation is not allowed in current version. + VERSION_UNSUPPORTED = 28; + + // Message not found from server. + MESSAGE_NOT_FOUND = 29; + + // Message offset is illegal. + ILLEGAL_MESSAGE_OFFSET = 30; + + // Illegal message is for the sake of backward compatibility. In most case, + // more definitive code is better, e.g. `ILLEGAL_MESSAGE_TAG`. + ILLEGAL_MESSAGE = 31; + + // Client type could not be recognized. + UNRECOGNIZED_CLIENT_TYPE = 32; + + // Code indicates that the server encountered an unexpected condition + // that prevented it from fulfilling the request. + // This error response is a generic "catch-all" response. + // Usually, this indicates the server cannot find a better alternative + // error code to response. Sometimes, server administrators log error + // responses like the 500 status code with more details about the request + // to prevent the error from happening again in the future. + // + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 + INTERNAL_SERVER_ERROR = 500; + + // Code means that the server or client does not support the functionality + // required to fulfill the request. + NOT_IMPLEMENTED = 501; + + // Code indicates that the server, while acting as a gateway or proxy, + // did not get a response in time from the upstream server that + // it needed in order to complete the request. + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504 + GATEWAY_TIMEOUT = 504; +} + +message Status { + Code code = 1; + string message = 2; +} + +enum Language { + LANGUAGE_UNSPECIFIED = 0; + JAVA = 1; + CPP = 2; + DOT_NET = 3; + GOLANG = 4; + RUST = 5; +} + +// User Agent +message UA { + // SDK language + Language language = 1; + + // SDK version + string version = 2; + + // Platform details, including OS name, version, arch etc. + string platform = 3; + + // Hostname of the node + string hostname = 4; +} \ No newline at end of file diff --git a/proto/apache/rocketmq/v2/service.proto b/proto/apache/rocketmq/v2/service.proto new file mode 100644 index 000000000..aa688b454 --- /dev/null +++ b/proto/apache/rocketmq/v2/service.proto @@ -0,0 +1,436 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/duration.proto"; + +import "apache/rocketmq/v2/definition.proto"; + +package apache.rocketmq.v2; + +option csharp_namespace = "Apache.Rocketmq.V2"; +option java_multiple_files = true; +option java_package = "apache.rocketmq.v2"; +option java_generate_equals_and_hash = true; +option java_string_check_utf8 = true; +option java_outer_classname = "MQService"; + +// Topics are destination of messages to publish to or subscribe from. Similar +// to domain names, they will be addressable after resolution through the +// provided access point. +// +// Access points are usually the addresses of name servers, which fulfill +// service discovery, load-balancing and other auxiliary services. Name servers +// receive periodic heartbeats from affiliate brokers and erase those which +// failed to maintain alive status. +// +// Name servers answer queries of QueryRouteRequest, responding clients with +// addressable message-queues, which they may directly publish messages to or +// subscribe messages from. +// +// QueryRouteRequest shall include source endpoints, aka, configured +// access-point, which annotates tenant-id, instance-id or other +// vendor-specific settings. Purpose-built name servers may respond customized +// results based on these particular requirements. +message QueryRouteRequest { + Resource topic = 1; + Endpoints endpoints = 2; +} + +message QueryRouteResponse { + Status status = 1; + + repeated MessageQueue message_queues = 2; +} + +message SendMessageRequest { + repeated Message messages = 1; +} + +message SendMessageResponse { + Status status = 1; + repeated SendReceipt receipts = 2; +} + +message QueryAssignmentRequest { + Resource topic = 1; + Resource group = 2; + Endpoints endpoints = 3; +} + +message QueryAssignmentResponse { + Status status = 1; + repeated Assignment assignments = 2; +} + +message ReceiveMessageRequest { + Resource group = 1; + MessageQueue message_queue = 2; + FilterExpression filter_expression = 3; + int32 batch_size = 4; + // Required if client type is simple consumer. + optional google.protobuf.Duration invisible_duration = 5; + // For message auto renew and clean + bool auto_renew = 6; +} + +message ReceiveMessageResponse { + oneof content { + Status status = 1; + Message message = 2; + } +} + +message AckMessageEntry { + string message_id = 1; + string receipt_handle = 2; +} + +message AckMessageRequest { + Resource group = 1; + Resource topic = 2; + repeated AckMessageEntry entries = 3; +} + +message AckMessageResultEntry { + string message_id = 1; + string receipt_handle = 2; + + // Acknowledge result may be acquired through inspecting + // `status.code`; In case acknowledgement failed, `status.message` + // is the explanation of the failure. + Status status = 3; +} + +message AckMessageResponse { + // RPC tier status, which is used to represent RPC-level errors including + // authentication, authorization, throttling and other general failures. + Status status = 1; + + repeated AckMessageResultEntry entries = 2; +} + +message ForwardMessageToDeadLetterQueueRequest { + Resource group = 1; + Resource topic = 2; + string receipt_handle = 3; + string message_id = 4; + int32 delivery_attempt = 5; + int32 max_delivery_attempts = 6; +} + +message ForwardMessageToDeadLetterQueueResponse { + Status status = 1; +} + +message HeartbeatRequest { + optional Resource group = 1; + ClientType client_type = 2; +} + +message HeartbeatResponse { + Status status = 1; +} + +message EndTransactionRequest { + Resource topic = 1; + string message_id = 2; + string transaction_id = 3; + TransactionResolution resolution = 4; + TransactionSource source = 5; + string trace_context = 6; +} + +message EndTransactionResponse { + Status status = 1; +} + +message PrintThreadStackTraceCommand { + string nonce = 1; +} + +message ThreadStackTrace { + string nonce = 1; + optional string thread_stack_trace = 2; +} + +message VerifyMessageCommand { + string nonce = 1; + Message message = 2; +} + +message VerifyMessageResult { + string nonce = 1; +} + +message RecoverOrphanedTransactionCommand { + MessageQueue message_queue = 1; + Message orphaned_transactional_message = 2; + string transaction_id = 3; +} + +message Publishing { + // Publishing settings below here is appointed by client, thus it is + // unnecessary for server to push at present. + // + // List of topics to which messages will publish to. + repeated Resource topics = 1; + + // Publishing settings below here are from server, it is essential for + // server to push. + // + // Body of message will be deflated if its size in bytes exceeds the + // threshold. + int32 compress_body_threshold = 2; + + // If the message body size exceeds `max_body_size`, broker servers would + // reject the request. As a result, it is advisable that Producer performs + // client-side check validation. + int32 max_body_size = 3; +} + +message Subscription { + // Subscription settings below here is appointed by client, thus it is + // unnecessary for server to push at present. + // + // Consumer group. + optional Resource group = 1; + + // Subscription for consumer. + repeated SubscriptionEntry subscriptions = 2; + + // Subscription settings below here are from server, it is essential for + // server to push. + // + // When FIFO flag is `true`, messages of the same message group are processed + // in first-in-first-out manner. + // + // Brokers will not deliver further messages of the same group utill prior + // ones are completely acknowledged. + optional bool fifo = 3; + + // Message receive batch size here is essential for push consumer. + optional int32 receive_batch_size = 4; + + // Long-polling timeout for `ReceiveMessageRequest`, which is essential for + // push consumer. + optional google.protobuf.Duration long_polling_timeout = 5; +} + +message Settings { + // Configurations for all clients. + optional ClientType client_type = 1; + + optional Endpoints access_point = 2; + + // If publishing of messages encounters throttling or server internal errors, + // publishers should implement automatic retries after progressive longer + // back-offs for consecutive errors. + // + // When processing message fails, `backoff_policy` describes an interval + // after which the message should be available to consume again. + // + // For FIFO messages, the interval should be relatively small because + // messages of the same message group would not be readily available utill + // the prior one depletes its lifecycle. + optional RetryPolicy backoff_policy = 3; + + // Request timeout for RPCs excluding long-polling. + optional google.protobuf.Duration request_timeout = 4; + + oneof pub_sub { + Publishing publishing = 5; + + Subscription subscription = 6; + } + + // User agent details + UA user_agent = 7; +} + +message TelemetryCommand { + optional Status status = 1; + + oneof command { + // Client settings + Settings settings = 2; + + // These messages are from client. + // + // Report thread stack trace to server. + ThreadStackTrace thread_stack_trace = 3; + + // Report message verify result to server. + VerifyMessageResult verify_message_result = 4; + + // There messages are from server. + // + // Request client to recover the orphaned transaction message. + RecoverOrphanedTransactionCommand recover_orphaned_transaction_command = 5; + + // Request client to print thread stack trace. + PrintThreadStackTraceCommand print_thread_stack_trace_command = 6; + + // Request client to verify the consumption of the appointed message. + VerifyMessageCommand verify_message_command = 7; + } +} + +message NotifyClientTerminationRequest { + // Consumer group, which is absent for producer. + optional Resource group = 1; +} + +message NotifyClientTerminationResponse { + Status status = 1; +} + +message ChangeInvisibleDurationRequest { + Resource group = 1; + Resource topic = 2; + + // Unique receipt handle to identify message to change + string receipt_handle = 3; + + // New invisible duration + google.protobuf.Duration invisible_duration = 4; + + // For message tracing + string message_id = 5; +} + +message ChangeInvisibleDurationResponse { + Status status = 1; + + // Server may generate a new receipt handle for the message. + string receipt_handle = 2; +} + +// For all the RPCs in MessagingService, the following error handling policies +// apply: +// +// If the request doesn't bear a valid authentication credential, return a +// response with common.status.code == `UNAUTHENTICATED`. If the authenticated +// user is not granted with sufficient permission to execute the requested +// operation, return a response with common.status.code == `PERMISSION_DENIED`. +// If the per-user-resource-based quota is exhausted, return a response with +// common.status.code == `RESOURCE_EXHAUSTED`. If any unexpected server-side +// errors raise, return a response with common.status.code == `INTERNAL`. +service MessagingService { + // Queries the route entries of the requested topic in the perspective of the + // given endpoints. On success, servers should return a collection of + // addressable message-queues. Note servers may return customized route + // entries based on endpoints provided. + // + // If the requested topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is empty, returns `INVALID_ARGUMENT`. + rpc QueryRoute(QueryRouteRequest) returns (QueryRouteResponse) { + } + + // Producer or consumer sends HeartbeatRequest to servers periodically to + // keep-alive. Additionally, it also reports client-side configuration, + // including topic subscription, load-balancing group name, etc. + // + // Returns `OK` if success. + // + // If a client specifies a language that is not yet supported by servers, + // returns `INVALID_ARGUMENT` + rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) { + } + + // Delivers messages to brokers. + // Clients may further: + // 1. Refine a message destination to message-queues which fulfills parts of + // FIFO semantic; + // 2. Flag a message as transactional, which keeps it invisible to consumers + // until it commits; + // 3. Time a message, making it invisible to consumers till specified + // time-point; + // 4. And more... + // + // Returns message-id or transaction-id with status `OK` on success. + // + // If the destination topic doesn't exist, returns `NOT_FOUND`. + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { + } + + // Queries the assigned route info of a topic for current consumer, + // the returned assignment result is decided by server-side load balancer. + // + // If the corresponding topic doesn't exist, returns `NOT_FOUND`. + // If the specific endpoints is empty, returns `INVALID_ARGUMENT`. + rpc QueryAssignment(QueryAssignmentRequest) returns (QueryAssignmentResponse) { + } + + // Receives messages from the server in batch manner, returns a set of + // messages if success. The received messages should be acked or redelivered + // after processed. + // + // If the pending concurrent receive requests exceed the quota of the given + // consumer group, returns `UNAVAILABLE`. If the upstream store server hangs, + // return `DEADLINE_EXCEEDED` in a timely manner. If the corresponding topic + // or consumer group doesn't exist, returns `NOT_FOUND`. If there is no new + // message in the specific topic, returns `OK` with an empty message set. + // Please note that client may suffer from false empty responses. + // + // If failed to receive message from remote, server must return only one + // `ReceiveMessageResponse` as the reply to the request, whose `Status` indicates + // the specific reason of failure, otherwise, the reply is considered successful. + rpc ReceiveMessage(ReceiveMessageRequest) returns (stream ReceiveMessageResponse) { + } + + // Acknowledges the message associated with the `receipt_handle` or `offset` + // in the `AckMessageRequest`, it means the message has been successfully + // processed. Returns `OK` if the message server remove the relevant message + // successfully. + // + // If the given receipt_handle is illegal or out of date, returns + // `INVALID_ARGUMENT`. + rpc AckMessage(AckMessageRequest) returns (AckMessageResponse) { + } + + // Forwards one message to dead letter queue if the max delivery attempts is + // exceeded by this message at client-side, return `OK` if success. + rpc ForwardMessageToDeadLetterQueue(ForwardMessageToDeadLetterQueueRequest) + returns (ForwardMessageToDeadLetterQueueResponse) { + } + + // Commits or rollback one transactional message. + rpc EndTransaction(EndTransactionRequest) returns (EndTransactionResponse) { + } + + // Once a client starts, it would immediately establishes bi-lateral stream + // RPCs with brokers, reporting its settings as the initiative command. + // + // When servers have need of inspecting client status, they would issue + // telemetry commands to clients. After executing received instructions, + // clients shall report command execution results through client-side streams. + rpc Telemetry(stream TelemetryCommand) returns (stream TelemetryCommand) { + } + + // Notify the server that the client is terminated. + rpc NotifyClientTermination(NotifyClientTerminationRequest) returns (NotifyClientTerminationResponse) { + } + + // Once a message is retrieved from consume queue on behalf of the group, it + // will be kept invisible to other clients of the same group for a period of + // time. The message is supposed to be processed within the invisible + // duration. If the client, which is in charge of the invisible message, is + // not capable of processing the message timely, it may use + // ChangeInvisibleDuration to lengthen invisible duration. + rpc ChangeInvisibleDuration(ChangeInvisibleDurationRequest) returns (ChangeInvisibleDurationResponse) { + } +} \ No newline at end of file diff --git a/src/main/cpp/admin/include/AdminClient.h b/src/main/cpp/admin/include/AdminClient.h index 968d837d7..9e6e7c7b7 100644 --- a/src/main/cpp/admin/include/AdminClient.h +++ b/src/main/cpp/admin/include/AdminClient.h @@ -15,12 +15,12 @@ * limitations under the License. */ #pragma once -#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "apache/rocketmq/v2/admin.grpc.pb.h" #include "rocketmq/RocketMQ.h" #include #include -namespace rmq = apache::rocketmq::v1; +namespace rmq = apache::rocketmq::v2; ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/admin/include/AdminServerImpl.h b/src/main/cpp/admin/include/AdminServerImpl.h index b6056352a..5ef88ab2d 100644 --- a/src/main/cpp/admin/include/AdminServerImpl.h +++ b/src/main/cpp/admin/include/AdminServerImpl.h @@ -16,15 +16,17 @@ */ #pragma once -#include "AdminServiceImpl.h" -#include "rocketmq/AdminServer.h" #include #include -#include #include #include #include +#include "AdminServiceImpl.h" +#include "rocketmq/AdminServer.h" + +#include "grpcpp/grpcpp.h" + using grpc::Server; ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/admin/include/AdminServiceImpl.h b/src/main/cpp/admin/include/AdminServiceImpl.h index 58933abd9..e2d750cbc 100644 --- a/src/main/cpp/admin/include/AdminServiceImpl.h +++ b/src/main/cpp/admin/include/AdminServiceImpl.h @@ -15,13 +15,14 @@ * limitations under the License. */ #pragma once -#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "apache/rocketmq/v2/admin.grpc.pb.h" +#include "grpcpp/grpcpp.h" #include "rocketmq/RocketMQ.h" -#include using grpc::ServerContext; using grpc::Status; -namespace rmq = apache::rocketmq::v1; + +namespace rmq = apache::rocketmq::v2; ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/admin/include/ServerCall.h b/src/main/cpp/admin/include/ServerCall.h index 138d43341..66f3246a0 100644 --- a/src/main/cpp/admin/include/ServerCall.h +++ b/src/main/cpp/admin/include/ServerCall.h @@ -18,11 +18,11 @@ #include -#include "apache/rocketmq/v1/admin.grpc.pb.h" +#include "apache/rocketmq/v2/admin.grpc.pb.h" #include "rocketmq/RocketMQ.h" -namespace rmq = apache::rocketmq::v1; +namespace rmq = apache::rocketmq::v2; ROCKETMQ_NAMESPACE_BEGIN diff --git a/src/main/cpp/base/BUILD.bazel b/src/main/cpp/base/BUILD.bazel index 5c7ab81c0..959bba5e7 100644 --- a/src/main/cpp/base/BUILD.bazel +++ b/src/main/cpp/base/BUILD.bazel @@ -25,6 +25,7 @@ cc_library( strip_include_prefix = "//src/main/cpp/base/include", deps = [ "//src/main/cpp/log:log_library", + "//proto:rocketmq_grpc_library", "@com_github_fmtlib_fmt//:fmtlib", "@com_google_absl//absl/base", "@com_google_absl//absl/container:flat_hash_map", diff --git a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp b/src/main/cpp/base/Configuration.cpp similarity index 54% rename from src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp rename to src/main/cpp/base/Configuration.cpp index f7df8ce9c..cf0f4bd3d 100644 --- a/src/test/cpp/ut/rocketmq/FilterExpressionTest.cpp +++ b/src/main/cpp/base/Configuration.cpp @@ -14,34 +14,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "FilterExpression.h" -#include "gtest/gtest.h" -ROCKETMQ_NAMESPACE_BEGIN +#include "rocketmq/Configuration.h" -class FilterExpressionTest : public testing::Test { -public: - FilterExpressionTest() : filter_expression_("TagA") { - } +ROCKETMQ_NAMESPACE_BEGIN - void SetUp() override { - } +ConfigurationBuilder Configuration::newBuilder() { + return {}; +} - void TearDown() override { - } +ConfigurationBuilder& ConfigurationBuilder::withEndpoints(std::string endpoints) { + configuration_.endpoints_ = std::move(endpoints); + return *this; +} -protected: - FilterExpression filter_expression_; -}; +ConfigurationBuilder& ConfigurationBuilder::withCredentialsProvider(std::shared_ptr provider) { + configuration_.credentials_provider_ = std::move(provider); + return *this; +} -TEST_F(FilterExpressionTest, testAccept) { - MQMessageExt message; - message.setTags("TagA"); - EXPECT_TRUE(filter_expression_.accept(message)); +ConfigurationBuilder& ConfigurationBuilder::withRequestTimeout(std::chrono::milliseconds request_timeout) { + configuration_.request_timeout_ = request_timeout; + return *this; +} - MQMessageExt message2; - message2.setTags("TagB"); - EXPECT_FALSE(filter_expression_.accept(message2)); +Configuration ConfigurationBuilder::build() { + return std::move(configuration_); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/ConfigurationDefaults.cpp b/src/main/cpp/base/ConfigurationDefaults.cpp new file mode 100644 index 000000000..b11a6e6b0 --- /dev/null +++ b/src/main/cpp/base/ConfigurationDefaults.cpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "rocketmq/ConfigurationDefaults.h" + +ROCKETMQ_NAMESPACE_BEGIN + +const std::chrono::milliseconds ConfigurationDefaults::RequestTimeout = std::chrono::seconds(3); + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/rocketmq/CredentialsProvider.cpp b/src/main/cpp/base/CredentialsProvider.cpp similarity index 100% rename from src/main/cpp/rocketmq/CredentialsProvider.cpp rename to src/main/cpp/base/CredentialsProvider.cpp diff --git a/src/main/cpp/base/ErrorCategory.cpp b/src/main/cpp/base/ErrorCategory.cpp index 166f632e1..4fe2ea0fd 100644 --- a/src/main/cpp/base/ErrorCategory.cpp +++ b/src/main/cpp/base/ErrorCategory.cpp @@ -24,6 +24,9 @@ std::string ErrorCategory::message(int code) const { case ErrorCode::Success: return "Success"; + case ErrorCode::NoContent: + return "Broker has processed the request but is not going to return any content."; + case ErrorCode::IllegalState: return "Client state illegal. Forgot to call start()?"; @@ -34,6 +37,9 @@ std::string ErrorCategory::message(int code) const { return "Message is ill-formed. Check validity of your topic, tag, " "etc"; + case ErrorCode::BadRequestAsyncPubFifoMessage: + return "Publishing of FIFO messages is only allowed synchronously"; + case ErrorCode::Unauthorized: return "Authentication failed. Possibly caused by invalid credentials."; @@ -42,8 +48,13 @@ std::string ErrorCategory::message(int code) const { "requested action"; case ErrorCode::NotFound: - return "Topic not found, which should be created through console or " + return "Request resource not found, which should be created through console or " "administration API before hand."; + case ErrorCode::TopicNotFound: + return "Topic is not found. Verify the request topic has already been created through console or management API"; + + case ErrorCode::GroupNotFound: + return "Group is not found. Verify the request group has already been created through console or management API"; case ErrorCode::RequestTimeout: return "Timeout when connecting, reading from or writing to brokers."; diff --git a/src/main/cpp/rocketmq/FilterExpression.cpp b/src/main/cpp/base/FilterExpression.cpp similarity index 89% rename from src/main/cpp/rocketmq/FilterExpression.cpp rename to src/main/cpp/base/FilterExpression.cpp index a60290b8d..b187994d1 100644 --- a/src/main/cpp/rocketmq/FilterExpression.cpp +++ b/src/main/cpp/base/FilterExpression.cpp @@ -14,18 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "FilterExpression.h" +#include "rocketmq/FilterExpression.h" ROCKETMQ_NAMESPACE_BEGIN -bool FilterExpression::accept(const MQMessageExt& message) const { - +bool FilterExpression::accept(const Message& message) const { switch (type_) { case ExpressionType::TAG: { if (WILD_CARD_TAG == content_) { return true; } else { - return message.getTags() == content_; + return message.tag() == content_; } } diff --git a/src/main/cpp/base/MQMessage.cpp b/src/main/cpp/base/MQMessage.cpp deleted file mode 100644 index 2015a618f..000000000 --- a/src/main/cpp/base/MQMessage.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/MQMessage.h" -#include "MessageAccessor.h" -#include "MessageImpl.h" -#include "MixAll.h" -#include "Protocol.h" -#include "UniqueIdGenerator.h" -#include "UtilAll.h" -#include "rocketmq/MQMessageExt.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -MQMessage::MQMessage() : MQMessage("", "", "", "") { -} - -MQMessage::MQMessage(const std::string& topic, const std::string& body) : MQMessage(topic, "", "", body) { -} - -MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& body) - : MQMessage(topic, tags, "", body) { -} - -MQMessage::MQMessage(const std::string& topic, const std::string& tags, const std::string& keys, - const std::string& body) - : impl_(new MessageImpl) { - impl_->topic_.name = topic; - impl_->system_attribute_.tag = tags; - if (!keys.empty()) { - impl_->system_attribute_.keys.emplace_back(keys); - } - - impl_->system_attribute_.born_host = UtilAll::hostname(); - impl_->system_attribute_.born_timestamp = absl::Now(); - impl_->system_attribute_.message_id = UniqueIdGenerator::instance().next(); - - impl_->body_.clear(); - impl_->body_.reserve(body.length()); - impl_->body_.append(body.data(), body.length()); -} - -MQMessage::~MQMessage() { - delete impl_; -} - -MQMessage::MQMessage(const MQMessage& other) { - impl_ = new MessageImpl(*other.impl_); -} - -MQMessage& MQMessage::operator=(const MQMessage& other) { - if (this == &other) { - return *this; - } - *impl_ = *(other.impl_); - return *this; -} - -const std::string& MQMessage::getMsgId() const { - return impl_->system_attribute_.message_id; -} - -std::string MQMessage::getBornHost() const { - return impl_->system_attribute_.born_host; -} - -std::chrono::system_clock::time_point MQMessage::deliveryTimestamp() const { - return absl::ToChronoTime(impl_->system_attribute_.delivery_timestamp); -} - -void MQMessage::setProperty(const std::string& name, const std::string& value) { - impl_->user_attribute_map_[name] = value; -} - -std::string MQMessage::getProperty(const std::string& name) const { - auto it = impl_->user_attribute_map_.find(name); - if (impl_->user_attribute_map_.end() == it) { - return ""; - } - return it->second; -} - -const std::string& MQMessage::getTopic() const { - return impl_->topic_.name; -} - -void MQMessage::setTopic(const std::string& topic) { - impl_->topic_.name = topic; -} - -void MQMessage::setTopic(const char* data, int len) { - impl_->topic_.name = std::string(data, len); -} - -std::string MQMessage::getTags() const { - return impl_->system_attribute_.tag; -} - -void MQMessage::setTags(const std::string& tags) { - impl_->system_attribute_.tag = tags; -} - -const std::vector& MQMessage::getKeys() const { - return impl_->system_attribute_.keys; -} - -void MQMessage::setKey(const std::string& key) { - impl_->system_attribute_.keys.push_back(key); -} - -void MQMessage::setKeys(const std::vector& keys) { - impl_->system_attribute_.keys = keys; -} - -int MQMessage::getDelayTimeLevel() const { - return impl_->system_attribute_.delay_level; -} - -void MQMessage::setDelayTimeLevel(int level) { - impl_->system_attribute_.delay_level = level; -} - -const std::string& MQMessage::traceContext() const { - return impl_->system_attribute_.trace_context; -} - -void MQMessage::traceContext(const std::string& trace_context) { - impl_->system_attribute_.trace_context = trace_context; -} - -const std::string& MQMessage::getBody() const { - return impl_->body_; -} - -void MQMessage::setBody(const char* body, int len) { - impl_->body_.clear(); - impl_->body_.reserve(len); - impl_->body_.append(body, len); -} - -void MQMessage::setBody(const std::string& body) { - impl_->body_ = body; -} - -uint32_t MQMessage::bodyLength() const { - return impl_->body_.length(); -} - -const std::map& MQMessage::getProperties() const { - return impl_->user_attribute_map_; -} - -void MQMessage::setProperties(const std::map& properties) { - for (const auto& it : properties) { - impl_->user_attribute_map_.insert({it.first, it.second}); - } -} - -void MQMessage::messageType(MessageType message_type) { - impl_->system_attribute_.message_type = message_type; -} - -MessageType MQMessage::messageType() const { - return impl_->system_attribute_.message_type; -} - -void MQMessage::bindMessageGroup(absl::string_view message_group) { - impl_->system_attribute_.message_group.append(message_group.data(), message_group.length()); - messageType(MessageType::FIFO); -} - -const std::string& MQMessage::messageGroup() const { - return impl_->system_attribute_.message_group; -} - -ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/MQMessageExt.cpp b/src/main/cpp/base/MQMessageExt.cpp deleted file mode 100644 index 21a947614..000000000 --- a/src/main/cpp/base/MQMessageExt.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/MQMessageExt.h" -#include "MessageImpl.h" - -ROCKETMQ_NAMESPACE_BEGIN - -MQMessageExt::MQMessageExt() : MQMessage() { -} - -MQMessageExt::MQMessageExt(const MQMessageExt& other) : MQMessage(other) { -} - -MQMessageExt& MQMessageExt::operator=(const MQMessageExt& other) { - if (this == &other) { - return *this; - } - - *impl_ = *(other.impl_); - return *this; -} - -int32_t MQMessageExt::getQueueId() const { - return impl_->system_attribute_.partition_id; -} - -std::chrono::system_clock::time_point MQMessageExt::bornTimestamp() const { - return absl::ToChronoTime(impl_->system_attribute_.born_timestamp); -} - -int64_t MQMessageExt::getBornTimestamp() const { - return absl::ToUnixMillis(impl_->system_attribute_.born_timestamp); -} - -std::chrono::system_clock::time_point MQMessageExt::storeTimestamp() const { - return absl::ToChronoTime(impl_->system_attribute_.store_timestamp); -} - -int64_t MQMessageExt::getStoreTimestamp() const { - return absl::ToUnixMillis(impl_->system_attribute_.store_timestamp); -} - -std::string MQMessageExt::getStoreHost() const { - return impl_->system_attribute_.store_host; -} - -int64_t MQMessageExt::getQueueOffset() const { - return impl_->system_attribute_.partition_offset; -} - -int32_t MQMessageExt::getDeliveryAttempt() const { - return impl_->system_attribute_.attempt_times; -} - -const std::string& MQMessageExt::receiptHandle() const { - return impl_->system_attribute_.receipt_handle; -} - -bool MQMessageExt::operator==(const MQMessageExt& other) { - return impl_->system_attribute_.message_id == other.impl_->system_attribute_.message_id; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/Message.cpp b/src/main/cpp/base/Message.cpp new file mode 100644 index 000000000..f20b4cd68 --- /dev/null +++ b/src/main/cpp/base/Message.cpp @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/Message.h" + +#include "UniqueIdGenerator.h" +#include "absl/memory/memory.h" + +ROCKETMQ_NAMESPACE_BEGIN + +Message::Message() { + id_ = UniqueIdGenerator::instance().next(); +} + +MessageBuilder Message::newBuilder() { + return {}; +} + +MessageBuilder::MessageBuilder() : message_(absl::make_unique()) { +} + +MessageBuilder& MessageBuilder::withTopic(std::string topic) { + message_->topic_.swap(topic); + return *this; +} + +MessageBuilder& MessageBuilder::withTag(std::string tag) { + message_->tag_.swap(tag); + return *this; +} + +MessageBuilder& MessageBuilder::withKeys(std::vector keys) { + message_->keys_.swap(keys); + return *this; +} + +MessageBuilder& MessageBuilder::withTraceContext(std::string trace_context) { + message_->trace_context_.swap(trace_context); + return *this; +} + +MessageBuilder& MessageBuilder::withBody(std::string body) { + message_->body_.swap(body); + return *this; +} + +MessageBuilder& MessageBuilder::withGroup(std::string group) { + message_->group_.swap(group); + return *this; +} + +MessageBuilder& MessageBuilder::withProperties(std::unordered_map properties) { + message_->properties_ = std::move(properties); + return *this; +} + +MessageConstPtr MessageBuilder::build() { + return std::move(message_); +} + +MessageBuilder& MessageBuilder::withId(std::string id) { + message_->id_ = std::move(id); + return *this; +} + +MessageBuilder& MessageBuilder::withBornTime(std::chrono::system_clock::time_point born_time) { + message_->born_time_ = born_time; + return *this; +} + +MessageBuilder& MessageBuilder::withBornHost(std::string born_host) { + message_->born_host_ = std::move(born_host); + return *this; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MessageAccessor.cpp b/src/main/cpp/base/MessageAccessor.cpp deleted file mode 100644 index 957f07652..000000000 --- a/src/main/cpp/base/MessageAccessor.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MessageAccessor.h" -#include "MessageImpl.h" -#include "rocketmq/MQMessage.h" - -ROCKETMQ_NAMESPACE_BEGIN - -void MessageAccessor::setMessageId(MQMessageExt& message, std::string message_id) { - message.impl_->system_attribute_.message_id = std::move(message_id); -} - -void MessageAccessor::setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp) { - message.impl_->system_attribute_.born_timestamp = born_timestamp; -} - -void MessageAccessor::setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp) { - message.impl_->system_attribute_.store_timestamp = store_timestamp; -} - -void MessageAccessor::setQueueId(MQMessageExt& message, int32_t queue_id) { - message.impl_->system_attribute_.partition_id = queue_id; -} - -void MessageAccessor::setQueueOffset(MQMessageExt& message, int64_t queue_offset) { - message.impl_->system_attribute_.partition_offset = queue_offset; -} - -void MessageAccessor::setBornHost(MQMessageExt& message, std::string born_host) { - message.impl_->system_attribute_.born_host = std::move(born_host); -} - -void MessageAccessor::setStoreHost(MQMessageExt& message, std::string store_host) { - message.impl_->system_attribute_.store_host = std::move(store_host); -} - -void MessageAccessor::setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp) { - message.impl_->system_attribute_.delivery_timestamp = delivery_timestamp; -} - -void MessageAccessor::setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times) { - message.impl_->system_attribute_.attempt_times = attempt_times; -} - -void MessageAccessor::setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp) { - message.impl_->system_attribute_.decode_timestamp = decode_timestamp; -} - -absl::Time MessageAccessor::decodedTimestamp(const MQMessageExt& message) { - return message.impl_->system_attribute_.decode_timestamp; -} - -void MessageAccessor::setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period) { - message.impl_->system_attribute_.invisible_period = invisible_period; -} - -void MessageAccessor::setReceiptHandle(MQMessageExt& message, std::string receipt_handle) { - message.impl_->system_attribute_.receipt_handle = std::move(receipt_handle); -} - -void MessageAccessor::setTraceContext(MQMessageExt& message, std::string trace_context) { - message.impl_->system_attribute_.trace_context = std::move(trace_context); -} - -void MessageAccessor::setMessageType(MQMessage& message, MessageType message_type) { - message.impl_->system_attribute_.message_type = message_type; -} - -void MessageAccessor::setTargetEndpoint(MQMessage& message, const std::string& target_endpoint) { - message.impl_->system_attribute_.target_endpoint = target_endpoint; -} - -const std::string& MessageAccessor::targetEndpoint(const MQMessage& message) { - return message.impl_->system_attribute_.target_endpoint; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/api/ErrorCodeTest.cpp b/src/main/cpp/base/MessageExt.cpp similarity index 75% rename from src/test/cpp/ut/api/ErrorCodeTest.cpp rename to src/main/cpp/base/MessageExt.cpp index c63df2f33..5477df764 100644 --- a/src/test/cpp/ut/api/ErrorCodeTest.cpp +++ b/src/main/cpp/base/MessageExt.cpp @@ -14,17 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "rocketmq/ErrorCode.h" -#include "gtest/gtest.h" -#include +#include "MessageExt.h" ROCKETMQ_NAMESPACE_BEGIN -TEST(ErrorCodeTest, testErrorCode) { - std::error_code ec = ErrorCode::BadRequest; - if (ec) { - std::cout << ec.message() << std::endl; +rmq::MessageType typeOf(const Message& message) { + if (message.group().has_value()) { + return rmq::MessageType::FIFO; } + + if (message.deliveryTimestamp().has_value()) { + return rmq::MessageType::DELAY; + } + + return rmq::MessageType::NORMAL; } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MetadataConstants.cpp b/src/main/cpp/base/MetadataConstants.cpp index 0aa8d933d..71e3da98f 100644 --- a/src/main/cpp/base/MetadataConstants.cpp +++ b/src/main/cpp/base/MetadataConstants.cpp @@ -18,19 +18,35 @@ ROCKETMQ_NAMESPACE_BEGIN -const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; -const char* MetadataConstants::NAMESPACE_KEY = "x-mq-namespace"; -const char* MetadataConstants::AUTHORIZATION = "authorization"; -const char* MetadataConstants::STS_SESSION_TOKEN = "x-mq-session-token"; -const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; -const char* MetadataConstants::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; -const char* MetadataConstants::CREDENTIAL_KEY = "Credential"; -const char* MetadataConstants::SIGNED_HEADERS_KEY = "SignedHeaders"; -const char* MetadataConstants::SIGNATURE_KEY = "Signature"; -const char* MetadataConstants::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; -const char* MetadataConstants::LANGUAGE_KEY = "x-mq-language"; -const char* MetadataConstants::CLIENT_VERSION_KEY = "x-mq-client-version"; +#ifndef CLIENT_VERSION_MAJOR +#define CLIENT_VERSION_MAJOR "5" +#endif + +#ifndef CLIENT_VERSION_MINOR +#define CLIENT_VERSION_MINOR "0" +#endif + +#ifndef CLIENT_VERSION_PATCH +#define CLIENT_VERSION_PATCH "0" +#endif + +const char* MetadataConstants::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; + +const char* MetadataConstants::TENANT_ID_KEY = "x-mq-tenant-id"; +const char* MetadataConstants::CLIENT_ID_KEY = "x-mq-client-id"; +const char* MetadataConstants::NAMESPACE_KEY = "x-mq-namespace"; +const char* MetadataConstants::AUTHORIZATION = "authorization"; +const char* MetadataConstants::STS_SESSION_TOKEN = "x-mq-session-token"; +const char* MetadataConstants::DATE_TIME_KEY = "x-mq-date-time"; +const char* MetadataConstants::ALGORITHM_KEY = "MQv2-HMAC-SHA1"; +const char* MetadataConstants::CREDENTIAL_KEY = "Credential"; +const char* MetadataConstants::SIGNED_HEADERS_KEY = "SignedHeaders"; +const char* MetadataConstants::SIGNATURE_KEY = "Signature"; +const char* MetadataConstants::DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ"; +const char* MetadataConstants::LANGUAGE_KEY = "x-mq-language"; +const char* MetadataConstants::CLIENT_VERSION_KEY = "x-mq-client-version"; const char* MetadataConstants::PROTOCOL_VERSION_KEY = "x-mq-protocol-version"; -const char* MetadataConstants::REQUEST_ID_KEY = "x-mq-request-id"; +const char* MetadataConstants::REQUEST_ID_KEY = "x-mq-request-id"; +const char* MetadataConstants::SERVICE_NAME = "RocketMQ"; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/MixAll.cpp b/src/main/cpp/base/MixAll.cpp index 0bf273a6f..4d244cec8 100644 --- a/src/main/cpp/base/MixAll.cpp +++ b/src/main/cpp/base/MixAll.cpp @@ -17,6 +17,7 @@ #include "MixAll.h" #include +#include #include #include "absl/random/random.h" @@ -55,7 +56,7 @@ const std::chrono::duration MixAll::DEFAULT_INVISIBLE_TIME_ = std::ch const std::chrono::duration MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_ = std::chrono::seconds(120); -const int32_t MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_ = 3; +const std::size_t MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_ = 3; const std::string MixAll::PROPERTY_TRANSACTION_PREPARED_ = "TRAN_MSG"; @@ -63,6 +64,8 @@ const std::string MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_ = "AVG"; const uint32_t MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_ = 1024 * 1024 * 4; +const std::chrono::milliseconds MixAll::DefaultReceiveMessageTimeout = std::chrono::seconds(30); + const char* MixAll::HOME_PROFILE_ENV_ = "HOME"; const char* MixAll::MESSAGE_KEY_SEPARATOR = " "; @@ -135,11 +138,11 @@ const char* MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION = "__await_consumption"; const char* MixAll::SPAN_ANNOTATION_MESSAGE_KEYS = "__message_keys"; const char* MixAll::SPAN_ANNOTATION_ATTR_START_TIME = "__start_time"; -bool MixAll::validate(const MQMessage& message) { - if (message.getTopic().empty()) { +bool MixAll::validate(const Message& message) { + if (message.topic().empty()) { return false; } - const std::string& topic = message.getTopic(); + const std::string& topic = message.topic(); // Topic should not start with "CID" or "GID" which are reserved prefix if (absl::StartsWith(topic, "CID") || absl::StartsWith(topic, "GID")) { return false; @@ -150,7 +153,7 @@ bool MixAll::validate(const MQMessage& message) { return false; } - uint32_t body_length = message.bodyLength(); + uint32_t body_length = message.body().length(); if (!body_length || body_length > MAX_MESSAGE_BODY_SIZE) { return false; } @@ -295,4 +298,28 @@ bool MixAll::isIPv4(absl::string_view host) { return RE2::FullMatch(re2::StringPiece(host.data(), host.length()), IP_REGEX); } +const char* MixAll::osName() { +#if INTPTR_MAX == INT32_MAX +#define BITS " 32-bit" +#elif INTPTR_MAX == INT64_MAX +#define BITS " 64-bit" +#else +#define BITS "" +#endif + +#if defined(_WIN32) || defined(_WIN64) + return "Windows" BITS; +#elif __APPLE__ || __MACH__ + return "Mac OSX" BITS; +#elif __linux__ + return "Linux" BITS; +#elif __FreeBSD__ + return "FreeBSD" BITS; +#elif __unix || __unix__ + return "Unix" BITS; +#else + return "Other" BITS; +#endif +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/Protocol.cpp b/src/main/cpp/base/Protocol.cpp index be031e324..91a643b3d 100644 --- a/src/main/cpp/base/Protocol.cpp +++ b/src/main/cpp/base/Protocol.cpp @@ -14,14 +14,140 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "Protocol.h" ROCKETMQ_NAMESPACE_BEGIN -#ifndef CLIENT_PROTOCOL_VERSION -#define CLIENT_PROTOCOL_VERSION "v1" -#endif +const char* protocolVersion() { + static const char* protocol_version = "v2"; + return protocol_version; +} + +bool writable(rmq::Permission p) { + switch (p) { + case rmq::Permission::WRITE: + case rmq::Permission::READ_WRITE: + return true; + default: { + return false; + } + } +} + +bool readable(rmq::Permission p) { + switch (p) { + case rmq::Permission::READ: + case rmq::Permission::READ_WRITE: + return true; + default: + return false; + } +} + +bool operator<(const rmq::Resource& lhs, const rmq::Resource& rhs) { + return lhs.resource_namespace() < rhs.resource_namespace() || lhs.name() < rhs.name(); +} + +bool operator==(const rmq::Resource& lhs, const rmq::Resource& rhs) { + return lhs.resource_namespace() == rhs.resource_namespace() && lhs.name() == rhs.name(); +} + +bool operator<(const rmq::Broker& lhs, const rmq::Broker& rhs) { + return lhs.name() < rhs.name() || lhs.id() < rhs.id(); +} + +bool operator==(const rmq::Broker& lhs, const rmq::Broker& rhs) { + return lhs.name() == rhs.name() && lhs.id() == rhs.id(); +} + +bool operator<(const rmq::MessageQueue& lhs, const rmq::MessageQueue& rhs) { + return lhs.topic() < rhs.topic() || lhs.id() < rhs.id() || lhs.broker() < rhs.broker() || + lhs.permission() < rhs.permission(); +} + +bool operator==(const rmq::MessageQueue& lhs, const rmq::MessageQueue& rhs) { + return lhs.topic() == rhs.topic() && lhs.id() == rhs.id() && lhs.broker() == rhs.broker() && + lhs.permission() == rhs.permission(); +} + +std::string simpleNameOf(const rmq::MessageQueue& m) { + return fmt::format("{}{}-{}-{}", m.topic().resource_namespace(), m.topic().name(), m.id(), m.broker().name()); +} + +bool operator==(const std::vector& lhs, const std::vector& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (std::size_t i = 0; i < lhs.size(); i++) { + if (lhs[i] == rhs[i]) { + continue; + } + return false; + } + + return true; +} + +bool operator!=(const std::vector& lhs, const std::vector& rhs) { + return !(lhs == rhs); +} + +std::string urlOf(const rmq::MessageQueue& message_queue) { + const auto& endpoints = message_queue.broker().endpoints(); + const auto& addresses = endpoints.addresses(); + switch (endpoints.scheme()) { + case rmq::AddressScheme::DOMAIN_NAME: { + assert(addresses.size() == 1); + auto first = addresses.begin(); + return fmt::format("dns:{}:{}", first->host(), first->port()); + } + case rmq::AddressScheme::IPv4: { + assert(!addresses.empty()); + auto it = addresses.cbegin(); + std::string result = fmt::format("ipv4:{}:{}", it->host(), it->port()); + for (++it; it != addresses.cend(); ++it) { + result.append(fmt::format(",{}:{}", it->host(), it->port())); + } + return result; + } + case rmq::AddressScheme::IPv6: { + assert(!addresses.empty()); + auto it = addresses.cbegin(); + std::string result = fmt::format("ipv6:{}:{}", it->host(), it->port()); + for (++it; it != addresses.cend(); ++it) { + result.append(fmt::format(",{}:{}", it->host(), it->port())); + } + return result; + } + default: { + break; + } + } + return {}; +} + +bool operator<(const rmq::Assignment& lhs, const rmq::Assignment& rhs) { + return lhs.message_queue() < rhs.message_queue(); +} + +bool operator==(const rmq::Assignment& lhs, const rmq::Assignment& rhs) { + return lhs.message_queue() == rhs.message_queue(); +} + +bool operator==(const std::vector& lhs, const std::vector& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } -const char* Protocol::PROTOCOL_VERSION = CLIENT_PROTOCOL_VERSION; + for (std::size_t i = 0; i < lhs.size(); i++) { + if (lhs[i] == rhs[i]) { + continue; + } + return false; + } + return true; +} -ROCKETMQ_NAMESPACE_END \ No newline at end of file +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/Tracing.cpp b/src/main/cpp/base/Tracing.cpp new file mode 100644 index 000000000..9eb733577 --- /dev/null +++ b/src/main/cpp/base/Tracing.cpp @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/Tracing.h" + +ROCKETMQ_NAMESPACE_BEGIN + +opencensus::trace::Sampler* traceSampler() { + static opencensus::trace::NeverSampler sampler; + return &sampler; +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/base/include/InvocationContext.h b/src/main/cpp/base/include/InvocationContext.h index cad3e0618..910a65dcf 100644 --- a/src/main/cpp/base/include/InvocationContext.h +++ b/src/main/cpp/base/include/InvocationContext.h @@ -30,7 +30,6 @@ #include "LoggerImpl.h" #include "MetadataConstants.h" #include "UniqueIdGenerator.h" -#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -86,6 +85,7 @@ struct InvocationContext : public BaseInvocationContext { SPDLOG_WARN("Asynchronous RPC[{}.{}] timed out, elapsing {}ms, deadline-over-due: {}ms", absl::FormatTime(created_time, absl::UTCTimeZone()), elapsed, diff); } + try { if (callback) { callback(this); diff --git a/src/main/cpp/base/include/MessageAccessor.h b/src/main/cpp/base/include/MessageAccessor.h deleted file mode 100644 index 371bd23a4..000000000 --- a/src/main/cpp/base/include/MessageAccessor.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "rocketmq/MQMessageExt.h" - -#include "Protocol.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MessageAccessor { - -public: - static void setMessageId(MQMessageExt& message, std::string message_id); - - static void setBornTimestamp(MQMessageExt& message, absl::Time born_timestamp); - - static void setStoreTimestamp(MQMessageExt& message, absl::Time store_timestamp); - - static void setQueueId(MQMessageExt& message, int32_t queue_id); - - static void setQueueOffset(MQMessageExt& message, int64_t queue_offset); - - static void setBornHost(MQMessageExt& message, std::string born_host); - - static void setStoreHost(MQMessageExt& message, std::string store_host); - - static void setDeliveryTimestamp(MQMessageExt& message, absl::Time delivery_timestamp); - - static void setDeliveryAttempt(MQMessageExt& message, int32_t attempt_times); - - static void setDecodedTimestamp(MQMessageExt& message, absl::Time decode_timestamp); - static absl::Time decodedTimestamp(const MQMessageExt& message); - - static void setInvisiblePeriod(MQMessageExt& message, absl::Duration invisible_period); - - static void setReceiptHandle(MQMessageExt& message, std::string receipt_handle); - static void setTraceContext(MQMessageExt& message, std::string trace_context); - - static void setMessageType(MQMessage& message, MessageType message_type); - - static void setTargetEndpoint(MQMessage& message, const std::string& target_endpoint); - - static const std::string& targetEndpoint(const MQMessage& message); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageExt.h b/src/main/cpp/base/include/MessageExt.h new file mode 100644 index 000000000..31106bba5 --- /dev/null +++ b/src/main/cpp/base/include/MessageExt.h @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "Protocol.h" +#include "rocketmq/Message.h" + +ROCKETMQ_NAMESPACE_BEGIN + +rmq::MessageType typeOf(const Message& message); + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MessageImpl.h b/src/main/cpp/base/include/MessageImpl.h deleted file mode 100644 index 079ba33fe..000000000 --- a/src/main/cpp/base/include/MessageImpl.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "Protocol.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MessageImpl { -protected: - Resource topic_; - std::map user_attribute_map_; - SystemAttribute system_attribute_; - std::string body_; - friend class MQMessage; - friend class MQMessageExt; - friend class MessageAccessor; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MetadataConstants.h b/src/main/cpp/base/include/MetadataConstants.h index ac3af5ba8..a02fc1919 100644 --- a/src/main/cpp/base/include/MetadataConstants.h +++ b/src/main/cpp/base/include/MetadataConstants.h @@ -24,6 +24,7 @@ class MetadataConstants { public: static const char* REQUEST_ID_KEY; static const char* TENANT_ID_KEY; + static const char* CLIENT_ID_KEY; static const char* NAMESPACE_KEY; static const char* AUTHORIZATION; static const char* STS_SESSION_TOKEN; @@ -36,6 +37,8 @@ class MetadataConstants { static const char* LANGUAGE_KEY; static const char* CLIENT_VERSION_KEY; static const char* PROTOCOL_VERSION_KEY; + static const char* CLIENT_VERSION; + static const char* SERVICE_NAME; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/MixAll.h b/src/main/cpp/base/include/MixAll.h index ae272dc2e..148373708 100644 --- a/src/main/cpp/base/include/MixAll.h +++ b/src/main/cpp/base/include/MixAll.h @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" #include "re2/re2.h" -#include "rocketmq/MQMessage.h" +#include "rocketmq/Message.h" ROCKETMQ_NAMESPACE_BEGIN @@ -49,7 +49,9 @@ class MixAll { static const std::chrono::duration PROCESS_QUEUE_EXPIRATION_THRESHOLD_; - static const int32_t MAX_SEND_MESSAGE_ATTEMPT_TIMES_; + static const std::chrono::milliseconds DefaultReceiveMessageTimeout; + + static const std::size_t MAX_SEND_MESSAGE_ATTEMPT_TIMES_; static const std::string PROPERTY_TRANSACTION_PREPARED_; @@ -142,7 +144,7 @@ class MixAll { * @param message * @return */ - static bool validate(const MQMessage& message); + static bool validate(const Message& message); static uint32_t random(uint32_t left, uint32_t right); @@ -162,6 +164,8 @@ class MixAll { static bool isIPv4(absl::string_view host); + static const char* osName(); + private: static bool hexCharValue(char c, uint8_t& value); }; diff --git a/src/main/cpp/base/include/Protocol.h b/src/main/cpp/base/include/Protocol.h index 7d52fd6c7..5a8e671f6 100644 --- a/src/main/cpp/base/include/Protocol.h +++ b/src/main/cpp/base/include/Protocol.h @@ -16,65 +16,77 @@ */ #pragma once -#include -#include -#include +#include "absl/hash/hash.h" +#include "fmt/format.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" +#include "apache/rocketmq/v2/definition.grpc.pb.h" +#include "apache/rocketmq/v2/definition.pb.h" +#include "apache/rocketmq/v2/service.grpc.pb.h" +#include "apache/rocketmq/v2/service.pb.h" -#include "DigestType.h" -#include "Encoding.h" -#include "rocketmq/MessageType.h" +#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN -class Protocol { -public: - static const char* PROTOCOL_VERSION; -}; - -struct Digest { - DigestType digest_type{DigestType::MD5}; - std::string checksum; - Digest() = default; -}; - -struct Resource { - /** - * Abstract resource namespace - */ - std::string resource_namespace; - - /** - * Resource name, which remains unique within given abstract resource namespace. - */ - std::string name; -}; - -struct SystemAttribute { - std::string tag; - std::vector keys; - std::string message_id; - Digest digest; - Encoding body_encoding; - MessageType message_type; - absl::Time born_timestamp{absl::Now()}; - std::string born_host; - absl::Time store_timestamp{absl::UnixEpoch()}; - std::string store_host; - absl::Time delivery_timestamp{absl::UnixEpoch()}; - absl::Time decode_timestamp{absl::Now()}; - int32_t delay_level{0}; - std::string receipt_handle; - int32_t partition_id{0}; - int64_t partition_offset{0}; - absl::Duration invisible_period; - int32_t attempt_times{0}; - Resource publisher_group; - std::string trace_context; - std::string target_endpoint; - std::string message_group; -}; - -ROCKETMQ_NAMESPACE_END +namespace rmq = apache::rocketmq::v2; + +using ChangeInvisibleDurationRequest = rmq::ChangeInvisibleDurationRequest; +using ChangeInvisibleDurationResponse = rmq::ChangeInvisibleDurationResponse; +using QueryRouteRequest = rmq::QueryRouteRequest; +using QueryRouteResponse = rmq::QueryRouteResponse; +using SendMessageRequest = rmq::SendMessageRequest; +using SendMessageResponse = rmq::SendMessageResponse; +using QueryAssignmentRequest = rmq::QueryAssignmentRequest; +using QueryAssignmentResponse = rmq::QueryAssignmentResponse; +using ReceiveMessageRequest = rmq::ReceiveMessageRequest; +using ReceiveMessageResponse = rmq::ReceiveMessageResponse; +using AckMessageRequest = rmq::AckMessageRequest; +using AckMessageResponse = rmq::AckMessageResponse; +using HeartbeatRequest = rmq::HeartbeatRequest; +using HeartbeatResponse = rmq::HeartbeatResponse; +using EndTransactionRequest = rmq::EndTransactionRequest; +using EndTransactionResponse = rmq::EndTransactionResponse; +using RecoverOrphanedTransactionCommand = rmq::RecoverOrphanedTransactionCommand; +using PrintThreadStackTraceCommand = rmq::PrintThreadStackTraceCommand; +using ThreadStackTrace = rmq::ThreadStackTrace; +using VerifyMessageCommand = rmq::VerifyMessageCommand; +using VerifyMessageResult = rmq::VerifyMessageResult; +using TelemetryCommand = rmq::TelemetryCommand; +using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; +using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; +using NotifyClientTerminationRequest = rmq::NotifyClientTerminationRequest; +using NotifyClientTerminationResponse = rmq::NotifyClientTerminationResponse; + +const char* protocolVersion(); + +bool writable(rmq::Permission p); + +bool readable(rmq::Permission p); + +bool operator<(const rmq::Resource& lhs, const rmq::Resource& rhs); + +bool operator==(const rmq::Resource& lhs, const rmq::Resource& rhs); + +bool operator<(const rmq::Broker& lhs, const rmq::Broker& rhs); + +bool operator==(const rmq::Broker& lhs, const rmq::Broker& rhs); + +bool operator<(const rmq::MessageQueue& lhs, const rmq::MessageQueue& rhs); + +bool operator==(const rmq::MessageQueue& lhs, const rmq::MessageQueue& rhs); + +std::string simpleNameOf(const rmq::MessageQueue& m); + +bool operator==(const std::vector& lhs, const std::vector& rhs); + +bool operator!=(const std::vector& lhs, const std::vector& rhs); + +std::string urlOf(const rmq::MessageQueue& message_queue); + +bool operator<(const rmq::Assignment& lhs, const rmq::Assignment& rhs); + +bool operator==(const rmq::Assignment& lhs, const rmq::Assignment& rhs); + +bool operator==(const std::vector& lhs, const std::vector& rhs); + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/RetryPolicy.h b/src/main/cpp/base/include/RetryPolicy.h new file mode 100644 index 000000000..5cc413863 --- /dev/null +++ b/src/main/cpp/base/include/RetryPolicy.h @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "absl/time/time.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class BackoffStrategy : std::uint8_t +{ + Customized = 0, + Exponential = 1, +}; + +struct RetryPolicy { + std::uint32_t max_attempt; + + BackoffStrategy strategy; + + // Exponential back-off + absl::Duration initial; + absl::Duration max; + float multiplier; + + // User defined back-off intervals. + std::vector next; + + std::int64_t backoff(std::size_t attempt) { + static std::int64_t default_backoff = 1000; + if (!attempt) { + attempt = 1; + } + + switch (strategy) { + case BackoffStrategy::Customized: { + if (next.empty()) { + return default_backoff; + } + if (attempt >= next.size()) { + return absl::ToInt64Milliseconds(next[next.size() - 1]); + } + return absl::ToInt64Milliseconds(next[attempt - 1]); + } + + case BackoffStrategy::Exponential: { + if (!absl::ToInt64Milliseconds(max)) { + return default_backoff; + } + + auto result = initial * pow(2, attempt); + if (result > max) { + return absl::ToInt64Milliseconds(max); + } + return absl::ToInt64Milliseconds(result); + } + default: + return default_backoff; + } + } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h b/src/main/cpp/base/include/StsCredentialsProviderImpl.h similarity index 100% rename from src/main/cpp/rocketmq/include/StsCredentialsProviderImpl.h rename to src/main/cpp/base/include/StsCredentialsProviderImpl.h diff --git a/src/main/cpp/base/include/SubscriptionEntry.h b/src/main/cpp/base/include/SubscriptionEntry.h new file mode 100644 index 000000000..9256242ae --- /dev/null +++ b/src/main/cpp/base/include/SubscriptionEntry.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "FilterExpression.h" +#include "Resource.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +struct SubscriptionEntry { + Resource topic; + FilterExpression expression; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/tests/AssignmentTest.cpp b/src/main/cpp/base/tests/AssignmentTest.cpp new file mode 100644 index 000000000..88c3c5a24 --- /dev/null +++ b/src/main/cpp/base/tests/AssignmentTest.cpp @@ -0,0 +1,23 @@ +#include "gtest/gtest.h" +#include + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class AssignmentTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } +}; + +TEST_F(AssignmentTest, testSort) { + std::vector assignments; + std::sort(assignments.begin(), assignments.end(), + [](const rmq::Assignment& lhs, const rmq::Assignment& rhs) { return lhs < rhs; }); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/tests/BUILD.bazel b/src/main/cpp/base/tests/BUILD.bazel new file mode 100644 index 000000000..054dfb5fb --- /dev/null +++ b/src/main/cpp/base/tests/BUILD.bazel @@ -0,0 +1,54 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +base_deps = [ + "//src/main/cpp/base:base_library", + "@com_google_googletest//:gtest_main", +] + +cc_test( + name = "message_builder_test", + srcs = [ + "MessageBuilderTest.cpp", + ], + deps = base_deps, +) + +cc_test( + name = "assignment_test", + srcs = [ + "AssignmentTest.cpp", + ], + deps = base_deps, +) + +cc_test( + name = "message_queue_test", + srcs = [ + "MessageQueueTest.cpp", + ], + deps = base_deps, +) + +cc_test( + name = "configuration_test", + srcs = [ + "ConfigurationTest.cpp", + ], + deps = base_deps, +) + +cc_test( + name = "mix_all_test", + srcs = [ + "MixAllTest.cpp", + ], + deps = base_deps, +) + +cc_test( + name = "retry_policy_test", + srcs = [ + "RetryPolicyTest.cpp", + ], + deps = base_deps, +) \ No newline at end of file diff --git a/src/main/cpp/base/tests/ConfigurationTest.cpp b/src/main/cpp/base/tests/ConfigurationTest.cpp new file mode 100644 index 000000000..9174e395d --- /dev/null +++ b/src/main/cpp/base/tests/ConfigurationTest.cpp @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "gtest/gtest.h" +#include "rocketmq/Configuration.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConfigurationTest : public testing::Test { +public: +protected: + std::string endpoints_{"8.8.8.8:80;8.8.4.4:80"}; + std::chrono::milliseconds request_timeout_{std::chrono::seconds(1)}; +}; + +TEST_F(ConfigurationTest, testEndpoints) { + auto configuration = Configuration::newBuilder().withEndpoints(endpoints_).build(); + ASSERT_EQ(endpoints_, configuration.endpoints()); +} + +TEST_F(ConfigurationTest, testCredentialsProvider) { + std::string access_key = "ak"; + std::string access_secret = "as"; + auto credentials_provider = std::make_shared(access_key, access_secret); + auto configuration = Configuration::newBuilder().withCredentialsProvider(credentials_provider).build(); + auto credentials = configuration.credentialsProvider()->getCredentials(); + + ASSERT_EQ(access_key, credentials.accessKey()); + ASSERT_EQ(access_secret, credentials.accessSecret()); +} + +TEST_F(ConfigurationTest, testRequestTimeout) { + auto configuration = Configuration::newBuilder().withRequestTimeout(request_timeout_).build(); + ASSERT_EQ(request_timeout_, configuration.requestTimeout()); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/tests/MessageBuilderTest.cpp b/src/main/cpp/base/tests/MessageBuilderTest.cpp new file mode 100644 index 000000000..9ad4d58e2 --- /dev/null +++ b/src/main/cpp/base/tests/MessageBuilderTest.cpp @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "MessageExt.h" +#include "gtest/gtest.h" +#include "rocketmq/Message.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageBuilderTest : public testing::Test { +protected: + std::string topic_{"TestTopic"}; + std::string tag_{"TagA"}; + std::vector keys_{"k1", "k2"}; + std::string body_{"sample body content"}; + std::string group_{"message-group"}; +}; + +TEST_F(MessageBuilderTest, testBuilder) { + MessageConstPtr message = + Message::newBuilder().withTopic(topic_).withTag(tag_).withKeys(keys_).withBody(body_).build(); + ASSERT_EQ(topic_, message->topic()); + ASSERT_EQ(tag_, message->tag()); + ASSERT_TRUE(keys_ == message->keys()); + ASSERT_EQ(body_, message->body()); + ASSERT_EQ(false, message->traceContext().has_value()); +} + +TEST_F(MessageBuilderTest, testBuilder2) { + for (std::size_t i = 0; i < 128; i++) { + MessageConstPtr message = + Message::newBuilder().withTopic(topic_).withTag(tag_).withKeys(keys_).withBody(body_).build(); + ASSERT_EQ(topic_, message->topic()); + ASSERT_EQ(tag_, message->tag()); + ASSERT_TRUE(keys_ == message->keys()); + ASSERT_EQ(body_, message->body()); + ASSERT_EQ(false, message->traceContext().has_value()); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/tests/MessageQueueTest.cpp b/src/main/cpp/base/tests/MessageQueueTest.cpp new file mode 100644 index 000000000..2db2c83cb --- /dev/null +++ b/src/main/cpp/base/tests/MessageQueueTest.cpp @@ -0,0 +1,21 @@ +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "google/protobuf/map.h" +#include "gtest/gtest.h" + +#include "Protocol.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MessageQueueTest : public testing::Test { +public: + void SetUp() override { + } + + void TearDown() override { + } +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp b/src/main/cpp/base/tests/MixAllTest.cpp similarity index 87% rename from src/test/cpp/ut/rocketmq/ConsumerTest.cpp rename to src/main/cpp/base/tests/MixAllTest.cpp index 0c950c206..e66030e39 100644 --- a/src/test/cpp/ut/rocketmq/ConsumerTest.cpp +++ b/src/main/cpp/base/tests/MixAllTest.cpp @@ -14,14 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "ConsumerMock.h" -#include "gmock/gmock.h" +#include "MixAll.h" #include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN -TEST(ConsumerMockTest, testMock) { - testing::NiceMock mock; +TEST(MixAllTest, testOsName) { + const char* os_name = MixAll::osName(); + std::cout << os_name << std::endl; } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp b/src/main/cpp/base/tests/RetryPolicyTest.cpp similarity index 65% rename from src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp rename to src/main/cpp/base/tests/RetryPolicyTest.cpp index a01cb6511..4a49bccec 100644 --- a/src/test/cpp/ut/rocketmq/DefaultMQPushConsumerTest.cpp +++ b/src/main/cpp/base/tests/RetryPolicyTest.cpp @@ -14,24 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "rocketmq/DefaultMQPushConsumer.h" - +#include "RetryPolicy.h" #include "gtest/gtest.h" ROCKETMQ_NAMESPACE_BEGIN -class DefaultMQPushConsumerTest : public testing::Test { -public: - DefaultMQPushConsumerTest() : group_name_("group-0"), consumer_(group_name_) { - } - -protected: - std::string group_name_; - DefaultMQPushConsumer consumer_; -}; - -TEST_F(DefaultMQPushConsumerTest, testGroupName) { - EXPECT_EQ(group_name_, consumer_.groupName()); +TEST(RetryPolicyTest, testBackoff) { + RetryPolicy policy{.max_attempt = 3, + .strategy = BackoffStrategy::Customized, + .next = {absl::Milliseconds(10), absl::Milliseconds(100), absl::Milliseconds(500)}}; + ASSERT_EQ(policy.backoff(1), 10); + ASSERT_EQ(policy.backoff(2), 100); + ASSERT_EQ(policy.backoff(3), 500); + ASSERT_EQ(policy.backoff(4), 500); + ASSERT_EQ(policy.backoff(10000), 500); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientConfigImpl.cpp b/src/main/cpp/client/ClientConfigImpl.cpp deleted file mode 100644 index 29304e0a2..000000000 --- a/src/main/cpp/client/ClientConfigImpl.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClientConfigImpl.h" - -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -#include "UtilAll.h" - -ROCKETMQ_NAMESPACE_BEGIN - -#ifndef CLIENT_VERSION_MAJOR -#define CLIENT_VERSION_MAJOR "5" -#endif - -#ifndef CLIENT_VERSION_MINOR -#define CLIENT_VERSION_MINOR "0" -#endif - -#ifndef CLIENT_VERSION_PATCH -#define CLIENT_VERSION_PATCH "0" -#endif - -const char* ClientConfigImpl::CLIENT_VERSION = CLIENT_VERSION_MAJOR "." CLIENT_VERSION_MINOR "." CLIENT_VERSION_PATCH; - -std::string ClientConfigImpl::steadyName() { - auto duration = std::chrono::steady_clock::now().time_since_epoch(); - return std::to_string(std::chrono::duration_cast(duration).count()); -} - -ClientConfigImpl::ClientConfigImpl(absl::string_view group_name) - : group_name_(group_name.data(), group_name.length()), io_timeout_(absl::Seconds(3)), - long_polling_timeout_(absl::Seconds(30)) { -} - -std::string ClientConfigImpl::clientId() const { - std::stringstream ss; - ss << UtilAll::hostname(); - ss << "@"; - std::string processID = std::to_string(getpid()); - ss << processID << "#"; - ss << instance_name_; - return ss.str(); -} - -const std::string& ClientConfigImpl::getInstanceName() const { - return instance_name_; -} - -void ClientConfigImpl::setInstanceName(std::string instance_name) { - instance_name_ = std::move(instance_name); -} - -const std::string& ClientConfigImpl::getGroupName() const { - return group_name_; -} - -void ClientConfigImpl::setGroupName(std::string group_name) { - group_name_ = std::move(group_name); -} - -void ClientConfigImpl::setIoTimeout(absl::Duration timeout) { - io_timeout_ = timeout; -} - -void ClientConfigImpl::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { - credentials_provider_ = std::move(credentials_provider); -} - -CredentialsProviderPtr ClientConfigImpl::credentialsProvider() { - return credentials_provider_; -} - -absl::Duration ClientConfigImpl::getIoTimeout() const { - return io_timeout_; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ClientManagerFactory.cpp b/src/main/cpp/client/ClientManagerFactory.cpp index c72aedbfa..3cacaae0a 100644 --- a/src/main/cpp/client/ClientManagerFactory.cpp +++ b/src/main/cpp/client/ClientManagerFactory.cpp @@ -27,20 +27,20 @@ ClientManagerFactory& ClientManagerFactory::getInstance() { ClientManagerPtr ClientManagerFactory::getClientManager(const ClientConfig& client_config) { { absl::MutexLock lock(&client_manager_table_mtx_); - auto search = client_manager_table_.find(client_config.resourceNamespace()); + auto search = client_manager_table_.find(client_config.resource_namespace); if (search != client_manager_table_.end()) { ClientManagerPtr client_manager = search->second.lock(); if (client_manager) { - SPDLOG_DEBUG("Re-use existing client_manager[resource_namespace={}]", client_config.resourceNamespace()); + SPDLOG_DEBUG("Re-use existing client_manager[resource_namespace={}]", client_config.resource_namespace); return client_manager; } else { - client_manager_table_.erase(client_config.resourceNamespace()); + client_manager_table_.erase(client_config.resource_namespace); } } - ClientManagerPtr client_manager = std::make_shared(client_config.resourceNamespace()); + ClientManagerPtr client_manager = std::make_shared(client_config.resource_namespace); std::weak_ptr client_instance_weak_ptr(client_manager); - client_manager_table_.insert_or_assign(client_config.resourceNamespace(), client_instance_weak_ptr); - SPDLOG_INFO("Created a new client manager[resource_namespace={}]", client_config.resourceNamespace()); + client_manager_table_.insert_or_assign(client_config.resource_namespace, client_instance_weak_ptr); + SPDLOG_INFO("Created a new client manager[resource_namespace={}]", client_config.resource_namespace); return client_manager; } } diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index d8a43e559..25fc55923 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -17,59 +17,45 @@ #include "ClientManagerImpl.h" #include +#include #include #include #include #include #include -#include "ReceiveMessageResult.h" -#include "Scheduler.h" -#include "google/rpc/code.pb.h" - #include "InvocationContext.h" #include "LogInterceptor.h" #include "LogInterceptorFactory.h" #include "LoggerImpl.h" -#include "MessageAccessor.h" +#include "MessageExt.h" #include "MetadataConstants.h" #include "MixAll.h" -#include "Partition.h" #include "Protocol.h" +#include "ReceiveMessageContext.h" #include "RpcClient.h" #include "RpcClientImpl.h" +#include "Scheduler.h" #include "TlsHelper.h" #include "UtilAll.h" +#include "google/protobuf/util/time_util.h" #include "grpcpp/create_channel.h" #include "rocketmq/ErrorCode.h" -#include "rocketmq/MQMessageExt.h" +#include "rocketmq/SendReceipt.h" ROCKETMQ_NAMESPACE_BEGIN ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) : scheduler_(std::make_shared()), resource_namespace_(std::move(resource_namespace)), - state_(State::CREATED), completion_queue_(std::make_shared()), + state_(State::CREATED), callback_thread_pool_(absl::make_unique(std::thread::hardware_concurrency())), latency_histogram_("Message-Latency", 11) { spdlog::set_level(spdlog::level::trace); assignLabels(latency_histogram_); - server_authorization_check_config_ = std::make_shared( - std::make_shared()); - - // Make use of encryption only at the moment. - std::vector identity_key_cert_list; - grpc::experimental::IdentityKeyCertPair pair{}; - pair.private_key = TlsHelper::client_private_key; - pair.certificate_chain = TlsHelper::client_certificate_chain; - - identity_key_cert_list.emplace_back(pair); - certificate_provider_ = - std::make_shared(TlsHelper::CA, identity_key_cert_list); - tls_channel_credential_options_.set_server_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION); - tls_channel_credential_options_.set_certificate_provider(certificate_provider_); - tls_channel_credential_options_.set_server_authorization_check_config(server_authorization_check_config_); - tls_channel_credential_options_.watch_root_certs(); - tls_channel_credential_options_.watch_identity_key_cert_pairs(); + + certificate_verifier_ = grpc::experimental::ExternalCertificateVerifier::Create(); + tls_channel_credential_options_.set_verify_server_certs(false); + tls_channel_credential_options_.set_check_call_host(false); channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); // Use unlimited receive message size. @@ -97,6 +83,8 @@ ClientManagerImpl::ClientManagerImpl(std::string resource_namespace) */ channel_arguments_.SetInt(GRPC_ARG_ENABLE_RETRIES, 0); + channel_arguments_.SetSslTargetNameOverride("localhost"); + SPDLOG_INFO("ClientManager[ResourceNamespace={}] created", resource_namespace_); } @@ -118,14 +106,6 @@ void ClientManagerImpl::start() { std::weak_ptr client_instance_weak_ptr = shared_from_this(); - auto health_check_functor = [client_instance_weak_ptr]() { - auto client_instance = client_instance_weak_ptr.lock(); - if (client_instance) { - client_instance->doHealthCheck(); - } - }; - health_check_task_id_ = scheduler_->schedule(health_check_functor, HEALTH_CHECK_TASK_NAME, std::chrono::seconds(5), - std::chrono::seconds(5)); auto heartbeat_functor = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); if (client_instance) { @@ -134,8 +114,7 @@ void ClientManagerImpl::start() { }; heartbeat_task_id_ = scheduler_->schedule(heartbeat_functor, HEARTBEAT_TASK_NAME, std::chrono::seconds(1), std::chrono::seconds(10)); - - completion_queue_thread_ = std::thread(std::bind(&ClientManagerImpl::pollCompletionQueue, this)); + SPDLOG_DEBUG("Heartbeat task-id={}", heartbeat_task_id_); auto stats_functor_ = [client_instance_weak_ptr]() { auto client_instance = client_instance_weak_ptr.lock(); @@ -158,10 +137,6 @@ void ClientManagerImpl::shutdown() { callback_thread_pool_->shutdown(); - if (health_check_task_id_) { - scheduler_->cancel(health_check_task_id_); - } - if (heartbeat_task_id_) { scheduler_->cancel(heartbeat_task_id_); } @@ -175,17 +150,11 @@ void ClientManagerImpl::shutdown() { { absl::MutexLock lk(&rpc_clients_mtx_); rpc_clients_.clear(); - SPDLOG_DEBUG("CompletionQueue of active clients stopped"); + SPDLOG_DEBUG("rpc_clients_ is clear"); } - completion_queue_->Shutdown(); - if (completion_queue_thread_.joinable()) { - completion_queue_thread_.join(); - } - SPDLOG_DEBUG("Completion queue thread completes OK"); - state_.store(State::STOPPED, std::memory_order_relaxed); - SPDLOG_DEBUG("Client instance stopped"); + SPDLOG_DEBUG("ClientManager stopped"); } void ClientManagerImpl::assignLabels(Histogram& histogram) { @@ -202,102 +171,6 @@ void ClientManagerImpl::assignLabels(Histogram& histogram) { histogram.labels().emplace_back("[200ms~inf): "); } -void ClientManagerImpl::healthCheck( - const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) { - std::error_code ec; - auto client = getRpcClient(target_host); - if (!client) { - ec = ErrorCode::RequestTimeout; - cb(ec, nullptr); - return; - } - - SPDLOG_DEBUG("Prepare to send health-check to {}. Request: {}", target_host, request.DebugString()); - - auto invocation_context = new InvocationContext(); - invocation_context->task_name = fmt::format("HealthCheck to {}", target_host); - invocation_context->remote_address = target_host; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - - for (const auto& entry : metadata) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - - auto callback = [cb](const InvocationContext* ctx) { - std::error_code ec; - if (!ctx->status.ok()) { - ec = ErrorCode::RequestTimeout; - cb(ec, ctx); - return; - } - - const auto& common = ctx->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - cb(ec, ctx); - } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); - ec = ErrorCode::Unauthorized; - cb(ec, ctx); - } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); - ec = ErrorCode::Forbidden; - cb(ec, ctx); - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); - ec = ErrorCode::InternalServerError; - cb(ec, ctx); - } break; - default: { - SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release"); - ec = ErrorCode::NotImplemented; - cb(ec, ctx); - } break; - } - }; - - invocation_context->callback = callback; - client->asyncHealthCheck(request, invocation_context); -} - -void ClientManagerImpl::doHealthCheck() { - SPDLOG_DEBUG("Start to perform health check for inactive clients"); - if (State::STARTED != state_.load(std::memory_order_relaxed) && - State::STARTING != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); - return; - } - - auto&& rpc_clients_removed = cleanOfflineRpcClients(); - - std::vector> clients; - { - absl::MutexLock lk(&clients_mtx_); - for (auto& item : clients_) { - auto client = item.lock(); - if (client && client->active()) { - clients.emplace_back(std::move(client)); - } - } - } - - if (!rpc_clients_removed.empty()) { - for (auto& client : clients) { - client->onRemoteEndpointRemoval(rpc_clients_removed); - } - } - - for (auto& client : clients) { - client->healthCheck(); - } - SPDLOG_DEBUG("Health check completed"); -} - std::vector ClientManagerImpl::cleanOfflineRpcClients() { absl::flat_hash_set hosts; { @@ -351,34 +224,30 @@ void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata return; } - const auto& common = invocation_context->response.common(); + auto&& status = invocation_context->response.status(); std::error_code ec; - switch (common.status().code()) { - case google::rpc::Code::OK: { + switch (status.code()) { + case rmq::Code::OK: { cb(ec, invocation_context->response); } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; cb(ec, invocation_context->response); } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; cb(ec, invocation_context->response); } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); - ec = ErrorCode::BadRequest; - cb(ec, invocation_context->response); - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; cb(ec, invocation_context->response); } break; default: { - SPDLOG_WARN("NotImplemented: Please upgrade SDK to latest release"); + SPDLOG_WARN("NotImplemented: Please upgrade SDK to latest release. Message={}, host={}", status.message(), + invocation_context->remote_address); } break; } }; @@ -391,60 +260,35 @@ void ClientManagerImpl::heartbeat(const std::string& target_host, const Metadata void ClientManagerImpl::doHeartbeat() { if (State::STARTED != state_.load(std::memory_order_relaxed) && State::STARTING != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected client instance state={}.", state_.load(std::memory_order_relaxed)); + SPDLOG_WARN("Unexpected client manager state={}.", state_.load(std::memory_order_relaxed)); return; } - std::vector> clients; { absl::MutexLock lk(&clients_mtx_); for (const auto& item : clients_) { auto client = item.lock(); if (client && client->active()) { - clients.emplace_back(std::move(client)); - } - } - } - - for (auto& client : clients) { - client->heartbeat(); - } -} - -void ClientManagerImpl::pollCompletionQueue() { - while (State::STARTED == state_.load(std::memory_order_relaxed) || - State::STARTING == state_.load(std::memory_order_relaxed)) { - bool ok = false; - void* opaque_invocation_context; - while (completion_queue_->Next(&opaque_invocation_context, &ok)) { - auto invocation_context = static_cast(opaque_invocation_context); - if (!ok) { - // the call is dead - SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); + client->heartbeat(); } - auto callback = [invocation_context, ok]() { invocation_context->onCompletion(ok); }; - callback_thread_pool_->submit(callback); } - SPDLOG_INFO("CompletionQueue is fully drained and shut down"); } - SPDLOG_INFO("pollCompletionQueue completed and quit"); } bool ClientManagerImpl::send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) { + SendCallback cb) { assert(cb); SPDLOG_DEBUG("Prepare to send message to {} asynchronously", target_host); RpcClientSharedPtr client = getRpcClient(target_host); // Invocation context will be deleted in its onComplete() method. auto invocation_context = new InvocationContext(); - invocation_context->task_name = - fmt::format("Send message[] to {}", request.message().system_attribute().message_id(), target_host); + invocation_context->task_name = fmt::format("Send message to {}", target_host); invocation_context->remote_address = target_host; for (const auto& entry : metadata) { invocation_context->context.AddMetadata(entry.first, entry.second); } - const std::string& topic = request.message().topic().name(); + const std::string& topic = request.messages().begin()->topic().name(); std::weak_ptr client_manager(shared_from_this()); auto completion_callback = [topic, cb, client_manager](const InvocationContext* invocation_context) { @@ -458,54 +302,51 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met return; } - const auto& common = invocation_context->response.common(); - + SendReceipt send_receipt; + std::error_code ec; if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to send message to {} due to gRPC error. gRPC code: {}, gRPC error message: {}", invocation_context->remote_address, invocation_context->status.error_code(), invocation_context->status.error_message()); - std::error_code ec = ErrorCode::RequestTimeout; - cb->onFailure(ec); + ec = ErrorCode::RequestTimeout; + cb(ec, send_receipt); return; } - if (invocation_context->status.ok()) { - switch (invocation_context->response.common().status().code()) { - case google::rpc::Code::OK: { - SendResult send_result; - send_result.setSendStatus(SendStatus::SEND_OK); - send_result.setMsgId(invocation_context->response.message_id()); - send_result.setTransactionId(invocation_context->response.transaction_id()); - cb->onSuccess(send_result); - } break; - - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}", common.status().message()); - std::error_code ec = ErrorCode::BadRequest; - cb->onFailure(ec); - } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}", common.status().message()); - std::error_code ec = ErrorCode::Unauthorized; - cb->onFailure(ec); - } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}", common.status().message()); - std::error_code ec = ErrorCode::Forbidden; - cb->onFailure(ec); - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}", common.status().message()); - std::error_code ec = ErrorCode::InternalServerError; - cb->onFailure(ec); - } break; - default: { - SPDLOG_WARN("Unsupported status code. Check and upgrade SDK to the latest"); - std::error_code ec = ErrorCode::NotImplemented; - cb->onFailure(ec); - } break; + auto&& status = invocation_context->response.status(); + switch (invocation_context->response.status().code()) { + case rmq::Code::OK: { + send_receipt.message_id = invocation_context->response.receipts().begin()->message_id(); + break; + } + case rmq::Code::TOPIC_NOT_FOUND: { + SPDLOG_WARN("TopicNotFound: {}", status.message()); + ec = ErrorCode::NotFound; + break; + } + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthenticated: {}", status.message()); + ec = ErrorCode::Unauthorized; + break; + } + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}", status.message()); + ec = ErrorCode::Forbidden; + cb(ec, send_receipt); + break; + } + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}", status.message()); + ec = ErrorCode::InternalServerError; + break; + } + default: { + SPDLOG_WARN("Unsupported status code. Check and upgrade SDK to the latest"); + ec = ErrorCode::NotImplemented; + break; } } + cb(ec, send_receipt); }; invocation_context->callback = completion_callback; @@ -542,7 +383,8 @@ RpcClientSharedPtr ClientManagerImpl::getRpcClient(const std::string& target_hos interceptor_factories.emplace_back(absl::make_unique()); auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( target_host, channel_credential_, channel_arguments_, std::move(interceptor_factories)); - client = std::make_shared(completion_queue_, channel, need_heartbeat); + std::weak_ptr client_manager(shared_from_this()); + client = std::make_shared(client_manager, channel, target_host, need_heartbeat); rpc_clients_.insert_or_assign(target_host, client); } else { client = search->second; @@ -568,18 +410,27 @@ void ClientManagerImpl::cleanRpcClients() { rpc_clients_.clear(); } -SendResult ClientManagerImpl::processSendResponse(const MQMessageQueue& message_queue, - const SendMessageResponse& response) { - if (google::rpc::Code::OK != response.common().status().code()) { - THROW_MQ_EXCEPTION(MQClientException, response.common().DebugString(), response.common().status().code()); +SendReceipt ClientManagerImpl::processSendResponse(const rmq::MessageQueue& message_queue, + const SendMessageResponse& response, std::error_code& ec) { + SendReceipt send_receipt; + + switch (response.status().code()) { + case rmq::Code::OK: { + assert(response.receipts_size() > 0); + send_receipt.message_id = response.receipts().begin()->message_id(); + send_receipt.transaction_id = response.receipts().begin()->transaction_id(); + return send_receipt; + } + case rmq::Code::ILLEGAL_TOPIC: { + ec = ErrorCode::BadRequest; + return send_receipt; + } + default: { + // TODO: handle other cases. + break; + } } - SendResult send_result; - send_result.setSendStatus(SendStatus::SEND_OK); - send_result.setMsgId(response.message_id()); - send_result.setQueueOffset(-1); - send_result.setMessageQueue(message_queue); - send_result.setTransactionId(response.transaction_id()); - return send_result; + return send_receipt; } void ClientManagerImpl::addClientObserver(std::weak_ptr client) { @@ -618,81 +469,33 @@ void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metad } std::error_code ec; - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - auto& partitions = invocation_context->response.partitions(); - std::vector topic_partitions; - for (const auto& partition : partitions) { - Topic t(partition.topic().resource_namespace(), partition.topic().name()); - - auto& broker = partition.broker(); - AddressScheme scheme = AddressScheme::IPv4; - switch (broker.endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - scheme = AddressScheme::IPv4; - break; - case rmq::AddressScheme::IPv6: - scheme = AddressScheme::IPv6; - break; - case rmq::AddressScheme::DOMAIN_NAME: - scheme = AddressScheme::DOMAIN_NAME; - break; - default: - break; - } - - std::vector
addresses; - for (const auto& address : broker.endpoints().addresses()) { - addresses.emplace_back(Address{address.host(), address.port()}); - } - ServiceAddress service_address(scheme, addresses); - Broker b(partition.broker().name(), partition.broker().id(), service_address); - - Permission permission = Permission::READ_WRITE; - switch (partition.permission()) { - case rmq::Permission::READ: - permission = Permission::READ; - break; - - case rmq::Permission::WRITE: - permission = Permission::WRITE; - break; - case rmq::Permission::READ_WRITE: - permission = Permission::READ_WRITE; - break; - default: - break; - } - Partition topic_partition(t, partition.id(), permission, std::move(b)); - topic_partitions.emplace_back(std::move(topic_partition)); + auto&& status = invocation_context->response.status(); + switch (status.code()) { + case rmq::Code::OK: { + std::vector message_queues; + for (const auto& item : invocation_context->response.message_queues()) { + message_queues.push_back(item); } - auto ptr = - std::make_shared(std::move(topic_partitions), invocation_context->response.DebugString()); + auto ptr = std::make_shared(std::move(message_queues)); cb(ec, ptr); } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}. Host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; cb(ec, nullptr); } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}. Host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; cb(ec, nullptr); } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::BadRequest; - cb(ec, nullptr); - } break; - case google::rpc::Code::NOT_FOUND: { - SPDLOG_WARN("NotFound: {}. Host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::TOPIC_NOT_FOUND: { + SPDLOG_WARN("TopicNotFound: {}. Host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::NotFound; cb(ec, nullptr); } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}. Host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; cb(ec, nullptr); } break; @@ -722,26 +525,22 @@ void ClientManagerImpl::queryAssignment( return; } - const auto& common = invocation_context->response.common(); + auto&& status = invocation_context->response.status(); std::error_code ec; - switch (common.status().code()) { - case google::rpc::Code::OK: { + switch (status.code()) { + case rmq::Code::OK: { SPDLOG_DEBUG("Query assignment OK"); } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::BadRequest; - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; } break; default: { @@ -766,124 +565,53 @@ void ClientManagerImpl::queryAssignment( void ClientManagerImpl::receiveMessage(const std::string& target_host, const Metadata& metadata, const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - const std::shared_ptr& cb) { + ReceiveMessageCallback cb) { SPDLOG_DEBUG("Prepare to receive message from {} asynchronously. Request: {}", target_host, request.DebugString()); RpcClientSharedPtr client = getRpcClient(target_host); - - auto invocation_context = new InvocationContext(); - invocation_context->task_name = fmt::format("ReceiveMessage from queue[{}-{}-{}-{}], host={}", request.group().name(), - request.partition().topic().name(), request.partition().broker().name(), - request.partition().id(), target_host); - invocation_context->remote_address = target_host; - if (!metadata.empty()) { - for (const auto& item : metadata) { - invocation_context->context.AddMetadata(item.first, item.second); - } - } - invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - - auto callback = [this, cb](const InvocationContext* invocation_context) { - std::error_code ec; - ReceiveMessageResult result; - - // Handle network error. - if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to pop messages through gRPC from {}, gRPC code: {}, gRPC error message: {}", - invocation_context->remote_address, invocation_context->status.error_code(), - invocation_context->status.error_message()); - ec = ErrorCode::RequestTimeout; - cb->onCompletion(ec, result); - return; - } - - // Handle application layer logic - result.source_host = invocation_context->remote_address; - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - SPDLOG_TRACE("ReceivedMessage Resonse: {}, host={}", invocation_context->response.DebugString(), - invocation_context->remote_address); - for (auto& item : invocation_context->response.messages()) { - MQMessageExt message_ext; - MessageAccessor::setTargetEndpoint(message_ext, invocation_context->remote_address); - if (wrapMessage(item, message_ext)) { - result.messages.emplace_back(message_ext); - } else { - SPDLOG_WARN("A message fails to pass body checksum validation. Skip processing it."); - } - } - break; - } - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Unauthorized; - } break; - - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Forbidden; - } break; - - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::BadRequest; - } break; - - case google::rpc::Code::DEADLINE_EXCEEDED: { - SPDLOG_WARN("DeadlineExceeded: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::GatewayTimeout; - } break; - - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("IntervalServerError: {}. Host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::InternalServerError; - } break; - default: { - SPDLOG_WARN("Unsupported code. Please upgrade to use the latest release. Host={}", - invocation_context->remote_address); - ec = ErrorCode::NotImplemented; - } break; - } - cb->onCompletion(ec, result); - }; - invocation_context->callback = callback; - client->asyncReceive(request, invocation_context); + auto context = absl::make_unique(); + context->callback = std::move(cb); + context->metadata = metadata; + context->timeout = timeout; + client->asyncReceive(request, std::move(context)); } State ClientManagerImpl::state() const { return state_.load(std::memory_order_relaxed); } -bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) { +MessageConstSharedPtr ClientManagerImpl::wrapMessage(const rmq::Message& item) { assert(item.topic().resource_namespace() == resource_namespace_); + auto builder = Message::newBuilder(); // base - message_ext.setTopic(item.topic().name()); - - const auto& system_attributes = item.system_attribute(); + builder.withTopic(item.topic().name()); - // Receipt-handle - MessageAccessor::setReceiptHandle(message_ext, system_attributes.receipt_handle()); + const auto& system_properties = item.system_properties(); // Tag - message_ext.setTags(system_attributes.tag()); + if (system_properties.has_tag()) { + builder.withTag(system_properties.tag()); + } // Keys std::vector keys; - for (const auto& key : system_attributes.keys()) { + for (const auto& key : system_properties.keys()) { keys.push_back(key); } - message_ext.setKeys(keys); + if (!keys.empty()) { + builder.withKeys(std::move(keys)); + } // Message-Id - MessageAccessor::setMessageId(message_ext, system_attributes.message_id()); + const auto& message_id = system_properties.message_id(); + builder.withId(message_id); // Validate body digest - const rmq::Digest& digest = system_attributes.body_digest(); + const rmq::Digest& digest = system_properties.body_digest(); bool body_digest_match = false; if (item.body().empty()) { SPDLOG_WARN("Body of message[topic={}, msgId={}] is empty", item.topic().name(), - item.system_attribute().message_id()); + item.system_properties().message_id()); body_digest_match = true; } else { switch (digest.type()) { @@ -908,10 +636,10 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess if (success) { body_digest_match = (digest.checksum() == checksum); if (body_digest_match) { - SPDLOG_DEBUG("Body of message[{}] MD5 checksum validation passed.", message_ext.getMsgId()); + SPDLOG_DEBUG("Body of message[{}] MD5 checksum validation passed.", message_id); } else { - SPDLOG_WARN("Body of message[{}] MD5 checksum validation failed. Expect: {}, Actual: {}", - message_ext.getMsgId(), digest.checksum(), checksum); + SPDLOG_WARN("Body of message[{}] MD5 checksum validation failed. Expect: {}, Actual: {}", message_id, + digest.checksum(), checksum); } } else { SPDLOG_WARN("Failed to calculate MD5 digest. Skip."); @@ -925,13 +653,13 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess if (success) { body_digest_match = (checksum == digest.checksum()); if (body_digest_match) { - SPDLOG_DEBUG("Body of message[{}] SHA1 checksum validation passed", message_ext.getMsgId()); + SPDLOG_DEBUG("Body of message[{}] SHA1 checksum validation passed", message_id); } else { - SPDLOG_WARN("Body of message[{}] SHA1 checksum validation failed. Expect: {}, Actual: {}", - message_ext.getMsgId(), digest.checksum(), checksum); + SPDLOG_WARN("Body of message[{}] SHA1 checksum validation failed. Expect: {}, Actual: {}", message_id, + digest.checksum(), checksum); } } else { - SPDLOG_WARN("Failed to calculate SHA1 digest for message[{}]. Skip.", message_ext.getMsgId()); + SPDLOG_WARN("Failed to calculate SHA1 digest for message[{}]. Skip.", message_id); } break; } @@ -944,21 +672,21 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess } if (!body_digest_match) { - SPDLOG_WARN("Message body checksum failed. MsgId={}", system_attributes.message_id()); + SPDLOG_WARN("Message body checksum failed. MsgId={}", system_properties.message_id()); // TODO: NACK it immediately - return false; + return nullptr; } // Body encoding - switch (system_attributes.body_encoding()) { + switch (system_properties.body_encoding()) { case rmq::Encoding::GZIP: { std::string uncompressed; UtilAll::uncompress(item.body(), uncompressed); - message_ext.setBody(uncompressed); + builder.withBody(uncompressed); break; } case rmq::Encoding::IDENTITY: { - message_ext.setBody(item.body()); + builder.withBody(item.body()); break; } default: { @@ -967,106 +695,82 @@ bool ClientManagerImpl::wrapMessage(const rmq::Message& item, MQMessageExt& mess } } - timeval tv{}; - - // Message-type - MessageType message_type; - switch (system_attributes.message_type()) { - case rmq::MessageType::NORMAL: - message_type = MessageType::NORMAL; - break; - case rmq::MessageType::FIFO: - message_type = MessageType::FIFO; - break; - case rmq::MessageType::DELAY: - message_type = MessageType::DELAY; - break; - case rmq::MessageType::TRANSACTION: - message_type = MessageType::TRANSACTION; - break; - default: - SPDLOG_WARN("Unknown message type. Treat it as normal message"); - message_type = MessageType::NORMAL; - break; + // User-properties + std::unordered_map properties; + for (const auto& it : item.user_properties()) { + properties.insert(std::make_pair(it.first, it.second)); + } + if (!properties.empty()) { + builder.withProperties(properties); } - MessageAccessor::setMessageType(message_ext, message_type); // Born-timestamp - if (system_attributes.has_born_timestamp()) { - tv.tv_sec = system_attributes.born_timestamp().seconds(); - tv.tv_usec = system_attributes.born_timestamp().nanos() / 1000; - auto born_timestamp = absl::TimeFromTimeval(tv); - MessageAccessor::setBornTimestamp(message_ext, born_timestamp); + if (system_properties.has_born_timestamp()) { + auto born_timestamp = google::protobuf::util::TimeUtil::TimestampToMilliseconds(system_properties.born_timestamp()); + builder.withBornTime(absl::ToChronoTime(absl::FromUnixMillis(born_timestamp))); } // Born-host - MessageAccessor::setBornHost(message_ext, system_attributes.born_host()); + builder.withBornHost(system_properties.born_host()); + + // Trace-context + if (system_properties.has_trace_context()) { + builder.withTraceContext(system_properties.trace_context()); + } + + auto message = builder.build(); + + const Message* raw = message.release(); + Message* msg = const_cast(raw); + Extension& extension = msg->mutableExtension(); + + // Receipt-handle + extension.receipt_handle = system_properties.receipt_handle(); // Store-timestamp - if (system_attributes.has_store_timestamp()) { - tv.tv_sec = system_attributes.store_timestamp().seconds(); - tv.tv_usec = system_attributes.store_timestamp().nanos() / 1000; - MessageAccessor::setStoreTimestamp(message_ext, absl::TimeFromTimeval(tv)); + if (system_properties.has_store_timestamp()) { + auto store_timestamp = + google::protobuf::util::TimeUtil::TimestampToMilliseconds(system_properties.store_timestamp()); + extension.store_time = absl::ToChronoTime(absl::FromUnixMillis(store_timestamp)); } // Store-host - MessageAccessor::setStoreHost(message_ext, system_attributes.store_host()); + extension.store_host = system_properties.store_host(); // Process one-of: delivery-timestamp and delay-level. - switch (system_attributes.timed_delivery_case()) { - case rmq::SystemAttribute::TimedDeliveryCase::kDelayLevel: { - message_ext.setDelayTimeLevel(system_attributes.delay_level()); - break; - } - - case rmq::SystemAttribute::TimedDeliveryCase::kDeliveryTimestamp: { - tv.tv_sec = system_attributes.delivery_timestamp().seconds(); - tv.tv_usec = system_attributes.delivery_timestamp().nanos(); - MessageAccessor::setDeliveryTimestamp(message_ext, absl::TimeFromTimeval(tv)); - break; - } - - default: - break; + if (system_properties.has_delivery_timestamp()) { + auto delivery_timestamp_ms = + google::protobuf::util::TimeUtil::TimestampToMilliseconds(system_properties.delivery_timestamp()); + extension.delivery_timepoint = absl::ToChronoTime(absl::FromUnixMillis(delivery_timestamp_ms)); } - // Partition-id - MessageAccessor::setQueueId(message_ext, system_attributes.partition_id()); + // Queue-id + extension.queue_id = system_properties.queue_id(); - // Partition-offset - MessageAccessor::setQueueOffset(message_ext, system_attributes.partition_offset()); + // Queue-offset + extension.offset = system_properties.queue_offset(); // Invisible-period - if (system_attributes.has_invisible_period()) { - absl::Duration invisible_period = absl::Seconds(system_attributes.invisible_period().seconds()) + - absl::Nanoseconds(system_attributes.invisible_period().nanos()); - MessageAccessor::setInvisiblePeriod(message_ext, invisible_period); + if (system_properties.has_invisible_duration()) { + auto invisible_period = std::chrono::seconds(system_properties.invisible_duration().seconds()) + + std::chrono::nanoseconds(system_properties.invisible_duration().nanos()); + extension.invisible_period = invisible_period; } // Delivery attempt - MessageAccessor::setDeliveryAttempt(message_ext, system_attributes.delivery_attempt()); - - // Trace-context - MessageAccessor::setTraceContext(message_ext, system_attributes.trace_context()); + extension.delivery_attempt = system_properties.delivery_attempt(); // Decoded Time-Point - MessageAccessor::setDecodedTimestamp(message_ext, absl::Now()); - - // User-properties - std::map properties; - for (const auto& it : item.user_attribute()) { - properties.insert(std::make_pair(it.first, it.second)); - } - message_ext.setProperties(properties); + extension.decode_time = std::chrono::system_clock::now(); // Extension { - auto elapsed = static_cast(absl::ToUnixMillis(absl::Now()) - message_ext.getStoreTimestamp()); + auto elapsed = MixAll::millisecondsOf(std::chrono::system_clock::now() - extension.store_time); if (elapsed >= 0) { latency_histogram_.countIn(elapsed / 20); } } - return true; + return MessageConstSharedPtr(raw); } SchedulerSharedPtr ClientManagerImpl::getScheduler() { @@ -1081,7 +785,7 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, RpcClientSharedPtr client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); - invocation_context->task_name = fmt::format("Ack message[{}] against {}", request.message_id(), target); + invocation_context->task_name = fmt::format("Ack messages against {}", target); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -1098,25 +802,21 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, return; } - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { + auto&& status = invocation_context->response.status(); + switch (status.code()) { + case rmq::Code::OK: { SPDLOG_DEBUG("Ack OK. host={}", invocation_context->remote_address); } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("PermissionDenied: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::BadRequest; - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; } break; default: { @@ -1130,13 +830,14 @@ void ClientManagerImpl::ack(const std::string& target, const Metadata& metadata, client->asyncAck(request, invocation_context); } -void ClientManagerImpl::nack(const std::string& target_host, const Metadata& metadata, - const NackMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& completion_callback) { +void ClientManagerImpl::changeInvisibleDuration( + const std::string& target_host, const Metadata& metadata, const ChangeInvisibleDurationRequest& request, + std::chrono::milliseconds timeout, const std::function& completion_callback) { RpcClientSharedPtr client = getRpcClient(target_host); assert(client); - auto invocation_context = new InvocationContext(); - invocation_context->task_name = fmt::format("Nack Message[{}] against {}", request.message_id(), target_host); + auto invocation_context = new InvocationContext(); + invocation_context->task_name = fmt::format("ChangeInvisibleDuration Message[receipt-handle={}] against {}", + request.receipt_handle(), target_host); invocation_context->remote_address = target_host; invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); @@ -1144,7 +845,7 @@ void ClientManagerImpl::nack(const std::string& target_host, const Metadata& met invocation_context->context.AddMetadata(item.first, item.second); } - auto callback = [completion_callback](const InvocationContext* invocation_context) { + auto callback = [completion_callback](const InvocationContext* invocation_context) { if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to write Nack request to wire. gRPC-code: {}, gRPC-message: {}", invocation_context->status.error_code(), invocation_context->status.error_message()); @@ -1154,24 +855,24 @@ void ClientManagerImpl::nack(const std::string& target_host, const Metadata& met } std::error_code ec; - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { + auto&& status = invocation_context->response.status(); + switch (status.code()) { + case rmq::Code::OK: { SPDLOG_DEBUG("Nack to {} OK", invocation_context->remote_address); break; }; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; break; } - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; break; } - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; break; } @@ -1184,7 +885,7 @@ void ClientManagerImpl::nack(const std::string& target_host, const Metadata& met completion_callback(ec); }; invocation_context->callback = callback; - client->asyncNack(request, invocation_context); + client->asyncChangeInvisibleDuration(request, invocation_context); } void ClientManagerImpl::endTransaction( @@ -1225,26 +926,26 @@ void ClientManagerImpl::endTransaction( return; } - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { + auto&& status = invocation_context->response.status(); + switch (status.code()) { + case rmq::Code::OK: { SPDLOG_DEBUG("endTransaction completed OK. Response: {}, host={}", invocation_context->response.DebugString(), invocation_context->remote_address); } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Unauthorized; } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::Forbidden; } break; - case google::rpc::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::InternalServerError; } break; default: { - SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. {}, host={}", common.status().message(), + SPDLOG_WARN("NotImplemented: please upgrade SDK to latest release. {}, host={}", status.message(), invocation_context->remote_address); ec = ErrorCode::NotImplemented; } @@ -1256,177 +957,6 @@ void ClientManagerImpl::endTransaction( client->asyncEndTransaction(request, invocation_context); } -void ClientManagerImpl::pollCommand(const std::string& target, const Metadata& metadata, - const PollCommandRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - auto client = getRpcClient(target); - - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = target; - for (const auto& item : metadata) { - invocation_context->context.AddMetadata(item.first, item.second); - } - auto deadline = std::chrono::system_clock::now() + timeout; - invocation_context->context.set_deadline(deadline); - - auto callback = [cb](const InvocationContext* invocation_context) { cb(invocation_context); }; - - invocation_context->callback = callback; - client->asyncPollCommand(request, invocation_context); -} - -void ClientManagerImpl::queryOffset(const std::string& target_host, const Metadata& metadata, - const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - auto client = getRpcClient(target_host); - std::error_code ec; - if (!client) { - SPDLOG_WARN("Failed to get/create RPC client for {}", target_host); - ec = ErrorCode::RequestTimeout; - QueryOffsetResponse response; - cb(ec, response); - return; - } - - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = target_host; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - - for (const auto& entry : metadata) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - - auto callback = [cb](const InvocationContext* invocation_context) { - std::error_code ec; - - if (!invocation_context->status.ok()) { - SPDLOG_WARN("Failed to write QueryOffset request to wire. gRPC-code: {}, gRPC-message: {}, host={}", - invocation_context->status.error_code(), invocation_context->status.error_message(), - invocation_context->remote_address); - ec = ErrorCode::RequestTimeout; - cb(ec, invocation_context->response); - return; - } - - const auto& common = invocation_context->response.common(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - SPDLOG_DEBUG("Query offset from server[host={}] OK", invocation_context->remote_address); - cb(ec, invocation_context->response); - } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Unauthorized; - cb(ec, invocation_context->response); - } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Forbidden; - cb(ec, invocation_context->response); - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::InternalServerError; - cb(ec, invocation_context->response); - } break; - default: { - SPDLOG_WARN("NotImplemented: please upgrade SDK to the latest release. host={}", - invocation_context->remote_address); - ec = ErrorCode::NotImplemented; - cb(ec, invocation_context->response); - } - } - }; - invocation_context->callback = callback; - client->asyncQueryOffset(request, invocation_context); -} - -void ClientManagerImpl::pullMessage( - const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - SPDLOG_DEBUG("PullMessage Request: {}, target_host={}", request.DebugString(), target_host); - auto client = getRpcClient(target_host); - auto invocation_context = new InvocationContext(); - invocation_context->task_name = fmt::format("PullMessage for queue[{}-{}-{}-{}] from {}", request.group().name(), - request.partition().topic().name(), request.partition().broker().name(), - request.partition().id(), target_host); - invocation_context->remote_address = target_host; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + timeout); - for (const auto& item : metadata) { - invocation_context->context.AddMetadata(item.first, item.second); - } - - auto callback = [cb, this](const InvocationContext* invocation_context) { - std::error_code ec; - ReceiveMessageResult result; - result.source_host = invocation_context->remote_address; - // Handle network issue. - if (!invocation_context->status.ok()) { - ec = ErrorCode::RequestTimeout; - cb(ec, result); - return; - } - - // Handle application layer logic: map status::code to corresponding error_code. - const auto& common = invocation_context->response.common(); - result.min_offset = invocation_context->response.min_offset(); - result.next_offset = invocation_context->response.next_offset(); - result.max_offset = invocation_context->response.max_offset(); - switch (common.status().code()) { - case google::rpc::Code::OK: { - SPDLOG_TRACE("Received PullMessage Response: {}, host={}", invocation_context->response.DebugString(), - invocation_context->remote_address); - for (const auto& item : invocation_context->response.messages()) { - MQMessageExt message; - if (!wrapMessage(item, message)) { - return; - } - result.messages.emplace_back(message); - } - } break; - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Forbidden; - } break; - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::Unauthorized; - } break; - case google::rpc::Code::NOT_FOUND: { - SPDLOG_WARN("NotFound: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::NotFound; - break; - } - case google::rpc::Code::DEADLINE_EXCEEDED: { - SPDLOG_WARN("DeadlineExceeded: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::GatewayTimeout; - } break; - case google::rpc::Code::INVALID_ARGUMENT: { - SPDLOG_WARN("InvalidArgument: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::BadRequest; - } break; - case google::rpc::Code::FAILED_PRECONDITION: { - SPDLOG_WARN("FailedPrecondition: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::PreconditionRequired; - } break; - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: {}, host={}", common.status().message(), invocation_context->remote_address); - ec = ErrorCode::InternalServerError; - } break; - default: { - SPDLOG_WARN("Unimplemented: Please upgrade to use latest SDK release, host={}", - invocation_context->remote_address); - ec = ErrorCode::NotImplemented; - } break; - } - cb(ec, result); - }; - - invocation_context->callback = callback; - client->asyncPull(request, invocation_context); -} - void ClientManagerImpl::forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, @@ -1458,93 +988,6 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); } -std::error_code ClientManagerImpl::reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, - const ReportThreadStackTraceRequest& request, - std::chrono::milliseconds timeout) { - std::error_code ec; - auto client = getRpcClient(target_host); - grpc::ClientContext context; - auto deadline = std::chrono::system_clock::now() + timeout; - context.set_deadline(deadline); - - for (const auto& item : metadata) { - context.AddMetadata(item.first, item.second); - } - - ReportThreadStackTraceResponse response; - auto status = client->reportThreadStackTrace(&context, request, &response); - if (!status.ok()) { - ec = ErrorCode::RequestTimeout; - SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); - return ec; - } - - switch (response.common().status().code()) { - case google::rpc::Code::OK: { - return ec; - } - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); - ec = ErrorCode::Unauthorized; - break; - } - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); - ec = ErrorCode::Forbidden; - break; - } - default: { - ec = ErrorCode::NotImplemented; - SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); - } - } - return ec; -} - -std::error_code ClientManagerImpl::reportMessageConsumptionResult(const std::string& target_host, - const Metadata& metadata, - const ReportMessageConsumptionResultRequest& request, - std::chrono::milliseconds timeout) { - std::error_code ec; - auto client = getRpcClient(target_host); - grpc::ClientContext context; - auto deadline = std::chrono::system_clock::now() + timeout; - context.set_deadline(deadline); - - for (const auto& item : metadata) { - context.AddMetadata(item.first, item.second); - } - - ReportMessageConsumptionResultResponse response; - auto status = client->reportMessageConsumptionResult(&context, request, &response); - if (!status.ok()) { - ec = ErrorCode::RequestTimeout; - SPDLOG_WARN("Failed to report thread-stack-trace to {}. Cause: {}", target_host, status.error_message()); - return ec; - } - - switch (response.common().status().code()) { - case google::rpc::Code::OK: { - return ec; - } - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthorized. Host={}, Cause: {}", target_host, response.common().status().message()); - ec = ErrorCode::Unauthorized; - break; - } - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("Forbidden. Host={}, Cause: {}", target_host, response.common().status().message()); - ec = ErrorCode::Forbidden; - break; - } - default: { - ec = ErrorCode::NotImplemented; - SPDLOG_WARN("Unsupported response code, please update client to latest release. Host={}", target_host); - } - } - return ec; -} - std::error_code ClientManagerImpl::notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) { @@ -1565,33 +1008,37 @@ std::error_code ClientManagerImpl::notifyClientTermination(const std::string& ta SPDLOG_DEBUG("NotifyClientTermination request: {}", request.DebugString()); NotifyClientTerminationResponse response; - grpc::Status status = client->notifyClientTermination(&context, request, &response); - if (!status.ok()) { - SPDLOG_WARN("NotifyClientTermination failed. gRPC-code={}, gRPC-message={}, host={}", status.error_code(), - status.error_message(), target_host); - ec = ErrorCode::RequestTimeout; - return ec; + { + grpc::Status status = client->notifyClientTermination(&context, request, &response); + if (!status.ok()) { + SPDLOG_WARN("NotifyClientTermination failed. gRPC-code={}, gRPC-message={}, host={}", status.error_code(), + status.error_message(), target_host); + ec = ErrorCode::RequestTimeout; + return ec; + } } - const auto& common = response.common(); + auto&& status = response.status(); - switch (common.status().code()) { - case google::rpc::Code::OK: { + switch (status.code()) { + case rmq::Code::OK: { SPDLOG_DEBUG("NotifyClientTermination OK. host={}", target_host); break; } - case google::rpc::Code::INTERNAL: { - SPDLOG_WARN("InternalServerError: Cause={}, host={}", common.status().message(), target_host); + case rmq::Code::INTERNAL_SERVER_ERROR: { + SPDLOG_WARN("InternalServerError: Cause={}, host={}", status.message(), target_host); ec = ErrorCode::InternalServerError; break; } - case google::rpc::Code::UNAUTHENTICATED: { - SPDLOG_WARN("Unauthenticated: Cause={}, host={}", common.status().message(), target_host); + case rmq::Code::UNAUTHORIZED: { + SPDLOG_WARN("Unauthorized due to lack of valid authentication credentials: Cause={}, host={}", status.message(), + target_host); ec = ErrorCode::Unauthorized; break; } - case google::rpc::Code::PERMISSION_DENIED: { - SPDLOG_WARN("PermissionDenied: Cause={}, host={}", common.status().message(), target_host); + case rmq::Code::FORBIDDEN: { + SPDLOG_WARN("Forbidden due to insufficient permission to the resource: Cause={}, host={}", status.message(), + target_host); ec = ErrorCode::Forbidden; break; } @@ -1611,11 +1058,14 @@ void ClientManagerImpl::logStats() { } void ClientManagerImpl::submit(std::function task) { + State current_state = state(); + if (current_state == State::STOPPING || current_state == State::STOPPED) { + return; + } callback_thread_pool_->submit(task); } const char* ClientManagerImpl::HEARTBEAT_TASK_NAME = "heartbeat-task"; const char* ClientManagerImpl::STATS_TASK_NAME = "stats-task"; -const char* ClientManagerImpl::HEALTH_CHECK_TASK_NAME = "health-check-task"; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/ReceiveMessageStreamReader.cpp b/src/main/cpp/client/ReceiveMessageStreamReader.cpp new file mode 100644 index 000000000..223c8797f --- /dev/null +++ b/src/main/cpp/client/ReceiveMessageStreamReader.cpp @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReceiveMessageStreamReader.h" + +#include "LoggerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ReceiveMessageStreamReader::ReceiveMessageStreamReader(std::weak_ptr client_manager, + rmq::MessagingService::Stub* stub, + std::string peer_address, + rmq::ReceiveMessageRequest request, + std::unique_ptr context) + : client_manager_(client_manager), + stub_(stub), + peer_address_(std::move(peer_address)), + request_(std::move(request)), + context_(std::move(context)) { + for (const auto& entry : context_->metadata) { + client_context_.AddMetadata(entry.first, entry.second); + } + client_context_.set_deadline(std::chrono::system_clock::now() + context_->timeout); + + stub_->async()->ReceiveMessage(&client_context_, &request_, this); + result_.source_host = peer_address_; + StartCall(); + StartRead(&response_); +} + +void ReceiveMessageStreamReader::OnReadDone(bool ok) { + if (ok) { + SPDLOG_DEBUG("ReceiveMessageStreamReader#OnReadDone: ok={}", ok); + } else { + if (result_.messages.empty() && !ec_) { + SPDLOG_WARN("ReceiveMessageStreamReader#OnReadDone: ok={}", ok); + ec_ = ErrorCode::BadGateway; + } else { + SPDLOG_DEBUG("ReceiveMessageStreamReader#OnReadDone reached end-of-stream"); + } + return; + } + + SPDLOG_DEBUG("ReceiveMessageStreamReader#OnReadDone: response={}", response_.DebugString()); + switch (response_.content_case()) { + case rmq::ReceiveMessageResponse::ContentCase::kStatus: { + SPDLOG_DEBUG("ReceiveMessageResponse.status.message={}", response_.status().message()); + switch (response_.status().code()) { + case rmq::Code::OK: { + break; + } + case rmq::Code::TOPIC_NOT_FOUND: { + ec_ = ErrorCode::TopicNotFound; + break; + } + + case rmq::Code::CONSUMER_GROUP_NOT_FOUND: { + ec_ = ErrorCode::GroupNotFound; + break; + } + case rmq::Code::TOO_MANY_REQUESTS: { + ec_ = ErrorCode::TooManyRequest; + break; + } + case rmq::Code::MESSAGE_NOT_FOUND: { + ec_ = ErrorCode::NoContent; + break; + } + default: + SPDLOG_WARN("Unsupported code={}", response_.status().code()); + break; + } + break; + } + case rmq::ReceiveMessageResponse::ContentCase::kMessage: { + auto client_manager = client_manager_.lock(); + auto message = client_manager->wrapMessage(response_.message()); + auto raw = const_cast(message.get()); + raw->mutableExtension().target_endpoint = peer_address_; + if (message) { + result_.messages.push_back(message); + } + break; + } + default: + break; + } + + StartRead(&response_); +} + +void ReceiveMessageStreamReader::OnDone(const grpc::Status& s) { + if (!s.ok()) { + SPDLOG_WARN("ReceiveMessageStreamReader#OnDone: status.ok={}, status.error_message={}", s.ok(), s.error_message()); + } else { + SPDLOG_DEBUG("ReceiveMessageStreamReader#OnDone: status.ok={}", s.ok()); + } + + status_ = s; + context_->callback(ec_, result_); + delete this; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/RpcClientImpl.cpp b/src/main/cpp/client/RpcClientImpl.cpp index 5d546b00e..623547e6b 100644 --- a/src/main/cpp/client/RpcClientImpl.cpp +++ b/src/main/cpp/client/RpcClientImpl.cpp @@ -17,103 +17,115 @@ #include "RpcClientImpl.h" #include +#include +#include +#include -#include "ClientConfigImpl.h" -#include "TlsHelper.h" #include "absl/time/time.h" -using ClientContext = grpc::ClientContext; +#include "ClientManager.h" +#include "ReceiveMessageStreamReader.h" +#include "RpcClient.h" +#include "TelemetryBidiReactor.h" +#include "TlsHelper.h" +#include "include/ReceiveMessageContext.h" ROCKETMQ_NAMESPACE_BEGIN +using ClientContext = grpc::ClientContext; + +void RpcClientImpl::asyncCallback(std::weak_ptr client, BaseInvocationContext* invocation_context, + grpc::Status status) { + + invocation_context->status = std::move(status); + std::shared_ptr stub = client.lock(); + if (!stub) { + SPDLOG_WARN("RpcClient has destructed. Response Ignored"); + // TODO: execute orphan callback in event-loop thread? + // invocation_context->onCompletion(false); + // or + delete invocation_context; + return; + } + + std::weak_ptr client_manager = stub->clientManager(); + std::shared_ptr manager = client_manager.lock(); + if (!manager) { + SPDLOG_WARN("ClientManager has destructed. Response ignored"); + // TODO: execute orphan callback in event-loop thread? + // invocation_context->onCompletion(false); + // or + delete invocation_context; + } + + auto task = [invocation_context, client] { + auto ptr = client.lock(); + if (!ptr) { + // RPC client should have destructed. + return; + } + invocation_context->onCompletion(invocation_context->status.ok()); + }; + + // Execute business post-processing in callback thread pool. + manager->submit(task); +} + void RpcClientImpl::asyncQueryRoute(const QueryRouteRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncQueryRoute(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->QueryRoute(&invocation_context->context, &request, &invocation_context->response, callback); } void RpcClientImpl::asyncSend(const SendMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncSendMessage(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->SendMessage(&invocation_context->context, &request, &invocation_context->response, callback); } void RpcClientImpl::asyncQueryAssignment(const QueryAssignmentRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncQueryAssignment(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - -std::shared_ptr& rocketmq::RpcClientImpl::completionQueue() { - return completion_queue_; + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->QueryAssignment(&invocation_context->context, &request, &invocation_context->response, callback); } -void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, - InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncReceiveMessage(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); +void RpcClientImpl::asyncReceive(const ReceiveMessageRequest& request, std::unique_ptr context) { + new ReceiveMessageStreamReader(client_manager_, stub_.get(), peer_address_, request, std::move(context)); } void RpcClientImpl::asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncAckMessage(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->AckMessage(&invocation_context->context, &request, &invocation_context->response, callback); } -void RpcClientImpl::asyncNack(const NackMessageRequest& request, - InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncNackMessage(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); +void RpcClientImpl::asyncChangeInvisibleDuration( + const ChangeInvisibleDurationRequest& request, + InvocationContext* invocation_context) { + + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + + stub_->async()->ChangeInvisibleDuration(&invocation_context->context, &request, &invocation_context->response, + callback); } void RpcClientImpl::asyncHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncHeartbeat(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - -void RpcClientImpl::asyncHealthCheck(const HealthCheckRequest& request, - InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncHealthCheck(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->Heartbeat(&invocation_context->context, &request, &invocation_context->response, callback); } void RpcClientImpl::asyncEndTransaction(const EndTransactionRequest& request, InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncEndTransaction(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->EndTransaction(&invocation_context->context, &request, &invocation_context->response, callback); } bool RpcClientImpl::ok() const { @@ -135,25 +147,8 @@ void RpcClientImpl::needHeartbeat(bool need_heartbeat) { need_heartbeat_ = need_heartbeat; } -void RpcClientImpl::asyncPollCommand(const PollCommandRequest& request, - InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncPollCommand(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - -grpc::Status RpcClientImpl::reportThreadStackTrace(grpc::ClientContext* context, - const ReportThreadStackTraceRequest& request, - ReportThreadStackTraceResponse* response) { - return stub_->ReportThreadStackTrace(context, request, response); -} - -grpc::Status RpcClientImpl::reportMessageConsumptionResult(grpc::ClientContext* context, - const ReportMessageConsumptionResultRequest& request, - ReportMessageConsumptionResultResponse* response) { - return stub_->ReportMessageConsumptionResult(context, request, response); +std::shared_ptr RpcClientImpl::asyncTelemetry(std::weak_ptr client) { + return std::make_shared(client, stub_.get(), peer_address_); } grpc::Status RpcClientImpl::notifyClientTermination(grpc::ClientContext* context, @@ -162,33 +157,17 @@ grpc::Status RpcClientImpl::notifyClientTermination(grpc::ClientContext* context return stub_->NotifyClientTermination(context, request, response); } -void RpcClientImpl::asyncQueryOffset(const QueryOffsetRequest& request, - InvocationContext* invocation_context) { - assert(invocation_context); - invocation_context->response_reader = - stub_->PrepareAsyncQueryOffset(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - -void RpcClientImpl::asyncPull(const PullMessageRequest& request, - InvocationContext* invocation_context) { - invocation_context->response_reader = - stub_->PrepareAsyncPullMessage(&invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - void RpcClientImpl::asyncForwardMessageToDeadLetterQueue( const ForwardMessageToDeadLetterQueueRequest& request, InvocationContext* invocation_context) { - invocation_context->response_reader = stub_->PrepareAsyncForwardMessageToDeadLetterQueue( - &invocation_context->context, request, completion_queue_.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); + std::weak_ptr rpc_client(shared_from_this()); + auto callback = std::bind(&RpcClientImpl::asyncCallback, rpc_client, invocation_context, std::placeholders::_1); + stub_->async()->ForwardMessageToDeadLetterQueue(&invocation_context->context, &request, &invocation_context->response, + callback); +} + +std::weak_ptr RpcClientImpl::clientManager() { + return client_manager_; } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/AwaitPullCallback.cpp b/src/main/cpp/client/SessionImpl.cpp similarity index 57% rename from src/main/cpp/rocketmq/AwaitPullCallback.cpp rename to src/main/cpp/client/SessionImpl.cpp index f4a5d99fd..5b7c08569 100644 --- a/src/main/cpp/rocketmq/AwaitPullCallback.cpp +++ b/src/main/cpp/client/SessionImpl.cpp @@ -14,33 +14,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "AwaitPullCallback.h" + +#include "SessionImpl.h" +#include "LoggerImpl.h" ROCKETMQ_NAMESPACE_BEGIN -void AwaitPullCallback::onSuccess(const PullResult& pull_result) noexcept { - absl::MutexLock lk(&mtx_); - completed_ = true; - // TODO: optimize out messages copy here. - pull_result_ = pull_result; - cv_.SignalAll(); +SessionImpl::SessionImpl(std::weak_ptr client, std::shared_ptr rpc_client) : client_(client), rpc_client_(rpc_client) { + telemetry_ = rpc_client->asyncTelemetry(client_); + syncSettings(); +} + +bool SessionImpl::await() { + return telemetry_->await(); } -void AwaitPullCallback::onFailure(const std::error_code& ec) noexcept { - absl::MutexLock lk(&mtx_); - completed_ = true; - ec_ = ec; - cv_.SignalAll(); +void SessionImpl::syncSettings() { + auto ptr = client_.lock(); + + TelemetryCommand command; + command.mutable_settings()->CopyFrom(ptr->clientSettings()); + telemetry_->write(command); } -bool AwaitPullCallback::await() { - { - absl::MutexLock lk(&mtx_); - while (!completed_) { - cv_.Wait(&mtx_); - } - return !hasFailure(); - } +SessionImpl::~SessionImpl() { + SPDLOG_DEBUG("Session for {} destructed", rpc_client_->remoteAddress()); + telemetry_->fireClose(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/Signature.cpp b/src/main/cpp/client/Signature.cpp index 33641582d..e25dbb54b 100644 --- a/src/main/cpp/client/Signature.cpp +++ b/src/main/cpp/client/Signature.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ #include "Signature.h" -#include "ClientConfigImpl.h" +#include "ClientConfig.h" #include "MetadataConstants.h" #include "Protocol.h" #include "TlsHelper.h" @@ -23,29 +23,21 @@ ROCKETMQ_NAMESPACE_BEGIN -void Signature::sign(ClientConfig* client, absl::flat_hash_map& metadata) { - assert(client); +void Signature::sign(const ClientConfig& client, absl::flat_hash_map& metadata) { metadata.insert({MetadataConstants::LANGUAGE_KEY, "CPP"}); // Add common headers - metadata.insert({MetadataConstants::CLIENT_VERSION_KEY, ClientConfigImpl::CLIENT_VERSION}); - metadata.insert({MetadataConstants::PROTOCOL_VERSION_KEY, Protocol::PROTOCOL_VERSION}); - - if (!client->tenantId().empty()) { - metadata.insert({MetadataConstants::TENANT_ID_KEY, client->tenantId()}); - } - - if (!client->resourceNamespace().empty()) { - metadata.insert({MetadataConstants::NAMESPACE_KEY, client->resourceNamespace()}); - } + metadata.insert({MetadataConstants::CLIENT_ID_KEY, client.client_id}); + metadata.insert({MetadataConstants::CLIENT_VERSION_KEY, MetadataConstants::CLIENT_VERSION}); + metadata.insert({MetadataConstants::PROTOCOL_VERSION_KEY, protocolVersion()}); absl::Time now = absl::Now(); absl::TimeZone utc_time_zone = absl::UTCTimeZone(); const std::string request_date_time = absl::FormatTime(MetadataConstants::DATE_TIME_FORMAT, now, utc_time_zone); metadata.insert({MetadataConstants::DATE_TIME_KEY, request_date_time}); - if (client->credentialsProvider()) { - Credentials&& credentials = client->credentialsProvider()->getCredentials(); + if (client.credentials_provider) { + Credentials&& credentials = client.credentials_provider->getCredentials(); if (credentials.accessKey().empty() || credentials.accessSecret().empty()) { SPDLOG_WARN("Access credential is incomplete. Check your access key/secret."); return; @@ -58,9 +50,9 @@ void Signature::sign(ClientConfig* client, absl::flat_hash_mapregion()) + .append(client.region) .append("/") - .append(client->serviceName()) + .append(MetadataConstants::SERVICE_NAME) .append(", ") .append(MetadataConstants::SIGNED_HEADERS_KEY) .append("=") diff --git a/src/main/cpp/client/TelemetryBidiReactor.cpp b/src/main/cpp/client/TelemetryBidiReactor.cpp new file mode 100644 index 000000000..f39cdecfd --- /dev/null +++ b/src/main/cpp/client/TelemetryBidiReactor.cpp @@ -0,0 +1,339 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TelemetryBidiReactor.h" + +#include +#include +#include + +#include "ClientManager.h" +#include "LoggerImpl.h" +#include "MessageExt.h" +#include "Metadata.h" +#include "RpcClient.h" +#include "Signature.h" +#include "google/protobuf/util/time_util.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TelemetryBidiReactor::TelemetryBidiReactor(std::weak_ptr client, + rmq::MessagingService::Stub* stub, + std::string peer_address) + : client_(client), peer_address_(std::move(peer_address)), stream_state_(StreamState::Created) { + auto ptr = client.lock(); + auto deadline = std::chrono::system_clock::now() + std::chrono::hours(1); + context_.set_deadline(deadline); + Metadata metadata; + Signature::sign(ptr->config(), metadata); + for (const auto& entry : metadata) { + context_.AddMetadata(entry.first, entry.second); + } + stub->async()->Telemetry(&context_, this); + StartCall(); +} + +TelemetryBidiReactor::~TelemetryBidiReactor() { + SPDLOG_INFO("Telemetry stream for {} destructed. StreamState={}", peer_address_, stream_state_); +} + +bool TelemetryBidiReactor::await() { + absl::MutexLock lk(&server_setting_received_mtx_); + if (server_setting_received_) { + return true; + } + + server_setting_received_cv_.Wait(&server_setting_received_mtx_); + return server_setting_received_; +} + +void TelemetryBidiReactor::OnWriteDone(bool ok) { + SPDLOG_DEBUG("OnWriteDone: {}", ok); + + { + bool expect = true; + if (!command_inflight_.compare_exchange_strong(expect, false, std::memory_order_relaxed)) { + SPDLOG_WARN("Illegal command-inflight state"); + } + } + + if (!ok) { + SPDLOG_WARN("Failed to write telemetry command {} to {}", write_.DebugString(), peer_address_); + { + absl::MutexLock lk(&stream_state_mtx_); + stream_state_ = StreamState::WriteDone; + } + + fireClose(); + return; + } + + { + absl::MutexLock lk(&stream_state_mtx_); + if (StreamState::Created == stream_state_) { + stream_state_ = StreamState::Active; + fireRead(); + } + } + + fireWrite(); +} + +void TelemetryBidiReactor::OnReadDone(bool ok) { + SPDLOG_DEBUG("OnReadDone: ok={}", ok); + if (!ok) { + SPDLOG_WARN("Failed to read telemetry command from {}", peer_address_); + { + absl::MutexLock lk(&stream_state_mtx_); + stream_state_ = StreamState::ReadDone; + } + fireClose(); + return; + } + + SPDLOG_DEBUG("Read a telemetry command from {}: {}", peer_address_, read_.DebugString()); + auto ptr = client_.lock(); + if (!ptr) { + SPDLOG_INFO("Client for {} has destructed", peer_address_); + return; + } + + switch (read_.command_case()) { + case rmq::TelemetryCommand::kSettings: { + auto settings = read_.settings(); + SPDLOG_INFO("Received settings from {}: {}", peer_address_, settings.DebugString()); + applySettings(settings); + { + absl::MutexLock lk(&server_setting_received_mtx_); + if (!server_setting_received_) { + server_setting_received_ = true; + server_setting_received_cv_.SignalAll(); + } + } + break; + } + case rmq::TelemetryCommand::kRecoverOrphanedTransactionCommand: { + auto client = client_.lock(); + if (!client) { + fireClose(); + return; + } + + auto message = client->manager()->wrapMessage(read_.release_verify_message_command()->message()); + auto raw = const_cast(message.get()); + raw->mutableExtension().target_endpoint = peer_address_; + raw->mutableExtension().transaction_id = read_.recover_orphaned_transaction_command().transaction_id(); + client->recoverOrphanedTransaction(message); + + break; + } + + case rmq::TelemetryCommand::kPrintThreadStackTraceCommand: { + TelemetryCommand response; + response.mutable_thread_stack_trace()->set_nonce(read_.print_thread_stack_trace_command().nonce()); + response.mutable_thread_stack_trace()->set_thread_stack_trace("PrintStackTrace is not supported"); + { + absl::MutexLock lk(&writes_mtx_); + writes_.push_back(response); + } + fireWrite(); + StartRead(&read_); + break; + } + + case rmq::TelemetryCommand::kVerifyMessageCommand: { + auto client = client_.lock(); + if (!client) { + fireClose(); + return; + } + + std::weak_ptr ptr(shared_from_this()); + auto cb = [ptr](TelemetryCommand command) { + auto reactor = ptr.lock(); + if (!reactor) { + return; + } + reactor->onVerifyMessageResult(std::move(command)); + }; + auto message = client->manager()->wrapMessage(read_.verify_message_command().message()); + auto raw = const_cast(message.get()); + raw->mutableExtension().target_endpoint = peer_address_; + raw->mutableExtension().nonce = read_.verify_message_command().nonce(); + client->verify(message, cb); + StartRead(&read_); + break; + } + + default: { + SPDLOG_WARN("Unsupported command"); + break; + } + } +} + +void TelemetryBidiReactor::applySettings(const rmq::Settings& settings) { + auto ptr = client_.lock(); + if (!ptr) { + SPDLOG_INFO("Client for {} has destructed", peer_address_); + return; + } + + applyBackoffPolicy(settings, ptr); + + switch (settings.pub_sub_case()) { + case rmq::Settings::PubSubCase::kPublishing: { + applyPublishingConfig(settings, ptr); + break; + } + case rmq::Settings::PubSubCase::kSubscription: { + applySubscriptionConfig(settings, ptr); + break; + } + default: { + break; + } + } +} + +void TelemetryBidiReactor::applyBackoffPolicy(const rmq::Settings& settings, std::shared_ptr& ptr) { + // Apply backoff policy on throttling + if (settings.has_backoff_policy()) { + const auto& backoff_policy = settings.backoff_policy(); + ptr->config().backoff_policy.max_attempt = backoff_policy.max_attempts(); + if (backoff_policy.has_customized_backoff()) { + for (const auto& item : backoff_policy.customized_backoff().next()) { + auto backoff = std::chrono::seconds(item.seconds()) + std::chrono::nanoseconds(item.nanos()); + ptr->config().backoff_policy.next.push_back(absl::FromChrono(backoff)); + } + } + + if (backoff_policy.has_exponential_backoff()) { + const auto& exp_backoff = backoff_policy.exponential_backoff(); + ptr->config().backoff_policy.initial = absl::FromChrono(std::chrono::seconds(exp_backoff.initial().seconds()) + + std::chrono::nanoseconds(exp_backoff.initial().nanos())); + ptr->config().backoff_policy.multiplier = exp_backoff.multiplier(); + ptr->config().backoff_policy.max = absl::FromChrono(std::chrono::seconds(exp_backoff.max().seconds()) + + std::chrono::nanoseconds(exp_backoff.max().nanos())); + } + } +} + +void TelemetryBidiReactor::applyPublishingConfig(const rmq::Settings& settings, std::shared_ptr client) { + client->config().publisher.max_body_size = settings.publishing().max_body_size(); + client->config().publisher.compress_body_threshold = settings.publishing().compress_body_threshold(); +} + +void TelemetryBidiReactor::applySubscriptionConfig(const rmq::Settings& settings, std::shared_ptr client) { + client->config().subscriber.fifo = settings.subscription().fifo(); + auto polling_timeout = + google::protobuf::util::TimeUtil::DurationToMilliseconds(settings.subscription().long_polling_timeout()); + client->config().subscriber.polling_timeout = absl::Milliseconds(polling_timeout); + client->config().subscriber.receive_batch_size = settings.subscription().receive_batch_size(); +} + +void TelemetryBidiReactor::fireRead() { + SPDLOG_DEBUG("{}#fireRead", peer_address_); + StartRead(&read_); +} + +void TelemetryBidiReactor::write(TelemetryCommand command) { + { + absl::MutexLock lk(&writes_mtx_); + writes_.push_back(command); + } + fireWrite(); +} + +void TelemetryBidiReactor::fireWrite() { + SPDLOG_DEBUG("{}#fireWrite", peer_address_); + { + absl::MutexLock lk(&writes_mtx_); + if (writes_.empty()) { + SPDLOG_DEBUG("No TelemtryCommand to write. Peer={}", peer_address_); + return; + } + + bool expect = false; + if (command_inflight_.compare_exchange_strong(expect, true, std::memory_order_relaxed)) { + write_ = std::move(*writes_.begin()); + writes_.erase(writes_.begin()); + } else { + SPDLOG_DEBUG("Another command is already on the wire. Peer={}", peer_address_); + return; + } + } + SPDLOG_DEBUG("Writing telemetry command to {}: {}", peer_address_, write_.DebugString()); + StartWrite(&write_); +} + +void TelemetryBidiReactor::fireClose() { + SPDLOG_INFO("{}#fireClose", peer_address_); + if (StreamState::Active == stream_state_) { + StartWritesDone(); + { + absl::MutexLock lk(&stream_state_mtx_); + if (StreamState::Active == stream_state_) { + stream_state_cv_.Wait(&stream_state_mtx_); + } + } + } +} + +void TelemetryBidiReactor::OnWritesDoneDone(bool ok) { + SPDLOG_DEBUG("{}#OnWritesDoneDone", peer_address_); +} + +void TelemetryBidiReactor::onVerifyMessageResult(TelemetryCommand command) { + { + absl::MutexLock lk(&writes_mtx_); + writes_.emplace_back(command); + } + fireWrite(); +} + +/// Notifies the application that all operations associated with this RPC +/// have completed and all Holds have been removed. OnDone provides the RPC +/// status outcome for both successful and failed RPCs and will be called in +/// all cases. If it is not called, it indicates an application-level problem +/// (like failure to remove a hold). +/// +/// \param[in] status The status outcome of this RPC +void TelemetryBidiReactor::OnDone(const grpc::Status& status) { + SPDLOG_DEBUG("{}#OnDone, status.ok={}", peer_address_, status.ok()); + if (!status.ok()) { + SPDLOG_WARN("{}#OnDone, status.error_code={}, status.error_message={}, status.error_details={}", peer_address_, + status.error_code(), status.error_message(), status.error_details()); + } + + { + SPDLOG_DEBUG("{} notifies awaiting close call", peer_address_); + absl::MutexLock lk(&stream_state_mtx_); + stream_state_ = StreamState::Closed; + stream_state_cv_.SignalAll(); + } + + auto client = client_.lock(); + if (!client) { + return; + } + + if (client->active()) { + client->createSession(peer_address_, true); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TlsHelper.cpp b/src/main/cpp/client/TlsHelper.cpp index 62d3ac1f2..aa4970a54 100644 --- a/src/main/cpp/client/TlsHelper.cpp +++ b/src/main/cpp/client/TlsHelper.cpp @@ -16,10 +16,11 @@ */ #include "TlsHelper.h" +#include + #include "MixAll.h" #include "OpenSSLCompatible.h" -#include -#include +#include "openssl/hmac.h" ROCKETMQ_NAMESPACE_BEGIN @@ -37,213 +38,4 @@ std::string TlsHelper::sign(const std::string& access_secret, const std::string& return hex_str; } -const char* TlsHelper::CA = R"( ------BEGIN CERTIFICATE----- -MIIGCDCCA/CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAkNO -MRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UE -CgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEu -YXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcw -HhcNMjEwNDI1MDkwMjE5WhcNMzEwNDIzMDkwMjE5WjCBiDELMAkGA1UEBhMCQ04x -EjAQBgNVBAgMCVpoZSBKaWFuZzEPMA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhS -b2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3 -DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDLL1rQ3A6YnWwdAT3rcf7wXK2DN6AwkWfGexp5o8y+dPj49D5b -PhEVIDEjidtWASFfCunJq9Ku48K/U8tha58JPL2CJ3773Mw7CU3LwD46Ft32+kGM -3+rXuXi44Fs714U/jJkxA6AsJdl4VtgjfXGWhbTq7ZT//l3tECH7Q096cp4xXATx -0RuvlaE33k8SpOPB+oHyjdFTwSW9HuZx71dLNCoZ0b52teJDVJFMACz6R5t3ZglF -MUosUzEUKdCXqBDvjOzMI2GHr7NYKAWHnOPNcYbf8jKzQ5msW+1MOfacVX+YUmKr -4Tf8NZHTSA4MuSNZ46gVFK7tYau2FDAkBWSb4hnuqqoGn58QtwrRBWtCmnLc4133 -6sgEGoJJR5M7WF0usl7KAgqgwy6xdU1joHO4SzvpysQ9kHWr1hcTqpeLD/svxLpZ -3x0WrhIAQoyd4uIHv/501LgD2KE5YnBa4S10LYrZeQpoJO5YV56HJdyvqNgVh2fn -tIRJAKq/1Wvw6izK/zOC6ZC+ug3n9vPqCgpBI0NFm3aR4AcSx70FsmlfrjUAsluL -kIzQwuu3Ip7hUS9kBVUxmvaD2yy2HPc+UPiYm4Y2YxqTx2j11APbN32M8rj1mroE -d9JFUV3WjbY+1QEAQ9upWTJLnOzS4a8yNsXIdMoKeU68Lyi/F3wA9ciK9wIDAQAB -o2YwZDAdBgNVHQ4EFgQUebBfwZjpYrRBUX9XL89qA0LzaXEwHwYDVR0jBBgwFoAU -3NYFR3h8OuGYCHVl1gk2yFoM8LIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B -Af8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJdwY+mAgtdAkVB8gRwcql7YdmWM -vlc3OYr14DHQT8eN5ItGou8MvTUR3wxCz72788/GHG1dLl54cnxzWjLgZZdapfQL -hsl1Iz6JSzyMTrNZ0HiuRw9J5XnRUIhWAO9fpRV4f/ce9J4o9YMFt//GW9WJrL59 -860gNL+L1IaWDWdnPlTYnQc3ndhnStelURANtsDp808t5RO8w94/Tp/6+KgqXwSj -uAxeHNVoMcNcr/Cff/fkLXQjSlSnpX7+Yf4y9pPSq7z6QHdsfSVq9FL51cXUdK5W -XRzkWK2QKp82CRz2jbrXd9Q7rjrWtDq8vgpKsZg+myLd55wPjiscM/vZmf4LjjFC -ubfKrclMh9CFtSsk38p21l3MBl4hyULsqVHAvz4jMrTaNB+1vYQ4OhgiqxB1KJff -hw2sHZfthHLm8W+mOma/LwA+u4QFE3ihq6bjgAZxte56HUuQwa7y4FifFcZi05ih -CnXNRo8Q8napmPTwtG9XKk9HQwLSMH+wTMnrV9R8Nt4ZkPlsJV8uY/zKOfOGHsY/ -+nR5vQ046Y8MT9z26dZe5MUsGUf9706+yAQAQAwzZiYJ+HiWijAIVynITMAZuEh9 -JfwS66kGVNOlwqMkbAEO61tuGFmy5iRBnXOoUM2mbaGVvnbS5iBCkH6M69oq5vip -42zKYBLmpEvL4PdS ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGIDCCBAigAwIBAgIJAN/jxbRuUfSjMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD -VQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYDVQQHDAlIYW5nIFpob3Ux -DzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0TVExHDAaBgNVBAMME3Jv -Y2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEWFGxpemhhbmh1aUBhcGFj -aGUub3JnMB4XDTIxMDQyNTA4NTMyMVoXDTQxMDQyMDA4NTMyMVowgZwxCzAJBgNV -BAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEP -MA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9j -a2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNo -ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDWKlLdC6uqiPA9 -egWqBxAmKCB47ALsh7gO0nxa/0Mkr/QsRfvUtRHMoTqxLJ/IiFXmy5ZHtXq6VppL -CWE3cjN6nbYhxAyP5zmXYLQVf3BAwRqGl/ZVfZLEUvgLACMTpLTNbsqtF90UVF1K -txdsfpfKgyYp5Sff0rll94enuOfoAk18oI6ukxw5rMR3vqk7XYWdvqbEfPKr3Hiw -6r7B/JxFLv+FJIdmmHb4dgQ26Jcs9B6e2MKLXm1LRtNBI9ko6eE6UZmYkK22D/YZ -T65o8j+ZxMQPHEI/evNZp+5PfV+/061d+dtC+qgqmUTdYREmotfYtdFDNNYhZuou -GuvIG4yb02I8bYkz9ZjcWrSlLkjsODQGlF3iAy7k8HX7e952D7W/ZtXcU/1iQNVn -o0Q27QAyuTlWlBcsmSsWBiJWo6FXUDz4ODtPEXnNLNHVcRTq4EIxmMada+0nSSOu -S39XKVGiPpC/4nzxtL3kD/DAw1loTEyJxz5QNOKm34IacCbmDlJ0eqi9vPMy7Fl7 -VZ5hc/yfFh1FEUlkuc4oQJftbZbUnd6osZjX9AJVX3yW+NgYtezInMhma2sGy4wn -ie2x0C8TxXF5dRrgzCk/cka3rp/InFkWx7OlJ3QLkYI6YIzbSQuF9xzJK5zFoMGP -rBCylmS1f+Xf2EbXfpek/ZZLBnzmHwIDAQABo2MwYTAdBgNVHQ4EFgQU3NYFR3h8 -OuGYCHVl1gk2yFoM8LIwHwYDVR0jBBgwFoAU3NYFR3h8OuGYCHVl1gk2yFoM8LIw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD -ggIBAAUCkPkzYeXNluXxrJnQowDsi9z8klpD1XkgB3e8j4eOMiqby1TfPIQVRZS9 -d18iENpMFszF8fwEp0mMYhH4amHvL12qjdiepeYV57HmYUWeJIxvp/6xaJjcYJLE -jJILlNdDvZdKDwJZ2iCvV95KCZ0sp3kFgI1ZHnbLPuGw79lNAwVIfp2+/2QEq6Fu -TYEeWAbTaIQaUpQr2DpJShKkri4crdTN780dsfeTV0behD9MFmJPtpxL678KbF8a -j/A6lFWuts7/uwNXV+SR4wJMJGcxU1cRCsB99NGc0KIEZR92OiOg7U9cDdw5s448 -yhm6+9zk2FKobXkBp25IaCyiwmHo8JP0Ht8sCKIORKGnXWKmwhQmZDIty+d+Jwi+ -EuLnYEmgepgm4PYfehLvPo6krJrsyqfytJRcEyvlnWWobYv2S7jvpEcii/7huFqc -tBaxBXqLiIYxCWnygImeViquYBTu8jv87QfA8zCLSYVWWMitNnZE/Cw6RiCdlXdf -SIxY0S364T4z3zoO4i2Tl3dtgMb8qf6plvVe2mgbZxkXvkeSzGUJlvpovV2gW8jD -+agCIhmMjuYuBTsfWyj5cAfibcZSKb0xzehRUTtzX+Uc7GCiX8P5u2tgDBnxQUzM -dpafYF4v5HJmOyUNoNWdX6DhysaUK/sq4so8fW9vjgTQ5o2r ------END CERTIFICATE-----)"; - -const char* TlsHelper::client_certificate_chain = R"( ------BEGIN CERTIFICATE----- -MIIGDjCCA/agAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAkNO -MRIwEAYDVQQIDAlaaGUgSmlhbmcxDzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwI -Um9ja2V0TVExHDAaBgNVBAMME3JvY2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG -9w0BCQEWFGxpemhhbmh1aUBhcGFjaGUub3JnMB4XDTIxMDQyNTEyMjIxOVoXDTMx -MDQyMzEyMjIxOVowgZ4xCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcx -EjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UECgwGQWxpeXVuMQswCQYDVQQLDAJN -UTEdMBsGA1UEAwwUY2xpZW50Lm1xLmFsaXl1bi5jb20xKjAoBgkqhkiG9w0BCQEW -G3NodXRpYW4ubHpoQGFsaWJhYmEtaW5jLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALNtRLFYGNpCzIPKC3wHMxxtPOWFl5Wl5wj9TZLYntyQ+h5C -x6OaZzdqCIconAqM+F+glShoe9y4MQn4tS4cg5UpwfJjAD2XJiri56S8L2tB5t45 -5mTUF3iMoncq07NQZdWwVJcrkdxawwNLmBJjdfnALrCLrK8gejtsa/eClLArR3Ep -WlgSchxTvCWduYmb8bJFfHP5dZ1aT4rXJkUA+lDvm73N2B06f5HDazvNSxXqQ8AY -DnqKXPkUkA53gOgNXRwmY6TFfQLp3qzxD13ueUuUC/zonn4EifTdcp6xsYLjv+Fd -Fd0feGTHfeGi1Pp9nvogYbGzPeC7CaSrLphuCl0CAwEAAaOCAWgwggFkMAkGA1Ud -EwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NM -IEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO40rO1cUjC4 -CvSeLx+79vnzlXVFMIHKBgNVHSMEgcIwgb+AFHmwX8GY6WK0QVF/Vy/PagNC82lx -oYGipIGfMIGcMQswCQYDVQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYD -VQQHDAlIYW5nIFpob3UxDzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0 -TVExHDAaBgNVBAMME3JvY2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEW -FGxpemhhbmh1aUBhcGFjaGUub3JnggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0l -BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBAJtAPRUp3pLU+u860tZR -315Kxv2UHf/Mz+hqAa5N7xuojVq61VkcK+Q5SkV88B1Male3aadQcRjddGdDPTMX -c/mw6mZ43xuu2uPB47PRkCEbmwIpAiaY9nAKo0fQeHaM1roYtf20A864C0u76YsK -Gl+j8Gry0kYFMLEoVLjbdr4XtqkJOZCaHErsVjf83YSFPiyQaq6GpuHpGEyMoj+h -MM7Pcb5aqpehs9cRgYoU2IS2VXQZ6W71z5IHzw9xJngI8oZ94hwWzWyRBWl/u3jb -eNfVdh5baC9JjjJBUFedj5J8ayPl7Fh9e7YQDPhe2og42D9fWr+SmgM06QnU6fAG -s+n1ocKjpT+Rm+0Q9+BkKKebISrgP92cyiVpb2m7kCH2m52pnvEQ4GGXt5gVAHgA -UTRu0hDrkJtouu8lh5wpiu1oskgUgsYTKEpQkVB00Ich1pVxNVROWmLRHAJHVskl -MYcNAYihk4RVmyGwIEX37SWyMRKIjHY3loKJIwRJoMPduGXPk6E5NmhTRYSwKigo -pHjfDLXeAlTSYIEHUZMpTHNjigkNq5wSYYWs9BYoFaMg4k+n5m88cCsbkb9m+0b0 -BBtZdWGVKbQK0YkyS+p9UULVhYg82JaF/IHLm/yR0LcmrCUp7qK6iLlXxSSD3OVP -79fXjr+aaE5Mskph9yNKFAKi ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGCDCCA/CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZwxCzAJBgNVBAYTAkNO -MRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEPMA0GA1UE -CgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEu -YXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcw -HhcNMjEwNDI1MDkwMjE5WhcNMzEwNDIzMDkwMjE5WjCBiDELMAkGA1UEBhMCQ04x -EjAQBgNVBAgMCVpoZSBKaWFuZzEPMA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhS -b2NrZXRNUTEcMBoGA1UEAwwTcm9ja2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3 -DQEJARYUbGl6aGFuaHVpQGFwYWNoZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDLL1rQ3A6YnWwdAT3rcf7wXK2DN6AwkWfGexp5o8y+dPj49D5b -PhEVIDEjidtWASFfCunJq9Ku48K/U8tha58JPL2CJ3773Mw7CU3LwD46Ft32+kGM -3+rXuXi44Fs714U/jJkxA6AsJdl4VtgjfXGWhbTq7ZT//l3tECH7Q096cp4xXATx -0RuvlaE33k8SpOPB+oHyjdFTwSW9HuZx71dLNCoZ0b52teJDVJFMACz6R5t3ZglF -MUosUzEUKdCXqBDvjOzMI2GHr7NYKAWHnOPNcYbf8jKzQ5msW+1MOfacVX+YUmKr -4Tf8NZHTSA4MuSNZ46gVFK7tYau2FDAkBWSb4hnuqqoGn58QtwrRBWtCmnLc4133 -6sgEGoJJR5M7WF0usl7KAgqgwy6xdU1joHO4SzvpysQ9kHWr1hcTqpeLD/svxLpZ -3x0WrhIAQoyd4uIHv/501LgD2KE5YnBa4S10LYrZeQpoJO5YV56HJdyvqNgVh2fn -tIRJAKq/1Wvw6izK/zOC6ZC+ug3n9vPqCgpBI0NFm3aR4AcSx70FsmlfrjUAsluL -kIzQwuu3Ip7hUS9kBVUxmvaD2yy2HPc+UPiYm4Y2YxqTx2j11APbN32M8rj1mroE -d9JFUV3WjbY+1QEAQ9upWTJLnOzS4a8yNsXIdMoKeU68Lyi/F3wA9ciK9wIDAQAB -o2YwZDAdBgNVHQ4EFgQUebBfwZjpYrRBUX9XL89qA0LzaXEwHwYDVR0jBBgwFoAU -3NYFR3h8OuGYCHVl1gk2yFoM8LIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B -Af8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJdwY+mAgtdAkVB8gRwcql7YdmWM -vlc3OYr14DHQT8eN5ItGou8MvTUR3wxCz72788/GHG1dLl54cnxzWjLgZZdapfQL -hsl1Iz6JSzyMTrNZ0HiuRw9J5XnRUIhWAO9fpRV4f/ce9J4o9YMFt//GW9WJrL59 -860gNL+L1IaWDWdnPlTYnQc3ndhnStelURANtsDp808t5RO8w94/Tp/6+KgqXwSj -uAxeHNVoMcNcr/Cff/fkLXQjSlSnpX7+Yf4y9pPSq7z6QHdsfSVq9FL51cXUdK5W -XRzkWK2QKp82CRz2jbrXd9Q7rjrWtDq8vgpKsZg+myLd55wPjiscM/vZmf4LjjFC -ubfKrclMh9CFtSsk38p21l3MBl4hyULsqVHAvz4jMrTaNB+1vYQ4OhgiqxB1KJff -hw2sHZfthHLm8W+mOma/LwA+u4QFE3ihq6bjgAZxte56HUuQwa7y4FifFcZi05ih -CnXNRo8Q8napmPTwtG9XKk9HQwLSMH+wTMnrV9R8Nt4ZkPlsJV8uY/zKOfOGHsY/ -+nR5vQ046Y8MT9z26dZe5MUsGUf9706+yAQAQAwzZiYJ+HiWijAIVynITMAZuEh9 -JfwS66kGVNOlwqMkbAEO61tuGFmy5iRBnXOoUM2mbaGVvnbS5iBCkH6M69oq5vip -42zKYBLmpEvL4PdS ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGIDCCBAigAwIBAgIJAN/jxbRuUfSjMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD -VQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYDVQQHDAlIYW5nIFpob3Ux -DzANBgNVBAoMBkFwYWNoZTERMA8GA1UECwwIUm9ja2V0TVExHDAaBgNVBAMME3Jv -Y2tldG1xLmFwYWNoZS5vcmcxIzAhBgkqhkiG9w0BCQEWFGxpemhhbmh1aUBhcGFj -aGUub3JnMB4XDTIxMDQyNTA4NTMyMVoXDTQxMDQyMDA4NTMyMVowgZwxCzAJBgNV -BAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEP -MA0GA1UECgwGQXBhY2hlMREwDwYDVQQLDAhSb2NrZXRNUTEcMBoGA1UEAwwTcm9j -a2V0bXEuYXBhY2hlLm9yZzEjMCEGCSqGSIb3DQEJARYUbGl6aGFuaHVpQGFwYWNo -ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDWKlLdC6uqiPA9 -egWqBxAmKCB47ALsh7gO0nxa/0Mkr/QsRfvUtRHMoTqxLJ/IiFXmy5ZHtXq6VppL -CWE3cjN6nbYhxAyP5zmXYLQVf3BAwRqGl/ZVfZLEUvgLACMTpLTNbsqtF90UVF1K -txdsfpfKgyYp5Sff0rll94enuOfoAk18oI6ukxw5rMR3vqk7XYWdvqbEfPKr3Hiw -6r7B/JxFLv+FJIdmmHb4dgQ26Jcs9B6e2MKLXm1LRtNBI9ko6eE6UZmYkK22D/YZ -T65o8j+ZxMQPHEI/evNZp+5PfV+/061d+dtC+qgqmUTdYREmotfYtdFDNNYhZuou -GuvIG4yb02I8bYkz9ZjcWrSlLkjsODQGlF3iAy7k8HX7e952D7W/ZtXcU/1iQNVn -o0Q27QAyuTlWlBcsmSsWBiJWo6FXUDz4ODtPEXnNLNHVcRTq4EIxmMada+0nSSOu -S39XKVGiPpC/4nzxtL3kD/DAw1loTEyJxz5QNOKm34IacCbmDlJ0eqi9vPMy7Fl7 -VZ5hc/yfFh1FEUlkuc4oQJftbZbUnd6osZjX9AJVX3yW+NgYtezInMhma2sGy4wn -ie2x0C8TxXF5dRrgzCk/cka3rp/InFkWx7OlJ3QLkYI6YIzbSQuF9xzJK5zFoMGP -rBCylmS1f+Xf2EbXfpek/ZZLBnzmHwIDAQABo2MwYTAdBgNVHQ4EFgQU3NYFR3h8 -OuGYCHVl1gk2yFoM8LIwHwYDVR0jBBgwFoAU3NYFR3h8OuGYCHVl1gk2yFoM8LIw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD -ggIBAAUCkPkzYeXNluXxrJnQowDsi9z8klpD1XkgB3e8j4eOMiqby1TfPIQVRZS9 -d18iENpMFszF8fwEp0mMYhH4amHvL12qjdiepeYV57HmYUWeJIxvp/6xaJjcYJLE -jJILlNdDvZdKDwJZ2iCvV95KCZ0sp3kFgI1ZHnbLPuGw79lNAwVIfp2+/2QEq6Fu -TYEeWAbTaIQaUpQr2DpJShKkri4crdTN780dsfeTV0behD9MFmJPtpxL678KbF8a -j/A6lFWuts7/uwNXV+SR4wJMJGcxU1cRCsB99NGc0KIEZR92OiOg7U9cDdw5s448 -yhm6+9zk2FKobXkBp25IaCyiwmHo8JP0Ht8sCKIORKGnXWKmwhQmZDIty+d+Jwi+ -EuLnYEmgepgm4PYfehLvPo6krJrsyqfytJRcEyvlnWWobYv2S7jvpEcii/7huFqc -tBaxBXqLiIYxCWnygImeViquYBTu8jv87QfA8zCLSYVWWMitNnZE/Cw6RiCdlXdf -SIxY0S364T4z3zoO4i2Tl3dtgMb8qf6plvVe2mgbZxkXvkeSzGUJlvpovV2gW8jD -+agCIhmMjuYuBTsfWyj5cAfibcZSKb0xzehRUTtzX+Uc7GCiX8P5u2tgDBnxQUzM -dpafYF4v5HJmOyUNoNWdX6DhysaUK/sq4so8fW9vjgTQ5o2r ------END CERTIFICATE----- -)"; - -const char* TlsHelper::client_private_key = R"( ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAs21EsVgY2kLMg8oLfAczHG085YWXlaXnCP1Nktie3JD6HkLH -o5pnN2oIhyicCoz4X6CVKGh73LgxCfi1LhyDlSnB8mMAPZcmKuLnpLwva0Hm3jnm -ZNQXeIyidyrTs1Bl1bBUlyuR3FrDA0uYEmN1+cAusIusryB6O2xr94KUsCtHcSla -WBJyHFO8JZ25iZvxskV8c/l1nVpPitcmRQD6UO+bvc3YHTp/kcNrO81LFepDwBgO -eopc+RSQDneA6A1dHCZjpMV9AunerPEPXe55S5QL/OiefgSJ9N1ynrGxguO/4V0V -3R94ZMd94aLU+n2e+iBhsbM94LsJpKsumG4KXQIDAQABAoIBAQCQigN+QZOzr4AE -4mwh+8LGsZEO7HFSUhUtfrBRDUPcNsXmPrGIL8faMgawqpwZO7qRflDp/lFZWt/W -Qo3hmYK+VKcyVWJKO0q53ZPxds9UPgB3IHli3CAUMmZmTQ/BMZD3dVaPeL/FnS/f -pbPZjFnrWU/DVKFFaopM+Gh5OK4Cw2Be704vGdqoLLSAixEpAPh+U7UFyDO2Er6r -ZewpqKz6fgwzn/FIXIe+OSlIg+5IyDNBYJkXDtAAd4tCxAflnLbbcIoHjeASiAZy -qg9ax5s2+rm1yRM2Eds/jMX6vKqCTWeOq7T8Sz5FpgS9F6hL70LnBy6dxwK2MYKr -lGjNc4wZAoGBAOX51pit7YuMk1vPO5wV8TUzX7jVz9N1zuuIybpDuhZ0y7gdM8L4 -LP+gaQpsdoeFnKCYC7oGjqLLrfXVxARqq1FLIaMTpd4C3uHAyWQmoZjBpFiw5a9f -FhZ67uDMEYTWoegAkDKV4O/AnfLkoDSJexh4eZ7LnChoEtGth2C4uuTPAoGBAMe7 -E1hXctDszEDQCbfzkvz4zSJZ32CzauYFCVxLz+4xQVRDKQccMwVnbDSjA0nq1RmN -c7COrEGQ4EkTgy/KGfo+KeEGoDWq3UG/EO+6lBSTaWD1M0E1nqidasPx3CuhZ81d -dAaqo4Hgm+QZOFPx7bCdNAZw8u/RS+ELxanEwcETAoGBAK/DiqeWBn8pKVZ5w+0/ -N+KZKQdzIitTVpndKZAaQxb2nNxSNUVFt96HEsDMtKpPC64RhqTebhUt5f6FKhLW -E6lF6qdgVNyhBQc0+LZ6b/b3yEugp2qpGZCE1B0q2hjUkVlnVM3Ujt2uMQl6gpak -nFfPe+RJaUfYgsmN/nGFvhExAoGAA/4LTvLAisiQ9KKz7wdhYskeYJdnTamIc3ya -SUwCpgI6ChMlmMzJgEwm745qnGmjRNV9AIR5kP3yXcOUFbp2dngTojndrfTyY0QM -dJ5II3km3BdKpP0LZNdwsOX+vqiFSKebkB6NEiWJODAA3jyxwZwC1DYhxQVr/N2d -X91c+h0CgYEA4PnAEmTILyJ9OPwV2A1cQ/RPi4smxZ0IIXZt/pmiEV4MYR5ULT7g -XzK3CHvV0wghQdBIxfAs8H31Ayux6N0wnva0w3tHesw15ByiDeNeJKO+tjpc01Zq -3xU6hUkWYGclvlezeWPQFkJKWYCLX4uw8lWOLCGd8VY2OZXJkS6W608= ------END RSA PRIVATE KEY-----)"; - ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/TopicAssignmentInfo.cpp b/src/main/cpp/client/TopicAssignmentInfo.cpp index 88e997601..9ebd8e1fc 100644 --- a/src/main/cpp/client/TopicAssignmentInfo.cpp +++ b/src/main/cpp/client/TopicAssignmentInfo.cpp @@ -14,67 +14,46 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "TopicAssignmentInfo.h" -#include "google/rpc/code.pb.h" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" +#include "LoggerImpl.h" ROCKETMQ_NAMESPACE_BEGIN thread_local uint32_t TopicAssignment::query_which_broker_ = 0; -TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) : debug_string_(response.DebugString()) { - if (response.common().status().code() != google::rpc::Code::OK) { +TopicAssignment::TopicAssignment(const QueryAssignmentResponse& response) { + if (response.status().code() != rmq::Code::OK) { SPDLOG_WARN("QueryAssignmentResponse#code is not SUCCESS. Keep assignment info intact. QueryAssignmentResponse: {}", response.DebugString()); return; } for (const auto& item : response.assignments()) { - const rmq::Partition& partition = item.partition(); - if (rmq::Permission::READ != partition.permission() && rmq::Permission::READ_WRITE != partition.permission()) { + const rmq::MessageQueue& queue = item.message_queue(); + if (!readable(queue.permission())) { continue; } - assert(partition.has_broker()); - const auto& broker = partition.broker(); + assert(queue.has_broker()); + const auto& broker = queue.broker(); if (broker.endpoints().addresses().empty()) { SPDLOG_WARN("Broker[{}] is not addressable", broker.DebugString()); continue; } - MQMessageQueue message_queue(partition.topic().name(), partition.broker().name(), partition.id()); - std::string service_address; - for (const auto& address : broker.endpoints().addresses()) { - if (service_address.empty()) { - switch (broker.endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - service_address.append("ipv4:"); - break; - case rmq::AddressScheme::IPv6: - service_address.append("ipv6:"); - break; - case rmq::AddressScheme::DOMAIN_NAME: - service_address.append("dns:"); - break; - default: - SPDLOG_WARN("Unsupported gRPC naming scheme"); - break; - } - } else { - service_address.append(","); - } - service_address.append(address.host()).append(":").append(std::to_string(address.port())); - - if (rmq::AddressScheme::DOMAIN_NAME == broker.endpoints().scheme()) { - break; - } - } - message_queue.serviceAddress(service_address); - assignment_list_.emplace_back(Assignment(message_queue)); + rmq::Assignment assignment; + assignment.mutable_message_queue()->CopyFrom(queue); + assignment_list_.emplace_back(std::move(assignment)); } - std::sort(assignment_list_.begin(), assignment_list_.end()); + + std::sort(assignment_list_.begin(), assignment_list_.end(), + [](const rmq::Assignment& lhs, const rmq::Assignment& rhs) { return lhs < rhs; }); +} + +bool operator==(const TopicAssignment& lhs, const TopicAssignment& rhs) { + return lhs.assignmentList() == rhs.assignmentList(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/TopicPublishInfo.cpp b/src/main/cpp/client/TopicPublishInfo.cpp deleted file mode 100644 index 9d43afb12..000000000 --- a/src/main/cpp/client/TopicPublishInfo.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TopicPublishInfo.h" -#include "TopicRouteData.h" - -#include "LoggerImpl.h" -#include "MixAll.h" -#include "absl/container/flat_hash_map.h" -#include "absl/strings/str_join.h" - -ROCKETMQ_NAMESPACE_BEGIN - -thread_local uint32_t TopicPublishInfo::send_which_queue_ = MixAll::random(0, 100); - -TopicPublishInfo::TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data) - : topic_(topic.data(), topic.length()), topic_route_data_(std::move(topic_route_data)) { - updatePublishInfo(); -} - -bool TopicPublishInfo::selectOneMessageQueue(MQMessageQueue& message_queue) { - unsigned int index = ++send_which_queue_; - { - absl::MutexLock lock(&partition_list_mtx_); - if (partition_list_.empty()) { - return false; - } - auto& partition = partition_list_[index % (partition_list_.size())]; - message_queue = partition.asMessageQueue(); - } - return true; -} - -bool TopicPublishInfo::selectOneActiveMessageQueue(absl::flat_hash_set& isolated, - MQMessageQueue& message_queue) { - unsigned int index = ++send_which_queue_; - { - absl::MutexLock lock(&partition_list_mtx_); - if (partition_list_.empty()) { - SPDLOG_DEBUG("message queue list is empty"); - return false; - } - - for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { - message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); - if (!isolated.contains(message_queue.serviceAddress())) { - SPDLOG_DEBUG("Selected host={}", message_queue.serviceAddress()); - return true; - } - } - } - return false; -} - -bool TopicPublishInfo::takeMessageQueues(absl::flat_hash_set& isolated, - std::vector& candidates, uint32_t count) { - - unsigned int index = ++send_which_queue_; - { - absl::MutexLock lock(&partition_list_mtx_); - if (partition_list_.empty()) { - SPDLOG_DEBUG("message queue list empty"); - return false; - } - - for (std::vector::size_type i = 0; i < partition_list_.size(); ++i) { - const MQMessageQueue& message_queue = partition_list_[index++ % (partition_list_.size())].asMessageQueue(); - if (!isolated.contains(message_queue.serviceAddress())) { - auto search = std::find_if(candidates.begin(), candidates.end(), [&](const MQMessageQueue& item) { - return item.getBrokerName() == message_queue.getBrokerName(); - }); - if (std::end(candidates) == search) { - candidates.emplace_back(message_queue); - } - if (candidates.size() >= count) { - return true; - } - } - } - } - return !candidates.empty(); -} - -void TopicPublishInfo::updatePublishInfo() { - - std::vector writable_partition_list; - { - for (const auto& partition : topic_route_data_->partitions()) { - assert(partition.topic().name() == topic_); - if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { - continue; - } - if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { - continue; - } - writable_partition_list.push_back(partition); - } - } - - if (writable_partition_list.empty()) { - SPDLOG_WARN("No writable partition is current available. Skip updating publish table for topic={}", topic_); - return; - } - - { - absl::MutexLock lk(&partition_list_mtx_); - partition_list_.swap(writable_partition_list); - } -} - -void TopicPublishInfo::topicRouteData(TopicRouteDataPtr topic_route_data) { - - SPDLOG_DEBUG("Update publish info according to renewed route data of topic={}", topic_); - { topic_route_data_ = std::move(topic_route_data); } - updatePublishInfo(); -} - -std::vector TopicPublishInfo::getMessageQueueList() { - std::vector message_queue_list; - { - absl::MutexLock lock(&partition_list_mtx_); - for (const auto& partition : partition_list_) { - if (Permission::READ == partition.permission() || Permission::NONE == partition.permission()) { - continue; - } - - MQMessageQueue message_queue(partition.asMessageQueue()); - if (message_queue.serviceAddress().empty()) { - SPDLOG_WARN("Failed to resolve service address for {}", message_queue.simpleName()); - continue; - } - message_queue_list.emplace_back(message_queue); - } - - std::sort(message_queue_list.begin(), message_queue_list.end()); - - return message_queue_list; - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Assignment.h b/src/main/cpp/client/include/Assignment.h deleted file mode 100644 index eea6bd0d6..000000000 --- a/src/main/cpp/client/include/Assignment.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "ConsumeMessageType.h" -#include "rocketmq/MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class Assignment { -public: - Assignment(MQMessageQueue message_queue) : message_queue_(std::move(message_queue)) { - } - - bool operator==(const Assignment& rhs) const { - if (this == &rhs) { - return true; - } - - return message_queue_ == rhs.message_queue_; - } - - bool operator<(const Assignment& rhs) const { - return message_queue_ < rhs.message_queue_; - } - - const MQMessageQueue& messageQueue() const { - return message_queue_; - } - -private: - MQMessageQueue message_queue_; -}; -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Broker.h b/src/main/cpp/client/include/Broker.h deleted file mode 100644 index 35d24b881..000000000 --- a/src/main/cpp/client/include/Broker.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "ServiceAddress.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class Broker { -public: - Broker(std::string name, int id, ServiceAddress service_address) - : name_(std::move(name)), id_(id), service_address_(std::move(service_address)) { - } - - const std::string& name() const { - return name_; - } - - int32_t id() const { - return id_; - } - - explicit operator bool() const { - return service_address_.operator bool(); - } - - bool operator==(const Broker& other) const { - return name_ == other.name_; - } - - bool operator<(const Broker& other) const { - return name_ < other.name_; - } - - std::string serviceAddress() const { - return service_address_.address(); - } - -private: - std::string name_; - int32_t id_; - ServiceAddress service_address_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Client.h b/src/main/cpp/client/include/Client.h index 0ad6e3dc3..db4eb09f3 100644 --- a/src/main/cpp/client/include/Client.h +++ b/src/main/cpp/client/include/Client.h @@ -18,15 +18,20 @@ #include -#include "absl/container/flat_hash_set.h" - #include "ClientConfig.h" +#include "RpcClient.h" +#include "absl/container/flat_hash_set.h" +#include "rocketmq/CredentialsProvider.h" ROCKETMQ_NAMESPACE_BEGIN -class Client : virtual public ClientConfig { +class Client { public: - ~Client() override = default; + virtual ~Client() = default; + + virtual rmq::Settings clientSettings() = 0; + + virtual ClientConfig& config() = 0; virtual void endpointsInUse(absl::flat_hash_set& endpoints) = 0; @@ -36,18 +41,26 @@ class Client : virtual public ClientConfig { virtual void onRemoteEndpointRemoval(const std::vector&) = 0; + virtual void schedule(const std::string& task_name, const std::function& task, + std::chrono::milliseconds delay) = 0; + /** - * For endpoints that are marked as inactive due to one or multiple business - * operation failure, this function is to initiate health-check RPCs; Once the - * health-check passes, they are conceptually add back to serve further - * business workload. + * @brief Create a Session object + * + * @param target Remote endpoint address in form of gRPC naming convention. + * @param verify Verify presence of target in route table before creating session. */ - virtual void healthCheck() = 0; + virtual void createSession(const std::string& target, bool verify) = 0; - virtual void schedule(const std::string& task_name, const std::function& task, - std::chrono::milliseconds delay) = 0; + virtual void verify(MessageConstSharedPtr message, std::function) = 0; + + virtual void recoverOrphanedTransaction(MessageConstSharedPtr message) = 0; virtual void notifyClientTermination() = 0; + + virtual void withCredentialsProvider(std::shared_ptr credentials_provider) = 0; + + virtual std::shared_ptr manager() const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 97a1ec239..458ca5f3c 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -16,37 +16,45 @@ */ #pragma once +#include +#include #include +#include +#include "absl/container/flat_hash_map.h" #include "absl/time/time.h" +#include "Protocol.h" +#include "RetryPolicy.h" #include "rocketmq/CredentialsProvider.h" ROCKETMQ_NAMESPACE_BEGIN -class ClientConfig { -public: - virtual ~ClientConfig() = default; - - virtual const std::string& region() const = 0; - - virtual const std::string& serviceName() const = 0; - - virtual const std::string& resourceNamespace() const = 0; - - virtual CredentialsProviderPtr credentialsProvider() = 0; - - virtual const std::string& tenantId() const = 0; - - virtual absl::Duration getIoTimeout() const = 0; - - virtual absl::Duration getLongPollingTimeout() const = 0; - - virtual const std::string& getGroupName() const = 0; +struct PublisherConfig { + std::vector topics; + std::uint32_t compress_body_threshold{4096}; + std::uint32_t max_body_size{4194304}; +}; - virtual std::string clientId() const = 0; +struct SubscriberConfig { + rmq::Resource group; + absl::flat_hash_map subscriptions; + bool fifo{false}; + std::uint32_t receive_batch_size{32}; + absl::Duration polling_timeout{absl::Seconds(30)}; +}; - virtual bool isTracingEnabled() const = 0; +struct ClientConfig { + std::string client_id; + rmq::ClientType client_type{rmq::ClientType::CLIENT_TYPE_UNSPECIFIED}; + RetryPolicy backoff_policy; + // TODO: use std::chrono::milliseconds + absl::Duration request_timeout; + std::string region{"cn-hangzhou"}; + std::string resource_namespace; + std::shared_ptr credentials_provider; + PublisherConfig publisher; + SubscriberConfig subscriber; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientConfigImpl.h b/src/main/cpp/client/include/ClientConfigImpl.h deleted file mode 100644 index 116eb7e42..000000000 --- a/src/main/cpp/client/include/ClientConfigImpl.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/time/time.h" - -#include "ClientConfig.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ClientConfigImpl : virtual public ClientConfig { -public: - explicit ClientConfigImpl(absl::string_view group_name); - - ~ClientConfigImpl() override = default; - - const std::string& resourceNamespace() const override { - return resource_namespace_; - } - - void resourceNamespace(absl::string_view resource_namespace) { - resource_namespace_ = std::string(resource_namespace.data(), resource_namespace.length()); - } - - std::string clientId() const override; - - const std::string& getInstanceName() const; - - void setInstanceName(std::string instance_name); - - const std::string& getGroupName() const override; - void setGroupName(std::string group_name); - - const std::string& getUnitName() const { - return unit_name_; - } - void setUnitName(std::string unit_name) { - unit_name_ = std::move(unit_name); - } - - absl::Duration getIoTimeout() const override; - void setIoTimeout(absl::Duration timeout); - - absl::Duration getLongPollingTimeout() const override { - return long_polling_timeout_; - } - - void setLongPollingTimeout(absl::Duration timeout) { - long_polling_timeout_ = timeout; - } - - bool isTracingEnabled() const override { - return enable_tracing_.load(); - } - - void enableTracing(bool enabled) { - enable_tracing_.store(enabled); - } - - CredentialsProviderPtr credentialsProvider() override; - void setCredentialsProvider(CredentialsProviderPtr credentials_provider); - - void serviceName(std::string service_name) { - service_name_ = std::move(service_name); - } - const std::string& serviceName() const override { - return service_name_; - } - - void region(std::string region) { - region_ = std::move(region); - } - const std::string& region() const override { - return region_; - } - - void tenantId(std::string tenant_id) { - tenant_id_ = std::move(tenant_id); - } - const std::string& tenantId() const override { - return tenant_id_; - } - - static const char* CLIENT_VERSION; - -protected: - /** - * Name of the service. - */ - std::string service_name_{"ONS"}; - - /** - * Region of the service to connect to. - */ - std::string region_; - - /** - * RocketMQ instance namespace, in which topic, consumer group and any other - * abstract resources remain unique. - */ - std::string resource_namespace_; - - /** - * Tenant identifier. - */ - std::string tenant_id_; - - std::string instance_name_{steadyName()}; - - std::string group_name_; - - std::string unit_name_; - - CredentialsProviderPtr credentials_provider_; - - absl::Duration io_timeout_; - - absl::Duration long_polling_timeout_; - - std::atomic enable_tracing_{true}; - - static std::string steadyName(); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index dceace394..c85c7ec2f 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -22,18 +22,22 @@ #include #include "Client.h" +#include "MessageExt.h" +#include "Metadata.h" #include "ReceiveMessageCallback.h" #include "RpcClient.h" #include "Scheduler.h" +#include "TelemetryBidiReactor.h" #include "TopAddressing.h" #include "TopicRouteData.h" -#include "rocketmq/MQMessageExt.h" +#include "rocketmq/SendCallback.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN -using Metadata = absl::flat_hash_map; - +/** + * @brief SessionManager + */ class ClientManager { public: virtual ~ClientManager() = default; @@ -44,6 +48,8 @@ class ClientManager { virtual SchedulerSharedPtr getScheduler() = 0; + virtual std::shared_ptr getRpcClient(const std::string& target_host, bool need_heartbeat) = 0; + virtual std::shared_ptr createChannel(const std::string& target_host) = 0; virtual void resolveRoute(const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, @@ -54,17 +60,14 @@ class ClientManager { std::chrono::milliseconds timeout, const std::function& cb) = 0; - virtual void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; - - virtual bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) = 0; + virtual MessageConstSharedPtr wrapMessage(const rmq::Message&) = 0; virtual void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb) = 0; - virtual void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& callback) = 0; + virtual void changeInvisibleDuration(const std::string& target_host, const Metadata& metadata, + const ChangeInvisibleDurationRequest&, std::chrono::milliseconds timeout, + const std::function&) = 0; virtual void forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, @@ -75,15 +78,6 @@ class ClientManager { const EndTransactionRequest& request, std::chrono::milliseconds timeout, const std::function& cb) = 0; - virtual void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) = 0; - - virtual void - healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; - virtual void addClientObserver(std::weak_ptr client) = 0; virtual void @@ -92,27 +86,15 @@ class ClientManager { const std::function& cb) = 0; virtual void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, - std::chrono::milliseconds timeout, const std::shared_ptr& cb) = 0; + std::chrono::milliseconds timeout, ReceiveMessageCallback callback) = 0; virtual bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) = 0; - - virtual void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) = 0; + SendCallback cb) = 0; virtual std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) = 0; - virtual std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, - const ReportThreadStackTraceRequest& request, - std::chrono::milliseconds timeout) = 0; - - virtual std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, - const ReportMessageConsumptionResultRequest& request, - std::chrono::milliseconds timeout) = 0; - virtual State state() const = 0; virtual void submit(std::function task) = 0; diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 47d33909b..50d8b0525 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -21,33 +21,33 @@ #include #include #include +#include #include #include #include -#include "Scheduler.h" -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" - #include "Client.h" #include "ClientManager.h" #include "HeartbeatDataCallback.h" #include "Histogram.h" +#include "InsecureCertificateVerifier.h" #include "InvocationContext.h" -#include "OrphanTransactionCallback.h" #include "ReceiveMessageCallback.h" #include "RpcClient.h" #include "RpcClientImpl.h" +#include "Scheduler.h" #include "SchedulerImpl.h" #include "SendMessageContext.h" +#include "TelemetryBidiReactor.h" #include "ThreadPoolImpl.h" #include "TopAddressing.h" #include "TopicRouteChangeCallback.h" #include "TopicRouteData.h" -#include "rocketmq/AsyncCallback.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN @@ -86,24 +86,14 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const std::function& cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); - void doHealthCheck() LOCKS_EXCLUDED(clients_mtx_); - /** * If inactive RPC clients refer to remote hosts that are absent from topic_route_table_, we need to purge them * immediately. */ std::vector cleanOfflineRpcClients() LOCKS_EXCLUDED(clients_mtx_, rpc_clients_mtx_); - /** - * Execute health-check on behalf of the client. - */ - void healthCheck(const std::string& target_host, const Metadata& metadata, const HealthCheckRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) - override LOCKS_EXCLUDED(rpc_clients_mtx_); - bool send(const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); + SendCallback cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); /** * Get a RpcClient according to the given target hosts, which follows scheme specified @@ -116,10 +106,11 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share * @param need_heartbeat * @return */ - RpcClientSharedPtr getRpcClient(const std::string& target_host, bool need_heartbeat = true) + RpcClientSharedPtr getRpcClient(const std::string& target_host, bool need_heartbeat = true) override LOCKS_EXCLUDED(rpc_clients_mtx_); - static SendResult processSendResponse(const MQMessageQueue& message_queue, const SendMessageResponse& response); + static SendReceipt processSendResponse(const rmq::MessageQueue& message_queue, const SendMessageResponse& response, + std::error_code& ec); // only for test void addRpcClient(const std::string& target_host, const RpcClientSharedPtr& client) LOCKS_EXCLUDED(rpc_clients_mtx_); @@ -134,17 +125,16 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const std::function& cb) override; void receiveMessage(const std::string& target, const Metadata& metadata, const ReceiveMessageRequest& request, - std::chrono::milliseconds timeout, const std::shared_ptr& cb) override + std::chrono::milliseconds timeout, ReceiveMessageCallback cb) override LOCKS_EXCLUDED(rpc_clients_mtx_); /** * Translate protobuf message struct to domain model. * * @param item - * @param message_ext * @return true if the translation succeeded; false if something wrong happens, including checksum verification, etc. */ - bool wrapMessage(const rmq::Message& item, MQMessageExt& message_ext) override; + MessageConstSharedPtr wrapMessage(const rmq::Message& item) override; SchedulerSharedPtr getScheduler() override; @@ -156,8 +146,9 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share void ack(const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, std::chrono::milliseconds timeout, const std::function& cb) override; - void nack(const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& callback) override; + void changeInvisibleDuration(const std::string& target_host, const Metadata& metadata, + const ChangeInvisibleDurationRequest&, std::chrono::milliseconds timeout, + const std::function&) override; void forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, @@ -182,30 +173,10 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share std::chrono::milliseconds timeout, const std::function& cb) override; - void pollCommand(const std::string& target, const Metadata& metadata, const PollCommandRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) override; - - void queryOffset(const std::string& target_host, const Metadata& metadata, const QueryOffsetRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) override; - - void pullMessage(const std::string& target_host, const Metadata& metadata, const PullMessageRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) override; - std::error_code notifyClientTermination(const std::string& target_host, const Metadata& metadata, const NotifyClientTerminationRequest& request, std::chrono::milliseconds timeout) override; - std::error_code reportThreadStackTrace(const std::string& target_host, const Metadata& metadata, - const ReportThreadStackTraceRequest& request, - std::chrono::milliseconds timeout) override; - - std::error_code reportMessageConsumptionResult(const std::string& target_host, const Metadata& metadata, - const ReportMessageConsumptionResultRequest& request, - std::chrono::milliseconds timeout) override; - void trace(bool trace) { trace_ = trace; } @@ -221,15 +192,12 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share private: void doHeartbeat(); - void pollCompletionQueue(); - void logStats(); SchedulerSharedPtr scheduler_; static const char* HEARTBEAT_TASK_NAME; static const char* STATS_TASK_NAME; - static const char* HEALTH_CHECK_TASK_NAME; std::string resource_namespace_; @@ -242,14 +210,10 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share absl::Mutex rpc_clients_mtx_; // protects rpc_clients_ std::uint32_t heartbeat_task_id_{0}; - std::uint32_t health_check_task_id_{0}; std::uint32_t stats_task_id_{0}; - std::shared_ptr completion_queue_; std::unique_ptr callback_thread_pool_; - std::thread completion_queue_thread_; - Histogram latency_histogram_; absl::flat_hash_set exporter_endpoint_set_ GUARDED_BY(exporter_endpoint_set_mtx_); @@ -260,13 +224,12 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share */ std::string tenant_id_; - std::string service_name_{"MQ"}; + std::string service_name_{"RocketMQ"}; /** * TLS configuration */ - std::shared_ptr server_authorization_check_config_; - std::shared_ptr certificate_provider_; + std::shared_ptr certificate_verifier_; grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; std::shared_ptr channel_credential_; grpc::ChannelArguments channel_arguments_; diff --git a/src/main/cpp/client/include/InsecureCertificateVerifier.h b/src/main/cpp/client/include/InsecureCertificateVerifier.h new file mode 100644 index 000000000..a673ddd5d --- /dev/null +++ b/src/main/cpp/client/include/InsecureCertificateVerifier.h @@ -0,0 +1,42 @@ +#include "grpcpp/security/tls_certificate_verifier.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class InsecureCertificateVerifier : public grpc::experimental::ExternalCertificateVerifier { +public: + // The verification logic that will be performed after the TLS handshake + // completes. Implementers can choose to do their checks synchronously or + // asynchronously. + // + // request: the verification information associated with this request + // callback: This should only be used if your check is done asynchronously. + // When the asynchronous work is done, invoke this callback function + // with the proper status, indicating the success or the failure of + // the check. The implementer MUST NOT invoke this |callback| in the + // same thread before Verify() returns, otherwise it can lead to + // deadlocks. + // sync_status: This should only be used if your check is done synchronously. + // Modifies this value to indicate the success or the failure of + // the check. + // return: return true if your check is done synchronously, otherwise return + // false + bool Verify(grpc::experimental::TlsCustomVerificationCheckRequest* request, + std::function callback, + grpc::Status* sync_status) override { + *sync_status = grpc::Status(grpc::StatusCode::OK, ""); + return true; + } + + // Cancels a verification request previously started via Verify(). + // Used when the connection attempt times out or is cancelled while an async + // verification request is pending. The implementation should abort whatever + // async operation it is waiting for and quickly invoke the callback that was + // passed to Verify() with a status indicating the cancellation. + // + // request: the verification information associated with this request + void Cancel(grpc::experimental::TlsCustomVerificationCheckRequest* request) override { + } +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/Encoding.h b/src/main/cpp/client/include/Metadata.h similarity index 89% rename from src/main/cpp/base/include/Encoding.h rename to src/main/cpp/client/include/Metadata.h index cfb71cae0..c831629b3 100644 --- a/src/main/cpp/base/include/Encoding.h +++ b/src/main/cpp/client/include/Metadata.h @@ -16,17 +16,12 @@ */ #pragma once -#include - #include "rocketmq/RocketMQ.h" +#include "absl/container/flat_hash_map.h" + ROCKETMQ_NAMESPACE_BEGIN -enum class Encoding : int8_t -{ - IDENTITY = 0, - GZIP = 1, - SNAPPY = 2, -}; +using Metadata = absl::flat_hash_map; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Partition.h b/src/main/cpp/client/include/Partition.h deleted file mode 100644 index 1d5d2658c..000000000 --- a/src/main/cpp/client/include/Partition.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "Broker.h" -#include "Topic.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum Permission : int8_t -{ - NONE = 0, - READ = 1, - WRITE = 2, - READ_WRITE = 3 -}; - -class Partition { -public: - Partition(Topic topic, int32_t id, Permission permission, Broker broker) - : topic_(std::move(topic)), broker_(std::move(broker)), id_(id), permission_(permission) { - } - - const Topic& topic() const { - return topic_; - } - - int32_t id() const { - return id_; - } - - const Broker& broker() const { - return broker_; - } - - Permission permission() const { - return permission_; - } - - MQMessageQueue asMessageQueue() const { - MQMessageQueue message_queue(topic_.name(), broker_.name(), id_); - message_queue.serviceAddress(broker_.serviceAddress()); - return message_queue; - } - - bool operator==(const Partition& other) const { - return topic_ == other.topic_ && id_ == other.id_ && broker_ == other.broker_ && permission_ == other.permission_; - } - - bool operator<(const Partition& other) const { - if (topic_ < other.topic_) { - return true; - } else if (other.topic_ < topic_) { - return false; - } - - if (broker_ < other.broker_) { - return true; - } else if (other.broker_ < broker_) { - return false; - } - - return id_ < other.id_; - } - -private: - Topic topic_; - Broker broker_; - int32_t id_; - Permission permission_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageCallback.h b/src/main/cpp/client/include/ReceiveMessageCallback.h index 10ddaf7ac..6a9db7cf2 100644 --- a/src/main/cpp/client/include/ReceiveMessageCallback.h +++ b/src/main/cpp/client/include/ReceiveMessageCallback.h @@ -16,19 +16,13 @@ */ #pragma once +#include #include #include "ReceiveMessageResult.h" -#include "rocketmq/AsyncCallback.h" -#include "rocketmq/ErrorCode.h" ROCKETMQ_NAMESPACE_BEGIN -class ReceiveMessageCallback : public AsyncCallback { -public: - ~ReceiveMessageCallback() override = default; - - virtual void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) = 0; -}; +using ReceiveMessageCallback = std::function; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/MQSelector.h b/src/main/cpp/client/include/ReceiveMessageContext.h similarity index 74% rename from api/rocketmq/MQSelector.h rename to src/main/cpp/client/include/ReceiveMessageContext.h index 0b31e55d8..190f1bbb9 100644 --- a/api/rocketmq/MQSelector.h +++ b/src/main/cpp/client/include/ReceiveMessageContext.h @@ -16,18 +16,21 @@ */ #pragma once -#include +#include -#include "MQMessage.h" -#include "MQMessageQueue.h" +#include "InvocationContext.h" +#include "Metadata.h" +#include "ReceiveMessageCallback.h" ROCKETMQ_NAMESPACE_BEGIN -class MessageQueueSelector { -public: - virtual ~MessageQueueSelector() = default; +struct ReceiveMessageContext : public BaseInvocationContext { + ReceiveMessageCallback callback; + Metadata metadata; + std::chrono::milliseconds timeout; - virtual MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) = 0; + void onCompletion(bool ok) override {} + }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/ReceiveMessageResult.h b/src/main/cpp/client/include/ReceiveMessageResult.h index 32efad98a..d084cbf23 100644 --- a/src/main/cpp/client/include/ReceiveMessageResult.h +++ b/src/main/cpp/client/include/ReceiveMessageResult.h @@ -21,9 +21,9 @@ #include #include +#include "MessageExt.h" #include "MixAll.h" #include "absl/time/time.h" -#include "rocketmq/MQMessageExt.h" ROCKETMQ_NAMESPACE_BEGIN @@ -31,7 +31,7 @@ struct ReceiveMessageResult { absl::Time pop_time; absl::Duration invisible_time; - std::vector messages; + std::vector messages; std::string source_host; diff --git a/src/main/cpp/client/include/ReceiveMessageStreamReader.h b/src/main/cpp/client/include/ReceiveMessageStreamReader.h new file mode 100644 index 000000000..f0a767e26 --- /dev/null +++ b/src/main/cpp/client/include/ReceiveMessageStreamReader.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "MessageExt.h" +#include "ReceiveMessageCallback.h" +#include "ReceiveMessageContext.h" +#include "grpcpp/client_context.h" + +#include "Client.h" +#include "ClientManager.h" +#include "Protocol.h" +#include "ReceiveMessageContext.h" +#include "ReceiveMessageResult.h" +#include "rocketmq/ErrorCode.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ReceiveMessageStreamReader : public grpc::ClientReadReactor { +public: + ReceiveMessageStreamReader(std::weak_ptr client_manager, + rmq::MessagingService::Stub* stub, + std::string peer_address, + rmq::ReceiveMessageRequest request, + std::unique_ptr context); + + void OnReadDone(bool ok) override; + + void OnDone(const grpc::Status& s) override; + +private: + std::weak_ptr client_manager_; + rmq::MessagingService::Stub* stub_; + std::string peer_address_; + rmq::ReceiveMessageRequest request_; + std::unique_ptr context_; + rmq::ReceiveMessageResponse response_; + grpc::ClientContext client_context_; + grpc::Status status_; + std::error_code ec_; + ReceiveMessageResult result_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/RpcClient.h b/src/main/cpp/client/include/RpcClient.h index add9931c9..2fc8448ae 100644 --- a/src/main/cpp/client/include/RpcClient.h +++ b/src/main/cpp/client/include/RpcClient.h @@ -21,55 +21,24 @@ #include #include +#include "ReceiveMessageResult.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" -#include "apache/rocketmq/v1/definition.grpc.pb.h" -#include "apache/rocketmq/v1/service.grpc.pb.h" -#include "apache/rocketmq/v1/service.pb.h" #include "grpcpp/grpcpp.h" #include "InvocationContext.h" -#include "OrphanTransactionCallback.h" +#include "Protocol.h" +#include "ReceiveMessageContext.h" ROCKETMQ_NAMESPACE_BEGIN -namespace rmq = apache::rocketmq::v1; - using Channel = grpc::Channel; using Status = grpc::Status; using CompletionQueue = grpc::CompletionQueue; -using QueryRouteRequest = rmq::QueryRouteRequest; -using QueryRouteResponse = rmq::QueryRouteResponse; -using SendMessageRequest = rmq::SendMessageRequest; -using SendMessageResponse = rmq::SendMessageResponse; -using QueryAssignmentRequest = rmq::QueryAssignmentRequest; -using QueryAssignmentResponse = rmq::QueryAssignmentResponse; -using ReceiveMessageRequest = rmq::ReceiveMessageRequest; -using ReceiveMessageResponse = rmq::ReceiveMessageResponse; -using AckMessageRequest = rmq::AckMessageRequest; -using AckMessageResponse = rmq::AckMessageResponse; -using NackMessageRequest = rmq::NackMessageRequest; -using NackMessageResponse = rmq::NackMessageResponse; -using HeartbeatRequest = rmq::HeartbeatRequest; -using HeartbeatResponse = rmq::HeartbeatResponse; -using HealthCheckRequest = rmq::HealthCheckRequest; -using HealthCheckResponse = rmq::HealthCheckResponse; -using EndTransactionRequest = rmq::EndTransactionRequest; -using EndTransactionResponse = rmq::EndTransactionResponse; -using QueryOffsetRequest = rmq::QueryOffsetRequest; -using QueryOffsetResponse = rmq::QueryOffsetResponse; -using PullMessageRequest = rmq::PullMessageRequest; -using PullMessageResponse = rmq::PullMessageResponse; -using PollCommandRequest = rmq::PollCommandRequest; -using PollCommandResponse = rmq::PollCommandResponse; -using ReportThreadStackTraceRequest = rmq::ReportThreadStackTraceRequest; -using ReportThreadStackTraceResponse = rmq::ReportThreadStackTraceResponse; -using ReportMessageConsumptionResultRequest = rmq::ReportMessageConsumptionResultRequest; -using ReportMessageConsumptionResultResponse = rmq::ReportMessageConsumptionResultResponse; -using ForwardMessageToDeadLetterQueueRequest = rmq::ForwardMessageToDeadLetterQueueRequest; -using ForwardMessageToDeadLetterQueueResponse = rmq::ForwardMessageToDeadLetterQueueResponse; -using NotifyClientTerminationRequest = rmq::NotifyClientTerminationRequest; -using NotifyClientTerminationResponse = rmq::NotifyClientTerminationResponse; + +class Client; +class ClientManager; +class TelemetryBidiReactor; /** * @brief A RpcClient represents a session between client and a remote broker. @@ -81,6 +50,8 @@ class RpcClient { virtual ~RpcClient() = default; + virtual const std::string& remoteAddress() const = 0; + virtual void asyncQueryRoute(const QueryRouteRequest& request, InvocationContext* invocation_context) = 0; @@ -90,51 +61,32 @@ class RpcClient { virtual void asyncQueryAssignment(const QueryAssignmentRequest& request, InvocationContext* invocation_context) = 0; - virtual std::shared_ptr& completionQueue() = 0; - - virtual void asyncReceive(const ReceiveMessageRequest& request, - InvocationContext* invocation_context) = 0; + virtual void asyncReceive(const ReceiveMessageRequest& request, std::unique_ptr context) = 0; virtual void asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) = 0; - virtual void asyncNack(const NackMessageRequest& request, - InvocationContext* invocation_context) = 0; + virtual void asyncChangeInvisibleDuration(const ChangeInvisibleDurationRequest& request, + InvocationContext*) = 0; virtual void asyncHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) = 0; - virtual void asyncHealthCheck(const HealthCheckRequest& request, - InvocationContext* invocation_context) = 0; - virtual void asyncEndTransaction(const EndTransactionRequest& request, InvocationContext* invocation_context) = 0; - virtual void asyncPollCommand(const PollCommandRequest& request, - InvocationContext* invocation_context) = 0; - - virtual void asyncQueryOffset(const QueryOffsetRequest& request, - InvocationContext* invocation_context) = 0; - - virtual void asyncPull(const PullMessageRequest& request, - InvocationContext* invocation_context) = 0; + virtual std::shared_ptr asyncTelemetry(std::weak_ptr client) = 0; virtual void asyncForwardMessageToDeadLetterQueue( const ForwardMessageToDeadLetterQueueRequest& request, InvocationContext* invocation_context) = 0; - virtual grpc::Status reportThreadStackTrace(grpc::ClientContext* context, - const ReportThreadStackTraceRequest& request, - ReportThreadStackTraceResponse* response) = 0; - - virtual grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, - const ReportMessageConsumptionResultRequest& request, - ReportMessageConsumptionResultResponse* response) = 0; - virtual grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, NotifyClientTerminationResponse* response) = 0; + virtual std::weak_ptr clientManager() = 0; + /** * Indicate if heartbeat is required. * @return true if periodic heartbeat is required; false otherwise. diff --git a/src/main/cpp/client/include/RpcClientImpl.h b/src/main/cpp/client/include/RpcClientImpl.h index 80a397241..6406c746e 100644 --- a/src/main/cpp/client/include/RpcClientImpl.h +++ b/src/main/cpp/client/include/RpcClientImpl.h @@ -18,17 +18,22 @@ #include +#include "InvocationContext.h" +#include "ReceiveMessageCallback.h" +#include "ReceiveMessageContext.h" #include "absl/container/flat_hash_map.h" +#include "Client.h" +#include "ClientManager.h" #include "RpcClient.h" ROCKETMQ_NAMESPACE_BEGIN class RpcClientImpl : public RpcClient, public std::enable_shared_from_this { public: - RpcClientImpl(std::shared_ptr completion_queue, std::shared_ptr channel, + RpcClientImpl(std::weak_ptr client_manager, std::shared_ptr channel, std::string peer_address, bool need_heartbeat = true) - : completion_queue_(std::move(completion_queue)), channel_(std::move(channel)), + : client_manager_(client_manager), channel_(std::move(channel)), peer_address_(std::move(peer_address)), stub_(rmq::MessagingService::NewStub(channel_)), need_heartbeat_(need_heartbeat) { } @@ -38,6 +43,10 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; @@ -47,48 +56,30 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this* invocation_context) override; - std::shared_ptr& completionQueue() override; - - void asyncReceive(const ReceiveMessageRequest& request, - InvocationContext* invocation_context) override; + void asyncReceive(const ReceiveMessageRequest& request, std::unique_ptr context) override; void asyncAck(const AckMessageRequest& request, InvocationContext* invocation_context) override; - void asyncNack(const NackMessageRequest& request, - InvocationContext* invocation_context) override; + void asyncChangeInvisibleDuration(const ChangeInvisibleDurationRequest& request, + InvocationContext*) override; void asyncHeartbeat(const HeartbeatRequest& request, InvocationContext* invocation_context) override; - void asyncHealthCheck(const HealthCheckRequest& request, - InvocationContext* invocation_context) override; - void asyncEndTransaction(const EndTransactionRequest& request, InvocationContext* invocation_context) override; - void asyncPollCommand(const PollCommandRequest& request, - InvocationContext* invocation_context) override; - - void asyncQueryOffset(const QueryOffsetRequest& request, - InvocationContext* invocation_context) override; - - void asyncPull(const PullMessageRequest& request, - InvocationContext* invocation_context) override; + std::shared_ptr asyncTelemetry(std::weak_ptr client) override; void asyncForwardMessageToDeadLetterQueue( const ForwardMessageToDeadLetterQueueRequest& request, InvocationContext* invocation_context) override; - grpc::Status reportThreadStackTrace(grpc::ClientContext* context, const ReportThreadStackTraceRequest& request, - ReportThreadStackTraceResponse* response) override; - - grpc::Status reportMessageConsumptionResult(grpc::ClientContext* context, - const ReportMessageConsumptionResultRequest& request, - ReportMessageConsumptionResultResponse* response) override; - grpc::Status notifyClientTermination(grpc::ClientContext* context, const NotifyClientTerminationRequest& request, NotifyClientTerminationResponse* response) override; + std::weak_ptr clientManager() override; + bool needHeartbeat() override; void needHeartbeat(bool need_heartbeat) override; @@ -98,8 +89,12 @@ class RpcClientImpl : public RpcClient, public std::enable_shared_from_this& metadata); - std::shared_ptr completion_queue_; + static void asyncCallback(std::weak_ptr client, BaseInvocationContext* invocation_context, + grpc::Status status); + + std::weak_ptr client_manager_; std::shared_ptr channel_; + std::string peer_address_; std::unique_ptr stub_; std::chrono::milliseconds connect_timeout_{3000}; bool need_heartbeat_{true}; diff --git a/src/main/cpp/client/include/SendMessageContext.h b/src/main/cpp/client/include/SendMessageContext.h index fa1660921..12ee91b79 100644 --- a/src/main/cpp/client/include/SendMessageContext.h +++ b/src/main/cpp/client/include/SendMessageContext.h @@ -18,8 +18,8 @@ #include -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" +#include "Protocol.h" +#include "rocketmq/Message.h" ROCKETMQ_NAMESPACE_BEGIN class SendMessageContext { @@ -30,16 +30,16 @@ class SendMessageContext { void setProducerGroup(const std::string& producer_group) { this->producer_group_ = producer_group; }; - const MQMessage& getMessage() const { + const Message& getMessage() const { return message_; } - void setMessage(const MQMessage& message) { + void setMessage(const Message& message) { this->message_ = message; } - const MQMessageQueue& getMessageQueue() const { + const rmq::MessageQueue& getMessageQueue() const { return message_queue_; } - void setMessageQueue(const MQMessageQueue& message_queue) { + void setMessageQueue(const rmq::MessageQueue& message_queue) { this->message_queue_ = message_queue; } const std::string& getBornHost() const { @@ -63,8 +63,8 @@ class SendMessageContext { private: std::string producer_group_; - MQMessage message_; - MQMessageQueue message_queue_; + Message message_; + rmq::MessageQueue message_queue_; std::string born_host_; std::string message_id_; long long queue_offset_{-1}; diff --git a/src/main/cpp/client/include/ServiceAddress.h b/src/main/cpp/client/include/ServiceAddress.h deleted file mode 100644 index b5b8c7fe7..000000000 --- a/src/main/cpp/client/include/ServiceAddress.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include - -#include "absl/strings/string_view.h" - -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -enum AddressScheme : int8_t -{ - IPv4 = 0, - IPv6 = 1, - DOMAIN_NAME = 2, -}; - -class Address { -public: - Address(absl::string_view host, int32_t port) : host_(host.data(), host.length()), port_(port) { - } - - bool operator==(const Address& other) const { - return host_ == other.host_ && port_ == other.port_; - } - - bool operator<(const Address& other) const { - if (host_ < other.host_) { - return true; - } else if (host_ > other.host_) { - return false; - } - - if (port_ < other.port_) { - return true; - } else if (port_ > other.port_) { - return false; - } - - return false; - } - - const std::string& host() const { - return host_; - } - - int32_t port() const { - return port_; - } - -private: - std::string host_; - int32_t port_; -}; - -/** - * Thread Safety Analysis: - * Once initialized, this class remains immutable. Thus it is threads-safe. - */ -class ServiceAddress { -public: - ServiceAddress(AddressScheme scheme, std::vector
addresses) - : scheme_(scheme), addresses_(std::move(addresses)) { - std::sort(addresses_.begin(), addresses_.end()); - } - - AddressScheme scheme() const { - return scheme_; - } - - explicit operator bool() const { - return !addresses_.empty(); - } - - std::string address() const { - std::string result; - switch (scheme_) { - case AddressScheme::IPv4: - result.append("ipv4:"); - break; - case AddressScheme::IPv6: - result.append("ipv6:"); - break; - case AddressScheme::DOMAIN_NAME: - result.append("dns:"); - break; - } - - bool first = true; - for (const auto& item : addresses_) { - if (!first) { - result.append(","); - } else { - first = false; - } - result.append(item.host()).append(":").append(std::to_string(item.port())); - } - return result; - } - -private: - AddressScheme scheme_; - std::vector
addresses_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/base/include/DigestType.h b/src/main/cpp/client/include/Session.h similarity index 91% rename from src/main/cpp/base/include/DigestType.h rename to src/main/cpp/client/include/Session.h index e255d828a..3c32ca58a 100644 --- a/src/main/cpp/base/include/DigestType.h +++ b/src/main/cpp/client/include/Session.h @@ -16,17 +16,15 @@ */ #pragma once -#include - #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN -enum class DigestType : int8_t -{ - CRC32 = 0, - MD5 = 1, - SHA1 = 2, +class Session { +public: + virtual ~Session() = default; + + virtual bool await() = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/api/rocketmq/LocalTransactionStateChecker.h b/src/main/cpp/client/include/SessionImpl.h similarity index 66% rename from api/rocketmq/LocalTransactionStateChecker.h rename to src/main/cpp/client/include/SessionImpl.h index 59d788120..3ab381d16 100644 --- a/api/rocketmq/LocalTransactionStateChecker.h +++ b/src/main/cpp/client/include/SessionImpl.h @@ -18,18 +18,28 @@ #include -#include "MQMessageExt.h" -#include "Transaction.h" +#include "Client.h" +#include "RpcClient.h" +#include "Session.h" +#include "TelemetryBidiReactor.h" ROCKETMQ_NAMESPACE_BEGIN -class LocalTransactionStateChecker { +class SessionImpl : public Session { public: - virtual ~LocalTransactionStateChecker() = default; + SessionImpl(std::weak_ptr client, std::shared_ptr rpc_client); - virtual TransactionState checkLocalTransactionState(const MQMessageExt& message) = 0; -}; + ~SessionImpl() override; + + bool await() override; -using LocalTransactionStateCheckerPtr = std::unique_ptr; +private: + std::weak_ptr client_; + std::shared_ptr rpc_client_; + // TODO: use unique_ptr + std::shared_ptr telemetry_; + + void syncSettings(); +}; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/Signature.h b/src/main/cpp/client/include/Signature.h index b10581c0c..29d5f331c 100644 --- a/src/main/cpp/client/include/Signature.h +++ b/src/main/cpp/client/include/Signature.h @@ -24,7 +24,7 @@ ROCKETMQ_NAMESPACE_BEGIN class Signature { public: - static void sign(ClientConfig* client, absl::flat_hash_map& metadata); + static void sign(const ClientConfig& client, absl::flat_hash_map& metadata); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TelemetryBidiReactor.h b/src/main/cpp/client/include/TelemetryBidiReactor.h new file mode 100644 index 000000000..9fe65f31e --- /dev/null +++ b/src/main/cpp/client/include/TelemetryBidiReactor.h @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Client.h" +#include "RpcClient.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +enum class StreamState : std::uint8_t +{ + Created = 0, + Active = 1, + ReadDone = 2, + WriteDone = 3, + Closed = 4, +}; + +class TelemetryBidiReactor : public grpc::ClientBidiReactor, + public std::enable_shared_from_this { +public: + TelemetryBidiReactor(std::weak_ptr client, rmq::MessagingService::Stub* stub, std::string peer_address); + + ~TelemetryBidiReactor(); + + void OnWriteDone(bool ok) override; + + void OnWritesDoneDone(bool ok) override; + + void OnReadDone(bool ok) override; + + void OnDone(const grpc::Status& status) override; + + void fireRead(); + + void fireWrite(); + + void fireClose(); + + void write(TelemetryCommand command); + + bool await(); + +private: + grpc::ClientContext context_; + + /** + * @brief Command to read from server. + */ + TelemetryCommand read_; + + /** + * @brief Buffered commands to write to server + */ + std::vector writes_ GUARDED_BY(writes_mtx_); + absl::Mutex writes_mtx_; + + /** + * @brief The command that is currently being written back to server. + */ + TelemetryCommand write_; + + /** + * @brief Each TelemetryBidiReactor belongs to a specific client as its owner. + */ + std::weak_ptr client_; + + /** + * @brief Address of remote peer. + */ + std::string peer_address_; + + /** + * @brief Indicate if there is a command being written to network. + */ + std::atomic_bool command_inflight_{false}; + + StreamState stream_state_ GUARDED_BY(stream_state_mtx_); + absl::Mutex stream_state_mtx_; + absl::CondVar stream_state_cv_; + + bool server_setting_received_ GUARDED_BY(server_setting_received_mtx_){false}; + absl::Mutex server_setting_received_mtx_; + absl::CondVar server_setting_received_cv_; + + void onVerifyMessageResult(TelemetryCommand command); + + void applySettings(const rmq::Settings& settings); + + void applyBackoffPolicy(const rmq::Settings& settings, std::shared_ptr& client); + + void applyPublishingConfig(const rmq::Settings& settings, std::shared_ptr client); + + void applySubscriptionConfig(const rmq::Settings& settings, std::shared_ptr client); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TlsHelper.h b/src/main/cpp/client/include/TlsHelper.h index b96f4d6ac..b906ce97e 100644 --- a/src/main/cpp/client/include/TlsHelper.h +++ b/src/main/cpp/client/include/TlsHelper.h @@ -16,16 +16,10 @@ */ #pragma once -#include "rocketmq/RocketMQ.h" #include -#include - -#include "grpcpp/security/tls_credentials_options.h" -#include -#include -#include -#include "spdlog/spdlog.h" +#include "openssl/x509.h" +#include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -33,29 +27,6 @@ class TlsHelper { public: static std::string sign(const std::string& access_secret, const std::string& content); - - static const char* CA; - - static const char* client_certificate_chain; - - static const char* client_private_key; -}; - -class TlsServerAuthorizationChecker : public grpc::experimental::TlsServerAuthorizationCheckInterface { -public: - int Schedule(grpc::experimental::TlsServerAuthorizationCheckArg* arg) override { - if (nullptr == arg) { - return 0; - } - - arg->set_success(1); - arg->set_status(GRPC_STATUS_OK); - return 0; - } - - ~TlsServerAuthorizationChecker() override { - SPDLOG_DEBUG("~TlsServerAuthorizationChecker() invoked"); - } }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicAssignmentInfo.h b/src/main/cpp/client/include/TopicAssignmentInfo.h index 7d522fd96..76777a950 100644 --- a/src/main/cpp/client/include/TopicAssignmentInfo.h +++ b/src/main/cpp/client/include/TopicAssignmentInfo.h @@ -16,40 +16,29 @@ */ #pragma once +#include #include #include -#include "Assignment.h" -#include "RpcClient.h" +#include "Protocol.h" ROCKETMQ_NAMESPACE_BEGIN class TopicAssignment { public: - explicit TopicAssignment(std::vector&& assignments) : assignment_list_(std::move(assignments)) { - std::sort(assignment_list_.begin(), assignment_list_.end()); + explicit TopicAssignment(std::vector&& assignments) : assignment_list_(std::move(assignments)) { + std::sort(assignment_list_.begin(), assignment_list_.end(), + [](const rmq::Assignment& lhs, const rmq::Assignment& rhs) { return lhs < rhs; }); } explicit TopicAssignment(const QueryAssignmentResponse& response); ~TopicAssignment() = default; - const std::vector& assignmentList() const { + const std::vector& assignmentList() const { return assignment_list_; } - bool operator==(const TopicAssignment& rhs) const { - return assignment_list_ == rhs.assignment_list_; - } - - bool operator!=(const TopicAssignment& rhs) const { - return assignment_list_ != rhs.assignment_list_; - } - - const std::string& debugString() const { - return debug_string_; - } - static unsigned int getAndIncreaseQueryWhichBroker() { return ++query_which_broker_; } @@ -58,13 +47,13 @@ class TopicAssignment { /** * Once it is set, it will be immutable. */ - std::vector assignment_list_; - - std::string debug_string_; + std::vector assignment_list_; thread_local static uint32_t query_which_broker_; }; +bool operator==(const TopicAssignment& lhs, const TopicAssignment& rhs); + using TopicAssignmentPtr = std::shared_ptr; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicRouteData.h b/src/main/cpp/client/include/TopicRouteData.h index f151d0058..807ac811b 100644 --- a/src/main/cpp/client/include/TopicRouteData.h +++ b/src/main/cpp/client/include/TopicRouteData.h @@ -17,46 +17,51 @@ #pragma once #include +#include +#include #include -#include "Partition.h" -#include "RpcClient.h" +#include "absl/strings/str_join.h" -ROCKETMQ_NAMESPACE_BEGIN +#include "Protocol.h" -namespace rmq = apache::rocketmq::v1; +ROCKETMQ_NAMESPACE_BEGIN /** * Thread Safety: This class is immutable and thus effectively thread safe. */ class TopicRouteData { public: - TopicRouteData(std::vector partitions, std::string debug_string) - : partitions_(std::move(partitions)), debug_string_(std::move(debug_string)) { - std::sort(partitions_.begin(), partitions_.end()); + TopicRouteData(std::vector message_queues) : message_queues_(message_queues) { + std::sort(message_queues_.begin(), message_queues_.end(), + [](const rmq::MessageQueue& lhs, const rmq::MessageQueue& rhs) { return lhs < rhs; }); } - const std::vector& partitions() const { - return partitions_; + const std::vector& messageQueues() const { + return message_queues_; } - const std::string& debugString() const { - return debug_string_; - } + std::string debugString() const { + return absl::StrJoin(message_queues_.begin(), message_queues_.end(), ",", + [](std::string* out, const rmq::MessageQueue& m) { out->append(m.DebugString()); }); + }; - bool operator==(const TopicRouteData& other) const { - return partitions_ == other.partitions_; - } +private: + std::vector message_queues_; - bool operator!=(const TopicRouteData& other) const { - return !this->operator==(other); - } + friend bool operator==(const TopicRouteData&, const TopicRouteData&); -private: - std::vector partitions_; - std::string debug_string_; + friend bool operator!=(const TopicRouteData&, const TopicRouteData&); }; +inline bool operator==(const TopicRouteData& lhs, const TopicRouteData& rhs) { + return lhs.message_queues_ == rhs.message_queues_; +} + +inline bool operator!=(const TopicRouteData& lhs, const TopicRouteData& rhs) { + return lhs.message_queues_ != rhs.message_queues_; +} + using TopicRouteDataPtr = std::shared_ptr; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/RpcClientMock.cpp b/src/main/cpp/client/mocks/RpcClientMock.cpp deleted file mode 100644 index f2fc94ac9..000000000 --- a/src/main/cpp/client/mocks/RpcClientMock.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "RpcClientMock.h" - -using namespace testing; - -ROCKETMQ_NAMESPACE_BEGIN - -/** - * TODO: Add commonly used mock operation for RpcClient - */ -RpcClientMock::RpcClientMock() : completion_queue_(std::make_shared()) { - ON_CALL(*this, completionQueue()).WillByDefault(ReturnRef(completion_queue_)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientConfigMock.h b/src/main/cpp/client/mocks/include/ClientConfigMock.h deleted file mode 100644 index 115e0bf0c..000000000 --- a/src/main/cpp/client/mocks/include/ClientConfigMock.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "ClientConfig.h" -#include "gmock/gmock.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ClientConfigMock : virtual public ClientConfig { -public: - ~ClientConfigMock() override = default; - MOCK_METHOD(const std::string&, region, (), (const override)); - MOCK_METHOD(const std::string&, serviceName, (), (const override)); - MOCK_METHOD(const std::string&, resourceNamespace, (), (const overide)); - MOCK_METHOD(CredentialsProviderPtr, credentialsProvider, (), (override)); - MOCK_METHOD(const std::string&, tenantId, (), (const override)); - MOCK_METHOD(absl::Duration, getIoTimeout, (), (const override)); - MOCK_METHOD(absl::Duration, getLongPollingTimeout, (), (const override)); - MOCK_METHOD(const std::string&, getGroupName, (), (const override)); - MOCK_METHOD(std::string, clientId, (), (const override)); - MOCK_METHOD(bool, isTracingEnabled, (), (const override)); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/mocks/include/ClientManagerMock.h b/src/main/cpp/client/mocks/include/ClientManagerMock.h index 01d6d41b3..d255b901b 100644 --- a/src/main/cpp/client/mocks/include/ClientManagerMock.h +++ b/src/main/cpp/client/mocks/include/ClientManagerMock.h @@ -16,11 +16,15 @@ */ #pragma once -#include "ClientManager.h" -#include "gmock/gmock.h" #include +#include #include +#include "gmock/gmock.h" + +#include "ClientManager.h" +#include "RpcClient.h" + ROCKETMQ_NAMESPACE_BEGIN class ClientManagerMock : public ClientManager { @@ -43,20 +47,18 @@ class ClientManagerMock : public ClientManager { (const std::function&)), (override)); - MOCK_METHOD(void, pollCommand, - (const std::string&, const Metadata&, const PollCommandRequest&, std::chrono::milliseconds, - const std::function*)>&), + MOCK_METHOD(std::shared_ptr, telemetry, (const std::string&, std::weak_ptr), (override)); - MOCK_METHOD(bool, wrapMessage, (const rmq::Message&, MQMessageExt&), (override)); + MOCK_METHOD(bool, wrapMessage, (const rmq::Message&, MessageExt&), (override)); MOCK_METHOD(void, ack, (const std::string&, const Metadata&, const AckMessageRequest&, std::chrono::milliseconds, (const std::function&)), (override)); - MOCK_METHOD(void, nack, - (const std::string&, const Metadata&, const NackMessageRequest&, std::chrono::milliseconds, + MOCK_METHOD(void, changeInvisibleDuration, + (const std::string&, const Metadata&, const ChangeInvisibleDurationRequest&, std::chrono::milliseconds, (const std::function&)), (override)); @@ -71,16 +73,6 @@ class ClientManagerMock : public ClientManager { (const std::function&)), (override)); - MOCK_METHOD(void, queryOffset, - (const std::string&, const Metadata&, const QueryOffsetRequest&, std::chrono::milliseconds, - (const std::function&)), - (override)); - - MOCK_METHOD(void, healthCheck, - (const std::string&, const Metadata&, const HealthCheckRequest&, std::chrono::milliseconds, - (const std::function*)>&)), - (override)); - MOCK_METHOD(void, addClientObserver, (std::weak_ptr), (override)); MOCK_METHOD(void, queryAssignment, @@ -90,29 +82,15 @@ class ClientManagerMock : public ClientManager { MOCK_METHOD(void, receiveMessage, (const std::string&, const Metadata&, const ReceiveMessageRequest&, std::chrono::milliseconds, - (const std::shared_ptr&)), + ReceiveMessageCallback), (override)); - MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback*), (override)); - - MOCK_METHOD(void, pullMessage, - (const std::string&, const Metadata&, const PullMessageRequest&, std::chrono::milliseconds, - (const std::function&)), - (override)); + MOCK_METHOD(bool, send, (const std::string&, const Metadata&, SendMessageRequest&, SendCallback), (override)); MOCK_METHOD(std::error_code, notifyClientTermination, (const std::string&, const Metadata&, const NotifyClientTerminationRequest&, std::chrono::milliseconds), (override)); - MOCK_METHOD(std::error_code, reportThreadStackTrace, - (const std::string&, const Metadata&, const ReportThreadStackTraceRequest&, std::chrono::milliseconds), - (override)); - - MOCK_METHOD(std::error_code, reportMessageConsumptionResult, - (const std::string&, const Metadata&, const ReportMessageConsumptionResultRequest&, - std::chrono::milliseconds), - (override)); - MOCK_METHOD(State, state, (), (const override)); MOCK_METHOD(void, submit, (std::function), (override)); diff --git a/src/main/cpp/client/mocks/include/ClientMock.h b/src/main/cpp/client/mocks/include/ClientMock.h index d7fbf782d..b88e3c819 100644 --- a/src/main/cpp/client/mocks/include/ClientMock.h +++ b/src/main/cpp/client/mocks/include/ClientMock.h @@ -17,11 +17,11 @@ #pragma once #include "Client.h" -#include "ClientConfigMock.h" +#include "RpcClient.h" ROCKETMQ_NAMESPACE_BEGIN -class ClientMock : virtual public Client, virtual public ClientConfigMock { +class ClientMock : virtual public Client { public: MOCK_METHOD(void, endpointsInUse, (absl::flat_hash_set&), (override)); @@ -31,12 +31,16 @@ class ClientMock : virtual public Client, virtual public ClientConfigMock { MOCK_METHOD(void, onRemoteEndpointRemoval, (const std::vector&), (override)); - MOCK_METHOD(void, healthCheck, (), (override)); - MOCK_METHOD(void, schedule, (const std::string&, const std::function&, std::chrono::milliseconds), (override)); + MOCK_METHOD(void, createSession, (const std::string&), (override)); + MOCK_METHOD(void, notifyClientTermination, (), (override)); + + MOCK_METHOD(void, verify, (VerifyMessageCommand, (std::function)), (override)); + + MOCK_METHOD(void, recoverOrphanedTransaction, (const RecoverOrphanedTransactionCommand&), (override)); }; ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/client/mocks/include/RpcClientMock.h b/src/main/cpp/client/mocks/include/RpcClientMock.h index 2cde10fd9..a84759763 100644 --- a/src/main/cpp/client/mocks/include/RpcClientMock.h +++ b/src/main/cpp/client/mocks/include/RpcClientMock.h @@ -16,21 +16,27 @@ */ #pragma once +#include +#include + +#include "gmock/gmock.h" + +#include "Client.h" #include "InvocationContext.h" #include "RpcClient.h" -#include "gmock/gmock.h" -#include ROCKETMQ_NAMESPACE_BEGIN class RpcClientMock : public RpcClient { public: - RpcClientMock(); + RpcClientMock() = default; ~RpcClientMock() override { std::cout << "~RpcClientMock()" << std::endl; } + MOCK_METHOD(const std::string&, remoteAddress, (), (const override)); + MOCK_METHOD(void, asyncQueryRoute, (const QueryRouteRequest&, InvocationContext*), (override)); MOCK_METHOD(void, asyncSend, (const SendMessageRequest&, InvocationContext*), (override)); @@ -38,54 +44,36 @@ class RpcClientMock : public RpcClient { MOCK_METHOD(void, asyncQueryAssignment, (const QueryAssignmentRequest&, InvocationContext*), (override)); - MOCK_METHOD(std::shared_ptr&, completionQueue, (), (override)); - - MOCK_METHOD(void, asyncReceive, (const ReceiveMessageRequest&, InvocationContext*), - (override)); + MOCK_METHOD(void, asyncReceive, (const ReceiveMessageRequest&, (std::unique_ptr)), (override)); MOCK_METHOD(void, asyncAck, (const AckMessageRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncNack, (const NackMessageRequest&, InvocationContext*), (override)); + MOCK_METHOD(void, asyncChangeInvisibleDuration, + (const ChangeInvisibleDurationRequest&, InvocationContext*), (override)); MOCK_METHOD(void, asyncHeartbeat, (const HeartbeatRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncHealthCheck, (const HealthCheckRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncEndTransaction, (const EndTransactionRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncQueryOffset, (const QueryOffsetRequest&, InvocationContext*), (override)); - - MOCK_METHOD(void, asyncPull, (const PullMessageRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncForwardMessageToDeadLetterQueue, (const ForwardMessageToDeadLetterQueueRequest&, InvocationContext*), (override)); - MOCK_METHOD(void, asyncPollCommand, (const PollCommandRequest&, InvocationContext*), (override)); - - MOCK_METHOD(grpc::Status, reportThreadStackTrace, - (grpc::ClientContext*, const ReportThreadStackTraceRequest&, ReportThreadStackTraceResponse*), - (override)); - - MOCK_METHOD(grpc::Status, reportMessageConsumptionResult, - (grpc::ClientContext*, const ReportMessageConsumptionResultRequest&, - ReportMessageConsumptionResultResponse*), - (override)); + MOCK_METHOD(std::shared_ptr, asyncTelemetry, (std::weak_ptr), (override)); MOCK_METHOD(grpc::Status, notifyClientTermination, (grpc::ClientContext*, const NotifyClientTerminationRequest&, NotifyClientTerminationResponse* response), (override)); + MOCK_METHOD(std::weak_ptr, clientManager, (), (override)); + MOCK_METHOD(bool, needHeartbeat, (), (override)); MOCK_METHOD(void, needHeartbeat, (bool), (override)); MOCK_METHOD(bool, ok, (), (const override)); - -protected: - std::shared_ptr completion_queue_; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index c3429ab8c..a9df915ae 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -21,28 +21,23 @@ #include "ClientManagerImpl.h" #include "ConsumeMessageType.h" #include "LoggerImpl.h" -#include "PushConsumer.h" +#include "ProcessQueue.h" +#include "PushConsumerImpl.h" ROCKETMQ_NAMESPACE_BEGIN -AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue) +AsyncReceiveMessageCallback::AsyncReceiveMessageCallback(std::weak_ptr process_queue) : process_queue_(std::move(process_queue)) { receive_message_later_ = std::bind(&AsyncReceiveMessageCallback::checkThrottleThenReceive, this); } void AsyncReceiveMessageCallback::onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) { - ProcessQueueSharedPtr process_queue = process_queue_.lock(); + std::shared_ptr process_queue = process_queue_.lock(); if (!process_queue) { SPDLOG_INFO("Process queue has been destructed."); return; } - std::shared_ptr impl = process_queue->getConsumer().lock(); - if (!impl->active()) { - SPDLOG_INFO("Consumer is not active any more. It should be quitting"); - return; - } - auto consumer = process_queue->getConsumer().lock(); if (!consumer) { return; @@ -57,7 +52,7 @@ void AsyncReceiveMessageCallback::onCompletion(const std::error_code& ec, const SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", result.source_host, result.messages.size(), process_queue->simpleName()); process_queue->cacheMessages(result.messages); - impl->getConsumeMessageService()->signalDispatcher(); + consumer->getConsumeMessageService()->signalDispatcher(); checkThrottleThenReceive(); } @@ -102,19 +97,19 @@ void AsyncReceiveMessageCallback::receiveMessageLater() { } void AsyncReceiveMessageCallback::receiveMessageImmediately() { - ProcessQueueSharedPtr process_queue_shared_ptr = process_queue_.lock(); + auto process_queue_shared_ptr = process_queue_.lock(); if (!process_queue_shared_ptr) { SPDLOG_INFO("ProcessQueue has been released. Ignore further receive message request-response cycles"); return; } - std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); + std::shared_ptr impl = process_queue_shared_ptr->getConsumer().lock(); if (!impl) { SPDLOG_INFO("Owner of ProcessQueue[{}] has been released. Ignore further receive message request-response cycles", process_queue_shared_ptr->simpleName()); return; } - impl->receiveMessage(process_queue_shared_ptr->getMQMessageQueue(), process_queue_shared_ptr->getFilterExpression()); + impl->receiveMessage(process_queue_shared_ptr->messageQueue(), process_queue_shared_ptr->getFilterExpression()); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/BUILD.bazel b/src/main/cpp/rocketmq/BUILD.bazel index 6d6dd7296..b37957d3f 100644 --- a/src/main/cpp/rocketmq/BUILD.bazel +++ b/src/main/cpp/rocketmq/BUILD.bazel @@ -25,8 +25,8 @@ cc_library( strip_include_prefix = "//src/main/cpp/rocketmq/include", deps = [ "//src/main/cpp/client:client_library", - "//src/main/cpp/tracing/exporters:otlp_exporter", - "//src/main/cpp/tracing:tracing_utility", + "//src/main/cpp/stats", + "//src/main/cpp/trace", "//src/main/cpp/log:log_library", "//src/main/cpp/admin:admin_server_library", "@com_google_absl//absl/types:optional", diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp index 960296909..8275922cd 100644 --- a/src/main/cpp/rocketmq/ClientImpl.cpp +++ b/src/main/cpp/rocketmq/ClientImpl.cpp @@ -14,11 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "ClientImpl.h" + #include #include #include #include #include +#include #include #include #include @@ -26,26 +29,67 @@ #include #include -#include "RpcClient.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "google/rpc/code.pb.h" - -#include "ClientImpl.h" #include "ClientManagerFactory.h" #include "HttpClientImpl.h" #include "InvocationContext.h" #include "LoggerImpl.h" -#include "MessageAccessor.h" +#include "MessageExt.h" #include "NamingScheme.h" +#include "SessionImpl.h" #include "Signature.h" -#include "rocketmq/MQMessageExt.h" +#include "UtilAll.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "rocketmq/Message.h" #include "rocketmq/MessageListener.h" ROCKETMQ_NAMESPACE_BEGIN -ClientImpl::ClientImpl(absl::string_view group_name) : ClientConfigImpl(group_name), state_(State::CREATED) { +ClientImpl::ClientImpl(absl::string_view group_name) : state_(State::CREATED) { + client_config_.subscriber.group.set_name(std::string(group_name.data(), group_name.length())); +} + +rmq::Endpoints ClientImpl::accessPoint() { + std::string endpoints = name_server_resolver_->resolve(); + rmq::Endpoints access_point; + + absl::string_view host_port; + if (absl::StartsWith(endpoints, NamingScheme::IPv4Prefix)) { + access_point.set_scheme(rmq::AddressScheme::IPv4); + host_port = absl::StripPrefix(endpoints, NamingScheme::IPv4Prefix); + } else if (absl::StartsWith(endpoints, NamingScheme::IPv6Prefix)) { + access_point.set_scheme(rmq::AddressScheme::IPv6); + host_port = absl::StripPrefix(endpoints, NamingScheme::IPv6Prefix); + } else { + access_point.set_scheme(rmq::AddressScheme::DOMAIN_NAME); + host_port = absl::StripPrefix(endpoints, NamingScheme::DnsPrefix); + } + + std::vector pairs = absl::StrSplit(host_port, ';', absl::SkipWhitespace()); + // Now endpoint is in form of host:port + for (auto& endpoint : pairs) { + std::reverse(endpoint.begin(), endpoint.end()); + std::vector segments = absl::StrSplit(endpoint, absl::MaxSplits(':', 1)); + for (auto& segment : segments) { + std::reverse(segment.begin(), segment.end()); + } + if (segments.size() != 2) { + continue; + } + + std::int32_t port; + if (!absl::SimpleAtoi(segments[0], &port)) { + // Failed to parse port + continue; + } + + auto addr = new rmq::Address(); + addr->set_host(segments[1]); + addr->set_port(port); + access_point.mutable_addresses()->AddAllocated(addr); + } + return access_point; } void ClientImpl::start() { @@ -62,14 +106,56 @@ void ClientImpl::start() { } name_server_resolver_->start(); - client_manager_ = ClientManagerFactory::getInstance().getClientManager(*this); + client_config_.client_id = clientId(); + + client_manager_ = ClientManagerFactory::getInstance().getClientManager(client_config_); client_manager_->start(); - exporter_ = std::make_shared(client_manager_, this); - exporter_->start(); + const auto& endpoint = name_server_resolver_->resolve(); + if (endpoint.empty()) { + SPDLOG_ERROR("Failed to resolve name server address"); + abort(); + } + + createSession(endpoint, false); + { + absl::MutexLock lk(&session_map_mtx_); + session_map_[endpoint]->await(); + } std::weak_ptr ptr(self()); + { + // Query routes for topics of interest in synchronous + std::vector topics; + topicsOfInterest(topics); + + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed = false; + for (const auto& topic : topics) { + completed = false; + auto callback = [&, mtx, cv](const std::error_code& ec, const TopicRouteDataPtr ptr) { + if (ec) { + SPDLOG_ERROR("Failed to query route for {} during starting. Cause: {}", topic, ec.message()); + } + + { + absl::MutexLock lk(mtx.get()); + completed = true; + } + cv->Signal(); + }; + getRouteFor(topic, callback); + { + absl::MutexLock lk(mtx.get()); + if (!completed) { + cv->Wait(mtx.get()); + } + } + } + } + auto route_update_functor = [ptr]() { std::shared_ptr base = ptr.lock(); if (base) { @@ -100,8 +186,8 @@ const char* ClientImpl::UPDATE_ROUTE_TASK_NAME = "route_updater"; void ClientImpl::endpointsInUse(absl::flat_hash_set& endpoints) { absl::MutexLock lk(&topic_route_table_mtx_); for (const auto& item : topic_route_table_) { - for (const auto& partition : item.second->partitions()) { - std::string endpoint = partition.asMessageQueue().serviceAddress(); + for (const auto& queue : item.second->messageQueues()) { + std::string endpoint = urlOf(queue); if (!endpoints.contains(endpoint)) { endpoints.emplace(std::move(endpoint)); } @@ -158,48 +244,6 @@ void ClientImpl::getRouteFor(const std::string& topic, } } -void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) { - std::vector> pairs; - { - std::string naming_address = name_server_resolver_->resolve(); - absl::string_view host_port_csv; - - if (absl::StartsWith(naming_address, NamingScheme::DnsPrefix)) { - endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME); - host_port_csv = absl::StripPrefix(naming_address, NamingScheme::DnsPrefix); - } else if (absl::StartsWith(naming_address, NamingScheme::IPv4Prefix)) { - endpoints->set_scheme(rmq::AddressScheme::IPv4); - host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv4Prefix); - } else if (absl::StartsWith(naming_address, NamingScheme::IPv6Prefix)) { - endpoints->set_scheme(rmq::AddressScheme::IPv6); - host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv6Prefix); - } else { - SPDLOG_WARN("Unsupported naming scheme"); - } - - std::vector name_server_list = absl::StrSplit(host_port_csv, ','); - - for (const auto& name_server_item : name_server_list) { - std::string::size_type pos = name_server_item.rfind(':'); - if (std::string::npos == pos) { - continue; - } - std::string host(name_server_item.substr(0, pos)); - std::string port(name_server_item.substr(pos + 1)); - pairs.emplace_back(std::make_pair(host, std::stoi(port))); - } - } - - if (!pairs.empty()) { - for (const auto& host_port : pairs) { - auto address = new rmq::Address(); - address->set_port(host_port.second); - address->set_host(host_port.first); - endpoints->mutable_addresses()->AddAllocated(address); - } - } -} - void ClientImpl::fetchRouteFor(const std::string& topic, const std::function& cb) { std::string name_server = name_server_resolver_->resolve(); @@ -224,13 +268,13 @@ void ClientImpl::fetchRouteFor(const std::string& topic, }; QueryRouteRequest request; - request.mutable_topic()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_resource_namespace(client_config_.resource_namespace); request.mutable_topic()->set_name(topic); - auto endpoints = request.mutable_endpoints(); - setAccessPoint(endpoints); + request.mutable_endpoints()->CopyFrom(accessPoint()); absl::flat_hash_map metadata; - Signature::sign(this, metadata); - client_manager_->resolveRoute(name_server, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + Signature::sign(client_config_, metadata); + client_manager_->resolveRoute(name_server, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout), callback); } void ClientImpl::updateRouteInfo() { @@ -247,6 +291,9 @@ void ClientImpl::updateRouteInfo() { topics.push_back(entry.first); } } + topicsOfInterest(topics); + + SPDLOG_DEBUG("Query route for {}", absl::StrJoin(topics, ",")); if (!topics.empty()) { for (const auto& topic : topics) { @@ -269,7 +316,7 @@ void ClientImpl::heartbeat() { prepareHeartbeatData(request); absl::flat_hash_map metadata; - Signature::sign(this, metadata); + Signature::sign(client_config_, metadata); for (const auto& target : hosts) { auto callback = [target](const std::error_code& ec, const HeartbeatResponse& response) { @@ -279,7 +326,8 @@ void ClientImpl::heartbeat() { } SPDLOG_DEBUG("Heartbeat to {} OK", target); }; - client_manager_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + client_manager_->heartbeat(target, metadata, request, absl::ToChronoMilliseconds(client_config_.request_timeout), + callback); } } @@ -307,46 +355,14 @@ void ClientImpl::onTopicRouteReady(const std::string& topic, const std::error_co } } -void ClientImpl::updateTraceHosts() { - absl::flat_hash_set hosts; - absl::MutexLock lk(&topic_route_table_mtx_); - for (const auto& item : topic_route_table_) { - for (const auto& partition : item.second->partitions()) { - if (Permission::NONE == partition.permission()) { - continue; - } - if (MixAll::MASTER_BROKER_ID != partition.broker().id()) { - continue; - } - std::string endpoint = partition.asMessageQueue().serviceAddress(); - if (!hosts.contains(endpoint)) { - hosts.emplace(std::move(endpoint)); - } - } - } - std::vector host_list(hosts.begin(), hosts.end()); - SPDLOG_DEBUG("Trace candidate hosts size={}", host_list.size()); - exporter_->updateHosts(host_list); -} - void ClientImpl::updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) { - if (ec || !route || route->partitions().empty()) { + if (ec || !route || route->messageQueues().empty()) { SPDLOG_WARN("Yuck! route for {} is invalid. Cause: {}", topic, ec.message()); return; } - absl::flat_hash_set new_hosts; { absl::MutexLock lk(&topic_route_table_mtx_); - absl::flat_hash_set existed_hosts; - for (const auto& item : topic_route_table_) { - for (const auto& partition : item.second->partitions()) { - std::string endpoint = partition.asMessageQueue().serviceAddress(); - if (!existed_hosts.contains(endpoint)) { - existed_hosts.emplace(std::move(endpoint)); - } - } - } if (!topic_route_table_.contains(topic)) { topic_route_table_.insert({topic, route}); SPDLOG_INFO("TopicRouteData for topic={} has changed. NONE --> {}", topic, route->debugString()); @@ -359,160 +375,119 @@ void ClientImpl::updateRouteCache(const std::string& topic, const std::error_cod route->debugString()); } } - absl::flat_hash_set hosts; - for (const auto& item : topic_route_table_) { - for (const auto& partition : item.second->partitions()) { - std::string endpoint = partition.asMessageQueue().serviceAddress(); - if (!hosts.contains(endpoint)) { - hosts.emplace(std::move(endpoint)); - } + } + + absl::flat_hash_set targets; + for (const auto& message_queue : route->messageQueues()) { + targets.insert(urlOf(message_queue)); + } + + { + absl::MutexLock lk(&session_map_mtx_); + for (auto it = targets.begin(); it != targets.end();) { + if (session_map_.contains(*it)) { + targets.erase(it++); + } else { + ++it; } } - std::set_difference(hosts.begin(), hosts.end(), existed_hosts.begin(), existed_hosts.end(), - std::inserter(new_hosts, new_hosts.begin())); } - updateTraceHosts(); - for (const auto& endpoints : new_hosts) { - pollCommand(endpoints); + + if (!targets.empty()) { + for (const auto& target : targets) { + createSession(target, true); + } } } -void ClientImpl::pollCommand(const std::string& target) { - SPDLOG_INFO("Start to poll command to remote, target={}", target); - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - - PollCommandRequest request; - auto&& resource_bundle = resourceBundle(); - request.set_client_id(resource_bundle.client_id); - switch (resource_bundle.group_type) { - case GroupType::PUBLISHER: - request.mutable_producer_group()->set_resource_namespace(resource_namespace_); - request.mutable_producer_group()->set_name(group_name_); - break; - - case GroupType::SUBSCRIBER: - request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); - request.mutable_consumer_group()->set_name(group_name_); - break; - } - auto topics = request.mutable_topics(); - for (const auto& item : resource_bundle.topics) { - auto topic = new rmq::Resource(); - topic->set_resource_namespace(resource_namespace_); - topic->set_name(item); - topics->AddAllocated(topic); - } - - client_manager_->pollCommand(target, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), - std::bind(&ClientImpl::onPollCommandResponse, this, std::placeholders::_1)); -} +rmq::Settings ClientImpl::clientSettings() { + rmq::Settings settings; + settings.mutable_access_point()->CopyFrom(accessPoint()); -void ClientImpl::verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message) { - SPDLOG_INFO("Received message to verify consumption, messageId={}", message.getMsgId()); - MessageListener* listener = messageListener(); + std::int64_t seconds = absl::ToInt64Seconds(client_config_.request_timeout); + settings.mutable_request_timeout()->set_seconds(seconds); + std::int64_t nanos = absl::ToInt64Nanoseconds(client_config_.request_timeout - absl::Seconds(seconds)); + settings.mutable_request_timeout()->set_nanos(nanos); - Metadata metadata; - Signature::sign(this, metadata); - ReportMessageConsumptionResultRequest request; - request.set_command_id(command_id); - - if (!listener) { - request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); - request.mutable_status()->set_message("Target is not a push consumer client"); - client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, - absl::ToChronoMilliseconds(io_timeout_)); - return; - } + // Fill User Agent + settings.mutable_user_agent()->set_hostname(UtilAll::hostname()); + settings.mutable_user_agent()->set_language(rmq::Language::CPP); + settings.mutable_user_agent()->set_version(MetadataConstants::CLIENT_VERSION); + settings.mutable_user_agent()->set_platform(MixAll::osName()); - if (MessageListenerType::FIFO == listener->listenerType()) { - request.mutable_status()->set_code(google::rpc::Code::FAILED_PRECONDITION); - request.mutable_status()->set_message("FIFO message does NOT support verification of message consumption"); - client_manager_->reportMessageConsumptionResult(remote_address, metadata, request, - absl::ToChronoMilliseconds(io_timeout_)); - return; - } + buildClientSettings(settings); - // Execute the actual verification task in dedicated thread-pool. - client_manager_->submit(std::bind(&ClientImpl::doVerify, this, remote_address, command_id, message)); + return settings; } -void ClientImpl::onPollCommandResponse(const InvocationContext* ctx) { - std::string address = ctx->remote_address; - absl::flat_hash_set hosts; - endpointsInUse(hosts); - if (!hosts.contains(address)) { - SPDLOG_INFO("Endpoint={} is now absent from route table. Break poll-command-cycle.", address); - return; +void ClientImpl::createSession(const std::string& target, bool verify) { + if (verify) { + absl::flat_hash_set endpoints; + endpointsInUse(endpoints); + if (!endpoints.contains(target)) { + return; + } } - if (!ctx->status.ok()) { - static std::string task_name = "Poll-Command-Later"; - client_manager_->getScheduler()->schedule(std::bind(&ClientImpl::pollCommand, this, ctx->remote_address), task_name, - std::chrono::seconds(3), std::chrono::seconds(0)); - return; + + std::weak_ptr client = self(); + auto rpc_client = client_manager_->getRpcClient(target, true); + SPDLOG_DEBUG("Create a new session for {}", target); + auto session = absl::make_unique(client, rpc_client); + { + absl::MutexLock lk(&session_map_mtx_); + session_map_.insert_or_assign(target, std::move(session)); } +} - switch (ctx->response.type_case()) { - case PollCommandResponse::TypeCase::kPrintThreadStackTraceCommand: { - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - ReportThreadStackTraceRequest request; - auto command_id = ctx->response.print_thread_stack_trace_command().command_id(); - request.set_command_id(command_id); - request.set_thread_stack_trace("--RocketMQ-Client-CPP does NOT support thread stack trace report--"); - client_manager_->reportThreadStackTrace(ctx->remote_address, metadata, request, - absl::ToChronoMilliseconds(io_timeout_)); - break; - } +void ClientImpl::verify(MessageConstSharedPtr message, std::function cb) { + std::weak_ptr ptr(self()); - case PollCommandResponse::TypeCase::kVerifyMessageConsumptionCommand: { - auto command_id = ctx->response.verify_message_consumption_command().command_id(); - auto data = ctx->response.verify_message_consumption_command().message(); - MQMessageExt message; - ReportMessageConsumptionResultRequest request; - request.set_command_id(command_id); - Metadata metadata; - Signature::sign(this, metadata); - if (!client_manager_->wrapMessage(data, message)) { - SPDLOG_WARN("Message to verify consumption is corrupted"); - request.mutable_status()->set_code(google::rpc::Code::INVALID_ARGUMENT); - request.mutable_status()->set_message("Data corrupted"); - client_manager_->reportMessageConsumptionResult(ctx->remote_address, metadata, request, - absl::ToChronoMilliseconds(io_timeout_)); - } - verifyMessageConsumption(std::move(ctx->remote_address), std::move(command_id), std::move(message)); - break; + // TODO: Use capture by move if C++14 is possible + auto task = [message, cb, ptr]() { + auto client = ptr.lock(); + if (!client) { + return; } - case PollCommandResponse::TypeCase::kRecoverOrphanedTransactionCommand: { - auto orphan = ctx->response.recover_orphaned_transaction_command().orphaned_transactional_message(); - MQMessageExt message; - if (client_manager_->wrapMessage(orphan, message)) { - MessageAccessor::setTargetEndpoint(message, ctx->remote_address); - const std::string& transaction_id = ctx->response.recover_orphaned_transaction_command().transaction_id(); - // Dispatch task to thread-pool. - client_manager_->submit( - std::bind(&ClientImpl::resolveOrphanedTransactionalMessage, this, transaction_id, message)); - } else { - SPDLOG_WARN("Failed to resolve orphaned transactional message, potentially caused by message-body checksum " - "verification failure."); - } - break; - } + client->onVerifyMessage(message, cb); + }; - case PollCommandResponse::TypeCase::kNoopCommand: { - SPDLOG_DEBUG("A long-polling-command period completed."); - break; - } + // Verify message may take a long period of time, we need to execute it in dedicated thread pool + // such that network-IO thread will not get blocked. + client_manager_->submit(task); +} + +void ClientImpl::onVerifyMessage(MessageConstSharedPtr message, std::function cb) { + rmq::TelemetryCommand cmd; + cmd.mutable_verify_message_result()->set_nonce(message->extension().nonce); + cmd.mutable_status()->set_code(rmq::Code::NOT_IMPLEMENTED); + cmd.mutable_status()->set_message("Unsupported Operation"); + cb(std::move(cmd)); +} - default: { - SPDLOG_WARN("Unsupported multiplex type"); - break; +void ClientImpl::recoverOrphanedTransaction(MessageConstSharedPtr message) { + auto ptr = self(); + std::weak_ptr owner(ptr); + + auto do_recover = [message, owner]() { + auto client = owner.lock(); + if (!client) { + return; } + client->doRecoverOrphanedTransaction(message); + }; + + // Execute orphaned transaction recovery in dedicated thread pool. + client_manager_->submit(do_recover); +} + +void ClientImpl::doRecoverOrphanedTransaction(MessageConstSharedPtr message) { + if (!message) { + SPDLOG_WARN("Failed to decode orphaned transaction message"); + return; } - // Initiate next round of long-polling-command immediately. - pollCommand(ctx->remote_address); + onOrphanedTransactionalMessage(message); } void ClientImpl::onRemoteEndpointRemoval(const std::vector& hosts) { @@ -527,52 +502,11 @@ void ClientImpl::onRemoteEndpointRemoval(const std::vector& hosts) } } -void ClientImpl::healthCheck() { - std::vector endpoints; - { - absl::MutexLock lk(&isolated_endpoints_mtx_); - for (const auto& item : isolated_endpoints_) { - endpoints.push_back(item); - } - } - - std::weak_ptr base(self()); - auto callback = [base](const std::error_code& ec, const InvocationContext* invocation_context) { - std::shared_ptr ptr = base.lock(); - if (!ptr) { - SPDLOG_INFO("BaseImpl has been destructed"); - return; - } - - ptr->onHealthCheckResponse(ec, invocation_context); - }; - - for (const auto& endpoint : endpoints) { - HealthCheckRequest request; - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - client_manager_->healthCheck(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); - } -} - void ClientImpl::schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) { client_manager_->getScheduler()->schedule(task, task_name, delay, std::chrono::milliseconds(0)); } -void ClientImpl::onHealthCheckResponse(const std::error_code& ec, const InvocationContext* ctx) { - if (ec) { - SPDLOG_WARN("Health check to server[host={}] failed. Cause: {}", ec.message()); - return; - } - - SPDLOG_INFO("Health check to server[host={}] passed. Remove it from isolated endpoint pool", ctx->remote_address); - { - absl::MutexLock lk(&isolated_endpoints_mtx_); - isolated_endpoints_.erase(ctx->remote_address); - } -} - void ClientImpl::notifyClientTermination() { SPDLOG_WARN("Should NOT reach here. Subclass should have overridden this function."); std::abort(); @@ -583,45 +517,23 @@ void ClientImpl::notifyClientTermination(const NotifyClientTerminationRequest& r endpointsInUse(endpoints); Metadata metadata; - Signature::sign(this, metadata); + Signature::sign(client_config_, metadata); for (const auto& endpoint : endpoints) { - client_manager_->notifyClientTermination(endpoint, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); + client_manager_->notifyClientTermination(endpoint, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout)); } } -void ClientImpl::doVerify(std::string target, std::string command_id, MQMessageExt message) { - ReportMessageConsumptionResultRequest request; - request.set_command_id(command_id); - StandardMessageListener* callback = reinterpret_cast(messageListener()); - try { - std::vector batch = {message}; - auto result = callback->consumeMessage(batch); - switch (result) { - case ConsumeMessageResult::SUCCESS: { - SPDLOG_DEBUG("Verify message[MsgId={}] OK", message.getMsgId()); - request.mutable_status()->set_message("Consume Success"); - break; - } - case ConsumeMessageResult::FAILURE: { - SPDLOG_WARN("Message Listener failed to consume message[MsgId={}] when verifying", message.getMsgId()); - request.mutable_status()->set_code(google::rpc::Code::INTERNAL); - request.mutable_status()->set_message("Consume Failed"); - break; - } - } - } catch (...) { - SPDLOG_WARN("Exception raised when invoking message listener provided by application developer. MsgId of message " - "to verify: {}", - message.getMsgId()); - request.mutable_status()->set_code(google::rpc::Code::INTERNAL); - request.mutable_status()->set_message( - "Unexpected exception raised while invoking message listener provided by application developer"); - } - - Metadata metadata; - Signature::sign(this, metadata); - client_manager_->reportMessageConsumptionResult(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_)); +std::string ClientImpl::clientId() { + static std::atomic_uint32_t sequence; + std::stringstream ss; + ss << UtilAll::hostname(); + ss << "@"; + std::string processID = std::to_string(getpid()); + ss << processID << "#"; + ss << sequence.fetch_add(1, std::memory_order_relaxed); + return ss.str(); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp index 76db7a593..aa99543da 100644 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp @@ -26,15 +26,16 @@ #include "opencensus/trace/span.h" #include "ConsumeFifoMessageService.h" -#include "MessageAccessor.h" +#include "MixAll.h" #include "ProcessQueue.h" #include "PushConsumerImpl.h" #include "rocketmq/MessageListener.h" +#include "rocketmq/Tracing.h" ROCKETMQ_NAMESPACE_BEGIN -ConsumeFifoMessageService ::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener) +ConsumeFifoMessageService::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, + MessageListener message_listener) : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { } @@ -60,11 +61,11 @@ void ConsumeFifoMessageService::shutdown() { } } -void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr& consumer, - const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message) { +void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr& consumer, + const std::weak_ptr& process_queue, + MessageConstSharedPtr message) { // In case custom executor is used. - const Executor& custom_executor = consumer->customExecutor(); + const auto& custom_executor = consumer->customExecutor(); if (custom_executor) { std::function consume_task = std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); @@ -80,8 +81,8 @@ void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptrsubmit(consume_task); } -void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { - ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); +void ConsumeFifoMessageService::submitConsumeTask(const std::weak_ptr& process_queue) { + auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { SPDLOG_INFO("Process queue has destructed"); return; @@ -93,30 +94,25 @@ void ConsumeFifoMessageService::submitConsumeTask(const ProcessQueueWeakPtr& pro return; } - assert(1 == consumer->consumeBatchSize()); - if (process_queue_ptr->bindFifoConsumeTask()) { - std::vector messages; - process_queue_ptr->take(consumer->consumeBatchSize(), messages); + std::vector messages; + process_queue_ptr->take(1, messages); if (!messages.empty()) { assert(1 == messages.size()); - submitConsumeTask0(consumer, process_queue, *messages.begin()); + submitConsumeTask0(consumer, process_queue, std::move(*messages.begin())); } } } -MessageListenerType ConsumeFifoMessageService::messageListenerType() { - return MessageListenerType::FIFO; -} - -void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message) { - ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); +void ConsumeFifoMessageService::consumeTask(const std::weak_ptr& process_queue, + MessageConstSharedPtr message) { + auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { return; } - const std::string& topic = message.getTopic(); - ConsumeMessageResult result; - std::shared_ptr consumer = consumer_.lock(); + const std::string& topic = message->topic(); + ConsumeResult result; + std::shared_ptr consumer = consumer_.lock(); // consumer might have been destructed. if (!consumer) { return; @@ -129,36 +125,37 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q } // Record await-consumption-span - { - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + if (message->traceContext().has_value()) { + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message->traceContext().value()); auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; + std::string span_name = + consumer->resourceNamespace() + "/" + topic + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, traceSampler()); } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - TracingUtility::addUniversalSpanAttributes(message, *consumer, span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); - absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(message); + TracingUtility::addUniversalSpanAttributes(*message, consumer->config(), span); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.extension.store_time); + absl::Time decoded_timestamp = absl::FromChrono(message->extension().decode_time); span.AddAnnotation( MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); span.End(); - MessageAccessor::setTraceContext(const_cast(message), - opencensus::trace::propagation::ToTraceParentHeader(span.context())); + // message.message.traceContext() + // MessageAccessor::setTraceContext(const_cast(message), + // opencensus::trace::propagation::ToTraceParentHeader(span.context())); } - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message->traceContext().value()); auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + message.getTopic() + " " + + std::string span_name = consumer->resourceNamespace() + "/" + message->topic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; if (span_context.IsValid()) { span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); @@ -169,29 +166,27 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); - TracingUtility::addUniversalSpanAttributes(message, *consumer, span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, message.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.getStoreTimestamp()); - MessageAccessor::setTraceContext(const_cast(message), - opencensus::trace::propagation::ToTraceParentHeader(span.context())); + TracingUtility::addUniversalSpanAttributes(*message, consumer->config(), span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, std::to_string(message->extension().delivery_attempt)); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, + MixAll::format(message->extension().store_time)); + // MessageAccessor::setTraceContext(const_cast(message), + // opencensus::trace::propagation::ToTraceParentHeader(span.context())); auto steady_start = std::chrono::steady_clock::now(); try { - assert(message_listener_); - auto message_listener = dynamic_cast(message_listener_); - assert(message_listener); - result = message_listener->consumeMessage(message); + result = message_listener_(*message); } catch (...) { - result = ConsumeMessageResult::FAILURE; + result = ConsumeResult::FAILURE; SPDLOG_ERROR("Business FIFO callback raised an exception when consumeMessage"); } switch (result) { - case ConsumeMessageResult::SUCCESS: + case ConsumeResult::SUCCESS: span.SetStatus(opencensus::trace::StatusCode::OK); break; - case ConsumeMessageResult::FAILURE: + case ConsumeResult::FAILURE: span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); break; } @@ -201,37 +196,33 @@ void ConsumeFifoMessageService::consumeTask(const ProcessQueueWeakPtr& process_q // Log client consume-time costs SPDLOG_DEBUG("Business callback spent {}ms processing message[Topic={}, MessageId={}].", - MixAll::millisecondsOf(duration), message.getTopic(), message.getMsgId()); + MixAll::millisecondsOf(duration), message->topic(), message->id()); + + if (result == ConsumeResult::SUCCESS) { + // Release message number and memory quota + process_queue_ptr->release(message->body().size()); - if (MessageModel::CLUSTERING == consumer->messageModel()) { - if (result == ConsumeMessageResult::SUCCESS) { - // Release message number and memory quota - process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); + // Ensure current message is acked before moving to the next message. + auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); + consumer->ack(*message, callback); + } else { + const Message* ptr = message.get(); + Message* raw = const_cast(ptr); + raw->mutableExtension().delivery_attempt++; - // Ensure current message is acked before moving to the next message. - auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); - consumer->ack(message, callback); + if (message->extension().delivery_attempt < consumer->maxDeliveryAttempts()) { + auto task = std::bind(&ConsumeFifoMessageService::scheduleConsumeTask, this, process_queue, message); + consumer->schedule("Scheduled-Consume-FIFO-Message-Task", task, std::chrono::seconds(1)); } else { - MessageAccessor::setDeliveryAttempt(message, message.getDeliveryAttempt() + 1); - if (message.getDeliveryAttempt() < consumer->maxDeliveryAttempts()) { - auto task = std::bind(&ConsumeFifoMessageService::scheduleConsumeTask, this, process_queue, message); - consumer->schedule("Scheduled-Consume-FIFO-Message-Task", task, std::chrono::seconds(1)); - } else { - auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, - std::placeholders::_1); - consumer->forwardToDeadLetterQueue(message, callback); - } - } - } else if (MessageModel::BROADCASTING == consumer->messageModel()) { - process_queue_ptr->release(message.getBody().size(), message.getQueueOffset()); - int64_t committed_offset; - if (process_queue_ptr->committedOffset(committed_offset)) { - consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, + std::placeholders::_1); + consumer->forwardToDeadLetterQueue(*message, callback); } } } -void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, +void ConsumeFifoMessageService::onAck(const std::weak_ptr& process_queue, + MessageConstSharedPtr message, const std::error_code& ec) { auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { @@ -241,7 +232,7 @@ void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, if (ec) { SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]. Cause: {}", - process_queue_ptr->simpleName(), message.getMsgId(), ec.message()); + process_queue_ptr->simpleName(), message->id(), ec.message()); auto consumer = consumer_.lock(); if (!consumer) { SPDLOG_WARN("Consumer instance has destructed"); @@ -250,20 +241,20 @@ void ConsumeFifoMessageService::onAck(const ProcessQueueWeakPtr& process_queue, auto task = std::bind(&ConsumeFifoMessageService::scheduleAckTask, this, process_queue, message); int32_t duration = 100; consumer->schedule("Ack-FIFO-Message-On-Failure", task, std::chrono::milliseconds(duration)); - SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message.getTopic(), message.getMsgId(), - duration); + SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message->topic(), message->id(), duration); } else { SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), - message.getMsgId()); + message->id()); process_queue_ptr->unbindFifoConsumeTask(); submitConsumeTask(process_queue); } } -void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message, bool ok) { +void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const std::weak_ptr& process_queue, + MessageConstSharedPtr message, + bool ok) { if (ok) { - SPDLOG_DEBUG("Forward message[Topic={}, MessagId={}] to DLQ OK", message.getTopic(), message.getMsgId()); + SPDLOG_DEBUG("Forward message[Topic={}, MessagId={}] to DLQ OK", message->topic(), message->id()); auto process_queue_ptr = process_queue.lock(); if (process_queue_ptr) { process_queue_ptr->unbindFifoConsumeTask(); @@ -271,7 +262,7 @@ void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const ProcessQueueWea return; } - SPDLOG_INFO("Failed to forward message[topic={}, MessageId={}] to DLQ", message.getTopic(), message.getMsgId()); + SPDLOG_INFO("Failed to forward message[topic={}, MessageId={}] to DLQ", message->topic(), message->id()); auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { SPDLOG_INFO("Abort further attempts considering its process queue has destructed"); @@ -285,8 +276,8 @@ void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const ProcessQueueWea consumer->schedule("Scheduled-Forward-DLQ-Task", task, std::chrono::milliseconds(100)); } -void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message) { +void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const std::weak_ptr& process_queue, + MessageConstSharedPtr message) { auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { return; @@ -295,10 +286,11 @@ void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const Process assert(consumer); auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, std::placeholders::_1); - consumer->forwardToDeadLetterQueue(message, callback); + consumer->forwardToDeadLetterQueue(*message, callback); } -void ConsumeFifoMessageService::scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message) { +void ConsumeFifoMessageService::scheduleAckTask(const std::weak_ptr& process_queue, + MessageConstSharedPtr message) { auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { return; @@ -307,12 +299,12 @@ void ConsumeFifoMessageService::scheduleAckTask(const ProcessQueueWeakPtr& proce auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); auto consumer = consumer_.lock(); if (consumer) { - consumer->ack(message, callback); + consumer->ack(*message, callback); } } -void ConsumeFifoMessageService::scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message) { +void ConsumeFifoMessageService::scheduleConsumeTask(const std::weak_ptr& process_queue, + MessageConstSharedPtr message) { auto consumer_ptr = consumer_.lock(); if (!consumer_ptr) { return; diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp index 4b34a8df4..71904221e 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp @@ -16,13 +16,13 @@ */ #include "ConsumeMessageServiceBase.h" #include "LoggerImpl.h" -#include "PushConsumer.h" +#include "PushConsumerImpl.h" #include "ThreadPoolImpl.h" ROCKETMQ_NAMESPACE_BEGIN -ConsumeMessageServiceBase::ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener) +ConsumeMessageServiceBase::ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, + MessageListener message_listener) : state_(State::CREATED), thread_count_(thread_count), pool_(absl::make_unique(thread_count_)), consumer_(std::move(consumer)), message_listener_(message_listener) { } @@ -91,13 +91,13 @@ std::shared_ptr> ConsumeMessageServiceBase::rateLimiter(const st } void ConsumeMessageServiceBase::dispatch() { - std::shared_ptr consumer = consumer_.lock(); + std::shared_ptr consumer = consumer_.lock(); if (!consumer) { SPDLOG_WARN("The consumer has already destructed"); return; } - auto callback = [this](const ProcessQueueSharedPtr& process_queue) { submitConsumeTask(process_queue); }; + auto callback = [this](const std::shared_ptr& process_queue) { submitConsumeTask(process_queue); }; consumer->iterateProcessQueue(callback); } diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp index e31b2edda..b478df9c8 100644 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp @@ -14,37 +14,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "ConsumeStandardMessageService.h" + #include #include #include #include +#include "LoggerImpl.h" +#include "MessageExt.h" +#include "MixAll.h" +#include "Protocol.h" +#include "PushConsumerImpl.h" #include "TracingUtility.h" +#include "UtilAll.h" #include "absl/memory/memory.h" #include "absl/strings/str_join.h" #include "absl/time/time.h" #include "absl/types/span.h" -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" - -#include "ConsumeStandardMessageService.h" -#include "LoggerImpl.h" -#include "MessageAccessor.h" -#include "MixAll.h" -#include "OtlpExporter.h" -#include "Protocol.h" -#include "PushConsumer.h" -#include "UtilAll.h" -#include "rocketmq/ConsumeType.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageExt.h" +#include "rocketmq/Message.h" #include "rocketmq/MessageListener.h" +#include "rocketmq/Tracing.h" ROCKETMQ_NAMESPACE_BEGIN -ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener_ptr) - : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener_ptr) { +ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener message_listener) + : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { } void ConsumeStandardMessageService::start() { @@ -67,13 +63,13 @@ void ConsumeStandardMessageService::shutdown() { } } -void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& process_queue) { - ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); +void ConsumeStandardMessageService::submitConsumeTask(const std::weak_ptr& process_queue) { + auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr) { SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); return; } - std::shared_ptr consumer = process_queue_ptr->getConsumer().lock(); + std::shared_ptr consumer = process_queue_ptr->getConsumer().lock(); if (!consumer) { return; @@ -81,9 +77,10 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& std::string topic = process_queue_ptr->topic(); bool has_more = true; + std::weak_ptr service(shared_from_this()); while (has_more) { - std::vector messages; - uint32_t batch_size = consumer->consumeBatchSize(); + std::vector messages; + uint32_t batch_size = 1; has_more = process_queue_ptr->take(batch_size, messages); if (messages.empty()) { assert(!has_more); @@ -94,7 +91,7 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& const Executor& custom_executor = consumer->customExecutor(); if (custom_executor) { std::function consume_task = - std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue, messages); + std::bind(&ConsumeStandardMessageService::consumeTask, service, process_queue, messages); custom_executor(consume_task); SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); continue; @@ -102,25 +99,39 @@ void ConsumeStandardMessageService::submitConsumeTask(const ProcessQueueWeakPtr& // submit batch message std::function consume_task = - std::bind(&ConsumeStandardMessageService::consumeTask, this, process_queue_ptr, messages); + std::bind(&ConsumeStandardMessageService::consumeTask, service, process_queue_ptr, messages); SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); pool_->submit(consume_task); } } -MessageListenerType ConsumeStandardMessageService::messageListenerType() { - return MessageListenerType::STANDARD; -} - -void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& process_queue, - const std::vector& msgs) { - ProcessQueueSharedPtr process_queue_ptr = process_queue.lock(); +void ConsumeStandardMessageService::consumeTask(std::weak_ptr service, + const std::weak_ptr& process_queue, + const std::vector& msgs) { + auto process_queue_ptr = process_queue.lock(); if (!process_queue_ptr || msgs.empty()) { return; } - std::string topic = msgs.begin()->getTopic(); - ConsumeMessageResult status; - std::shared_ptr consumer = consumer_.lock(); + + auto svc = service.lock(); + if (!svc) { + return; + } + + auto process_queue_shared_ptr = process_queue.lock(); + if (!process_queue_shared_ptr) { + return; + } + + svc->consume(process_queue_shared_ptr, msgs); +} + +void ConsumeStandardMessageService::consume(const std::shared_ptr& process_queue, + const std::vector& msgs) { + std::string topic = (*msgs.begin())->topic(); + ConsumeResult status; + + std::shared_ptr consumer = consumer_.lock(); // consumer might have been destructed. if (!consumer) { return; @@ -138,30 +149,33 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce // Record await-consumption-span { for (const auto& msg : msgs) { - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + if (!msg->traceContext().has_value()) { + continue; + } + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg->traceContext().value()); auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + std::string span_name = consumer->resourceNamespace() + "/" + msg->topic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, &Samplers::always()); + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, traceSampler()); } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); } span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); - absl::Time decoded_timestamp = MessageAccessor::decodedTimestamp(msg); + TracingUtility::addUniversalSpanAttributes(*msg, consumer->config(), span); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); + absl::Time decoded_timestamp = absl::FromChrono(msg->extension().decode_time); span.AddAnnotation( MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); span.End(); - MessageAccessor::setTraceContext(const_cast(msg), - opencensus::trace::propagation::ToTraceParentHeader(span.context())); + // MessageAccessor::setTraceContext(const_cast(msg), + // opencensus::trace::propagation::ToTraceParentHeader(span.context())); } } @@ -169,9 +183,12 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce std::vector spans; { for (const auto& msg : msgs) { - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg.traceContext()); + if (!msg->traceContext().has_value()) { + continue; + } + auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg->traceContext().value()); auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + msg.getTopic() + " " + + std::string span_name = consumer->resourceNamespace() + "/" + msg->topic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; if (span_context.IsValid()) { span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); @@ -182,25 +199,23 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); - TracingUtility::addUniversalSpanAttributes(msg, *consumer, span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg.getDeliveryAttempt()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); + TracingUtility::addUniversalSpanAttributes(*msg, consumer->config(), span); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg->extension().delivery_attempt); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.extension.store_time); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); spans.emplace_back(std::move(span)); - MessageAccessor::setTraceContext(const_cast(msg), - opencensus::trace::propagation::ToTraceParentHeader(span.context())); + // MessageAccessor::setTraceContext(const_cast(msg), + // opencensus::trace::propagation::ToTraceParentHeader(span.context())); } } auto steady_start = std::chrono::steady_clock::now(); try { - assert(nullptr != message_listener_); - auto message_listener = dynamic_cast(message_listener_); - assert(message_listener); - status = message_listener->consumeMessage(msgs); + // TODO: + status = message_listener_(**msgs.begin()); } catch (...) { - status = ConsumeMessageResult::FAILURE; + status = ConsumeResult::FAILURE; SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); } @@ -210,10 +225,10 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce { for (auto& span : spans) { switch (status) { - case ConsumeMessageResult::SUCCESS: + case ConsumeResult::SUCCESS: span.SetStatus(opencensus::trace::StatusCode::OK); break; - case ConsumeMessageResult::FAILURE: + case ConsumeResult::FAILURE: span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); break; } @@ -224,43 +239,35 @@ void ConsumeStandardMessageService::consumeTask(const ProcessQueueWeakPtr& proce // Log client consume-time costs SPDLOG_DEBUG("Business callback spent {}ms processing {} messages.", MixAll::millisecondsOf(duration), msgs.size()); - if (MessageModel::CLUSTERING == consumer->messageModel()) { - for (const auto& msg : msgs) { - const std::string& message_id = msg.getMsgId(); - - // Release message number and memory quota - process_queue_ptr->release(msg.getBody().size(), msg.getQueueOffset()); - - if (status == ConsumeMessageResult::SUCCESS) { - auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { - if (ec) { - SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]. Cause: {}", - process_queue_ptr->simpleName(), message_id, ec.message()); - } else { - SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), - message_id); - } - }; - consumer->ack(msg, callback); - } else { - auto callback = [process_queue_ptr, message_id](const std::error_code& ec) { - if (ec) { - SPDLOG_WARN("Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Cause: {} Message will be " - "re-consumed after default invisible time", - process_queue_ptr->simpleName(), message_id, ec.message()); - return; - } - - SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), message_id); - }; - consumer->nack(msg, callback); - } - } + for (const auto& msg : msgs) { + const std::string& message_id = msg->id(); + + // Release message number and memory quota + process_queue->release(msg->body().size()); + + if (status == ConsumeResult::SUCCESS) { + auto callback = [process_queue, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]. Cause: {}", + process_queue->simpleName(), message_id, ec.message()); + } else { + SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue->simpleName(), message_id); + } + }; + consumer->ack(*msg, callback); + } else { + auto callback = [process_queue, message_id](const std::error_code& ec) { + if (ec) { + SPDLOG_WARN( + "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Cause: {} Message will be " + "re-consumed after default invisible time", + process_queue->simpleName(), message_id, ec.message()); + return; + } - } else if (MessageModel::BROADCASTING == consumer->messageModel()) { - int64_t committed_offset; - if (process_queue_ptr->committedOffset(committed_offset)) { - consumer->updateOffset(process_queue_ptr->getMQMessageQueue(), committed_offset); + SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue->simpleName(), message_id); + }; + consumer->nack(*msg, callback); } } } diff --git a/src/main/cpp/rocketmq/DefaultMQProducer.cpp b/src/main/cpp/rocketmq/DefaultMQProducer.cpp deleted file mode 100644 index e4beacb12..000000000 --- a/src/main/cpp/rocketmq/DefaultMQProducer.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/DefaultMQProducer.h" - -#include -#include -#include -#include - -#include "absl/strings/str_split.h" - -#include "DynamicNameServerResolver.h" -#include "MixAll.h" -#include "ProducerImpl.h" -#include "StaticNameServerResolver.h" -#include "rocketmq/Transaction.h" - -ROCKETMQ_NAMESPACE_BEGIN - -DefaultMQProducer::DefaultMQProducer(const std::string& group_name) - : impl_(std::make_shared(group_name)) { -} - -void DefaultMQProducer::start() { - impl_->start(); -} - -void DefaultMQProducer::shutdown() { - impl_->shutdown(); -} - -std::chrono::milliseconds DefaultMQProducer::getSendMsgTimeout() const { - return absl::ToChronoMilliseconds(impl_->getIoTimeout()); -} - -SendResult DefaultMQProducer::send(MQMessage& message, const std::string& message_group) { - message.bindMessageGroup(message_group); - std::error_code ec; - auto&& send_result = impl_->send(message, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return std::move(send_result); -} - -void DefaultMQProducer::setSendMsgTimeout(std::chrono::milliseconds timeout) { - impl_->setIoTimeout(absl::FromChrono(timeout)); -} - -void DefaultMQProducer::setNamesrvAddr(const std::string& name_server_address_list) { - auto name_server_resolver = std::make_shared(name_server_address_list); - impl_->withNameServerResolver(name_server_resolver); -} - -void DefaultMQProducer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { - auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); - impl_->withNameServerResolver(name_server_resolver); -} - -void DefaultMQProducer::setGroupName(const std::string& group_name) { - impl_->setGroupName(group_name); -} - -void DefaultMQProducer::setInstanceName(const std::string& instance_name) { - impl_->setInstanceName(instance_name); -} - -void DefaultMQProducer::enableTracing(bool enabled) { - impl_->enableTracing(enabled); -} - -bool DefaultMQProducer::isTracingEnabled() { - return impl_->isTracingEnabled(); -} - -SendResult DefaultMQProducer::send(const MQMessage& message, bool filter_active_broker) { - std::error_code ec; - auto&& send_result = impl_->send(message, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return std::move(send_result); -} - -SendResult DefaultMQProducer::send(const MQMessage& message, std::error_code& ec) noexcept { - return impl_->send(message, ec); -} - -SendResult DefaultMQProducer::send(MQMessage& msg, const MQMessageQueue& mq) { - msg.bindMessageQueue(mq); - std::error_code ec; - auto&& send_result = impl_->send(msg, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return std::move(send_result); -} - -SendResult DefaultMQProducer::send(MQMessage& msg, MessageQueueSelector* selector, void* arg) { - std::error_code ec; - auto&& list = impl_->listMessageQueue(msg.getTopic(), ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - - auto&& message_queue = selector->select(list, msg, arg); - msg.bindMessageQueue(message_queue); - - auto&& send_result = impl_->send(msg, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return std::move(send_result); -} - -SendResult DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, int retry_times, - bool select_active_broker) { - return send(message, selector, arg); -} - -void DefaultMQProducer::send(const MQMessage& message, SendCallback* send_callback, bool select_active_broker) { - impl_->send(message, send_callback); -} - -void DefaultMQProducer::send(MQMessage& message, const MQMessageQueue& message_queue, SendCallback* send_callback) { - message.bindMessageQueue(message_queue); - impl_->send(message, send_callback); -} - -void DefaultMQProducer::send(MQMessage& message, MessageQueueSelector* selector, void* arg, - SendCallback* send_callback) { - std::error_code ec; - - // TODO: make querying route async - auto&& list = impl_->listMessageQueue(message.getTopic(), ec); - if (ec) { - send_callback->onFailure(ec); - } - - if (list.empty()) { - send_callback->onFailure(ErrorCode::ServiceUnavailable); - } - - auto&& message_queue = selector->select(list, message, arg); - message.bindMessageQueue(message_queue); - - impl_->send(message, send_callback); -} - -void DefaultMQProducer::sendOneway(const MQMessage& message, bool select_active_broker) { - std::error_code ec; - impl_->sendOneway(message, ec); -} - -void DefaultMQProducer::sendOneway(MQMessage& message, const MQMessageQueue& message_queue) { - message.bindMessageQueue(message_queue); - std::error_code ec; - impl_->sendOneway(message, ec); - if (ec) { - SPDLOG_INFO("Failed to send message in one-way: {}", ec.message()); - } -} - -void DefaultMQProducer::sendOneway(MQMessage& message, MessageQueueSelector* selector, void* arg) { - std::error_code ec; - auto&& list = impl_->listMessageQueue(message.getTopic(), ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - - if (list.empty()) { - ec = ErrorCode::ServiceUnavailable; - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - - auto&& message_queue = selector->select(list, message, arg); - message.bindMessageQueue(message_queue); - impl_->sendOneway(message, ec); -} - -void DefaultMQProducer::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { - impl_->setLocalTransactionStateChecker(std::move(checker)); -} - -void DefaultMQProducer::setMaxAttemptTimes(int max_attempt_times) { - impl_->maxAttemptTimes(max_attempt_times); -} - -int DefaultMQProducer::getMaxAttemptTimes() const { - return impl_->maxAttemptTimes(); -} - -std::vector DefaultMQProducer::getTopicMessageQueueInfo(const std::string& topic) { - std::error_code ec; - auto&& list = impl_->listMessageQueue(topic, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return std::move(list); -} - -void DefaultMQProducer::setUnitName(std::string unit_name) { - impl_->setUnitName(std::move(unit_name)); -} - -const std::string& DefaultMQProducer::getUnitName() { - return impl_->getUnitName(); -} - -uint32_t DefaultMQProducer::compressBodyThreshold() const { - return impl_->compressBodyThreshold(); -} - -void DefaultMQProducer::compressBodyThreshold(uint32_t threshold) { - impl_->compressBodyThreshold(threshold); -} - -void DefaultMQProducer::setResourceNamespace(const std::string& resource_namespace) { - impl_->resourceNamespace(resource_namespace); -} - -void DefaultMQProducer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { - impl_->setCredentialsProvider(std::move(credentials_provider)); -} - -void DefaultMQProducer::setRegion(const std::string& region) { - impl_->region(region); -} - -TransactionPtr DefaultMQProducer::prepare(MQMessage& message) { - std::error_code ec; - auto transaction = impl_->prepare(message, ec); - if (ec) { - THROW_MQ_EXCEPTION(MQClientException, ec.message(), ec.value()); - } - return transaction; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp deleted file mode 100644 index 56e22d391..000000000 --- a/src/main/cpp/rocketmq/DefaultMQPullConsumer.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/DefaultMQPullConsumer.h" - -#include - -#include "absl/strings/str_split.h" - -#include "AwaitPullCallback.h" -#include "DynamicNameServerResolver.h" -#include "PullConsumerImpl.h" -#include "StaticNameServerResolver.h" - -ROCKETMQ_NAMESPACE_BEGIN - -DefaultMQPullConsumer::DefaultMQPullConsumer(const std::string& group_name) - : impl_(std::make_shared(group_name)) { -} - -void DefaultMQPullConsumer::start() { - impl_->start(); -} - -void DefaultMQPullConsumer::shutdown() { - impl_->shutdown(); -} - -std::future> DefaultMQPullConsumer::queuesFor(const std::string& topic) { - return impl_->queuesFor(topic); -} - -std::future DefaultMQPullConsumer::queryOffset(const OffsetQuery& query) { - return impl_->queryOffset(query); -} - -bool DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullResult& pull_result) { - auto callback = absl::make_unique(pull_result); - pull(query, callback.get()); - return callback->await(); -} - -void DefaultMQPullConsumer::pull(const PullMessageQuery& query, PullCallback* callback) { - impl_->pull(query, callback); -} - -void DefaultMQPullConsumer::setResourceNamespace(const std::string& resource_namespace) { - impl_->resourceNamespace(resource_namespace); -} - -void DefaultMQPullConsumer::setCredentialsProvider(std::shared_ptr credentials_provider) { - impl_->setCredentialsProvider(std::move(credentials_provider)); -} - -void DefaultMQPullConsumer::setNamesrvAddr(const std::string& name_srv) { - auto name_server_resolver = std::make_shared(name_srv); - impl_->withNameServerResolver(name_server_resolver); -} - -void DefaultMQPullConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { - auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); - impl_->withNameServerResolver(name_server_resolver); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp b/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp deleted file mode 100644 index 890221cf5..000000000 --- a/src/main/cpp/rocketmq/DefaultMQPushConsumer.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "DynamicNameServerResolver.h" -#include "PushConsumerImpl.h" -#include "StaticNameServerResolver.h" -#include "rocketmq/DefaultMQPushConsumer.h" - -ROCKETMQ_NAMESPACE_BEGIN - -DefaultMQPushConsumer::DefaultMQPushConsumer(const std::string& group_name) { - impl_ = std::make_shared(group_name); -} - -void DefaultMQPushConsumer::start() { - impl_->start(); -} - -void DefaultMQPushConsumer::shutdown() { - impl_->shutdown(); - SPDLOG_DEBUG("PushConsumerImpl shared_ptr use_count={}", impl_.use_count()); -} - -void DefaultMQPushConsumer::subscribe(const std::string& topic, const std::string& expression, - ExpressionType expression_type) { - impl_->subscribe(topic, expression, expression_type); -} - -void DefaultMQPushConsumer::setConsumeFromWhere(ConsumeFromWhere policy) { - impl_->setConsumeFromWhere(policy); -} - -void DefaultMQPushConsumer::registerMessageListener(MessageListener* listener) { - impl_->registerMessageListener(listener); -} - -void DefaultMQPushConsumer::setNamesrvAddr(const std::string& name_srv) { - auto name_server_resolver = std::make_shared(name_srv); - impl_->withNameServerResolver(name_server_resolver); -} - -void DefaultMQPushConsumer::setNameServerListDiscoveryEndpoint(const std::string& discovery_endpoint) { - if (discovery_endpoint.empty()) { - return; - } - - auto name_server_resolver = std::make_shared(discovery_endpoint, std::chrono::seconds(10)); - impl_->withNameServerResolver(name_server_resolver); -} - -void DefaultMQPushConsumer::setGroupName(const std::string& group_name) { - impl_->setGroupName(group_name); -} - -void DefaultMQPushConsumer::setConsumeThreadCount(int thread_count) { - impl_->consumeThreadPoolSize(thread_count); -} - -void DefaultMQPushConsumer::setInstanceName(const std::string& instance_name) { - impl_->setInstanceName(instance_name); -} - -int DefaultMQPushConsumer::getProcessQueueTableSize() { - return impl_->getProcessQueueTableSize(); -} - -void DefaultMQPushConsumer::setUnitName(std::string unit_name) { - impl_->setUnitName(std::move(unit_name)); -} - -const std::string& DefaultMQPushConsumer::getUnitName() const { - return impl_->getUnitName(); -} - -void DefaultMQPushConsumer::enableTracing(bool enabled) { - impl_->enableTracing(enabled); -} - -bool DefaultMQPushConsumer::isTracingEnabled() { - return impl_->isTracingEnabled(); -} - -void DefaultMQPushConsumer::setAsyncPull(bool) { -} - -void DefaultMQPushConsumer::setConsumeMessageBatchMaxSize(int batch_size) { - impl_->consumeBatchSize(batch_size); -} - -void DefaultMQPushConsumer::setCustomExecutor(const Executor& executor) { - impl_->setCustomExecutor(executor); -} - -void DefaultMQPushConsumer::setThrottle(const std::string& topic, uint32_t threshold) { - impl_->setThrottle(topic, threshold); -} - -void DefaultMQPushConsumer::setResourceNamespace(const std::string& resource_namespace) { - impl_->resourceNamespace(resource_namespace); -} - -void DefaultMQPushConsumer::setCredentialsProvider(CredentialsProviderPtr credentials_provider) { - impl_->setCredentialsProvider(std::move(credentials_provider)); -} - -void DefaultMQPushConsumer::setMessageModel(MessageModel message_model) { - impl_->setMessageModel(message_model); -} - -std::string DefaultMQPushConsumer::groupName() const { - return impl_->getGroupName(); -} - -void DefaultMQPushConsumer::setOffsetStore(std::unique_ptr offset_store) { - impl_->setOffsetStore(std::move(offset_store)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp index 6604f1134..b0825453e 100644 --- a/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp +++ b/src/main/cpp/rocketmq/MessageGroupQueueSelector.cpp @@ -25,8 +25,7 @@ MessageGroupQueueSelector::MessageGroupQueueSelector(std::string message_group) : message_group_(std::move(message_group)) { } -MQMessageQueue MessageGroupQueueSelector::select(const std::vector& mqs, const MQMessage& msg, - void* arg) { +rmq::MessageQueue MessageGroupQueueSelector::select(const std::vector& mqs) { std::size_t hash_code = std::hash{}(message_group_); assert(!mqs.empty()); std::size_t len = mqs.size(); diff --git a/src/main/cpp/rocketmq/NamingScheme.cpp b/src/main/cpp/rocketmq/NamingScheme.cpp index 4646af971..467e295b0 100644 --- a/src/main/cpp/rocketmq/NamingScheme.cpp +++ b/src/main/cpp/rocketmq/NamingScheme.cpp @@ -37,6 +37,14 @@ const char* NamingScheme::IPv6Regex = "((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]) NamingScheme::NamingScheme() : ipv4_pattern_(IPv4Regex), ipv6_pattern_(IPv6Regex) { } +bool NamingScheme::isIPv4(const std::string& host) { + return re2::RE2::FullMatch(host, ipv4_pattern_); +} + +bool NamingScheme::isIPv6(const std::string& host) { + return re2::RE2::FullMatch(host, ipv6_pattern_); +} + std::string NamingScheme::buildAddress(const std::vector& list) { absl::flat_hash_map ipv4; absl::flat_hash_map ipv6; @@ -47,7 +55,7 @@ std::string NamingScheme::buildAddress(const std::vector& list) { continue; } - if (re2::RE2::FullMatch(host_port[0], ipv4_pattern_)) { + if (isIPv4(host_port[0])) { std::uint32_t port; if (absl::SimpleAtoi(host_port[1], &port)) { ipv4.insert_or_assign(host_port[0], port); @@ -55,7 +63,7 @@ std::string NamingScheme::buildAddress(const std::vector& list) { continue; } - if (re2::RE2::FullMatch(host_port[0], ipv6_pattern_)) { + if (isIPv6(host_port[0])) { std::uint32_t port; if (absl::SimpleAtoi(host_port[1], &port)) { ipv6.insert_or_assign(host_port[0], port); diff --git a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp index d9ba8cefd..0eeb08eef 100644 --- a/src/main/cpp/rocketmq/ProcessQueueImpl.cpp +++ b/src/main/cpp/rocketmq/ProcessQueueImpl.cpp @@ -25,20 +25,22 @@ #include "ClientManagerImpl.h" #include "MetadataConstants.h" #include "Protocol.h" -#include "PushConsumer.h" +#include "PushConsumerImpl.h" +#include "ReceiveMessageResult.h" #include "Signature.h" +#include "include/AsyncReceiveMessageCallback.h" #include "rocketmq/MessageListener.h" -#include "rocketmq/MessageModel.h" using namespace std::chrono; ROCKETMQ_NAMESPACE_BEGIN -ProcessQueueImpl::ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, - std::weak_ptr consumer, std::shared_ptr client_instance) +ProcessQueueImpl::ProcessQueueImpl(rmq::MessageQueue message_queue, FilterExpression filter_expression, + std::weak_ptr consumer, + std::shared_ptr client_instance) : message_queue_(std::move(message_queue)), filter_expression_(std::move(filter_expression)), invisible_time_(MixAll::millisecondsOf(MixAll::DEFAULT_INVISIBLE_TIME_)), - simple_name_(message_queue_.simpleName()), consumer_(std::move(consumer)), + simple_name_(simpleNameOf(message_queue_)), consumer_(std::move(consumer)), client_manager_(std::move(client_instance)), cached_message_quantity_(0), cached_message_memory_(0) { SPDLOG_DEBUG("Created ProcessQueue={}", simpleName()); } @@ -47,8 +49,8 @@ ProcessQueueImpl::~ProcessQueueImpl() { SPDLOG_INFO("ProcessQueue={} should have been re-balanced away, thus, is destructed", simpleName()); } -void ProcessQueueImpl::callback(std::shared_ptr callback) { - receive_callback_ = std::move(callback); +void ProcessQueueImpl::callback(std::shared_ptr callback) { + receive_callback_ = callback; } bool ProcessQueueImpl::expired() const { @@ -94,16 +96,7 @@ void ProcessQueueImpl::receiveMessage() { return; } - switch (consumer->messageModel()) { - case MessageModel::CLUSTERING: { - popMessage(); - break; - } - case MessageModel::BROADCASTING: { - pullMessage(); - break; - } - } + popMessage(); } void ProcessQueueImpl::popMessage() { @@ -113,37 +106,22 @@ void ProcessQueueImpl::popMessage() { if (!consumer_client) { return; } - Signature::sign(consumer_client.get(), metadata); + Signature::sign(consumer_client->config(), metadata); wrapPopMessageRequest(metadata, request); syncIdleState(); - SPDLOG_DEBUG("Try to pop message from {}", message_queue_.simpleName()); - client_manager_->receiveMessage(message_queue_.serviceAddress(), metadata, request, - absl::ToChronoMilliseconds(consumer_client->getLongPollingTimeout()), - receive_callback_); -} + SPDLOG_DEBUG("Try to pop message from {}", simpleNameOf(message_queue_)); -void ProcessQueueImpl::pullMessage() { - rmq::PullMessageRequest request; - absl::flat_hash_map metadata; - auto consumer = consumer_.lock(); - if (!consumer) { - SPDLOG_INFO("Owner consumer has destructed"); - return; - } - - Signature::sign(consumer.get(), metadata); - wrapPullMessageRequest(request); - syncIdleState(); - SPDLOG_DEBUG("Try to pull message from {}", message_queue_.simpleName()); - - auto timeout = consumer->getLongPollingTimeout(); - - auto callback = [this](const std::error_code& ec, const ReceiveMessageResult& result) { - receive_callback_->onCompletion(ec, result); + std::weak_ptr cb{receive_callback_}; + auto callback = [cb](const std::error_code& ec, const ReceiveMessageResult& result) { + auto recv_cb = cb.lock(); + if (recv_cb) { + recv_cb->onCompletion(ec, result); + } }; - client_manager_->pullMessage(message_queue_.serviceAddress(), metadata, request, absl::ToChronoMilliseconds(timeout), - callback); + client_manager_->receiveMessage( + urlOf(message_queue_), metadata, request, + absl::ToChronoMilliseconds(consumer_client->config().subscriber.polling_timeout), callback); } bool ProcessQueueImpl::hasPendingMessages() const { @@ -151,7 +129,7 @@ bool ProcessQueueImpl::hasPendingMessages() const { return !cached_messages_.empty(); } -void ProcessQueueImpl::cacheMessages(const std::vector& messages) { +void ProcessQueueImpl::cacheMessages(const std::vector& messages) { auto consumer = consumer_.lock(); if (!consumer) { return; @@ -159,11 +137,10 @@ void ProcessQueueImpl::cacheMessages(const std::vector& messages) { absl::MutexLock messages_lock_guard(&messages_mtx_); - absl::MutexLock offsets_lock_guard(&offsets_mtx_); for (const auto& message : messages) { - const std::string& msg_id = message.getMsgId(); - if (!filter_expression_.accept(message)) { - const std::string& topic = message.getTopic(); + const std::string& msg_id = message->id(); + if (!filter_expression_.accept(*message)) { + const std::string& topic = message->topic(); auto callback = [topic, msg_id](const std::error_code& ec) { if (ec) { SPDLOG_WARN( @@ -174,24 +151,17 @@ void ProcessQueueImpl::cacheMessages(const std::vector& messages) msg_id); } }; - consumer->ack(message, callback); + consumer->ack(*message, callback); continue; } cached_messages_.emplace_back(message); cached_message_quantity_.fetch_add(1, std::memory_order_relaxed); - cached_message_memory_.fetch_add(message.getBody().size(), std::memory_order_relaxed); - if (MessageModel::BROADCASTING == consumer->messageModel()) { - if (offsets_.size() == 1 && offsets_.begin()->released_) { - int64_t previously_released = offsets_.begin()->offset_; - offsets_.erase(OffsetRecord(previously_released)); - } - offsets_.emplace(message.getQueueOffset()); - } + cached_message_memory_.fetch_add(message->body().size(), std::memory_order_relaxed); } } } -bool ProcessQueueImpl::take(uint32_t batch_size, std::vector& messages) { +bool ProcessQueueImpl::take(uint32_t batch_size, std::vector& messages) { absl::MutexLock lock(&messages_mtx_); if (cached_messages_.empty()) { return false; @@ -201,27 +171,13 @@ bool ProcessQueueImpl::take(uint32_t batch_size, std::vector& mess if (0 == batch_size--) { break; } - messages.push_back(*it); it = cached_messages_.erase(it); } return !cached_messages_.empty(); } -bool ProcessQueueImpl::committedOffset(int64_t& offset) { - absl::MutexLock lk(&offsets_mtx_); - if (offsets_.empty()) { - return false; - } - if (offsets_.begin()->released_) { - offset = offsets_.begin()->offset_ + 1; - } else { - offset = offsets_.begin()->offset_; - } - return true; -} - -void ProcessQueueImpl::release(uint64_t body_size, int64_t offset) { +void ProcessQueueImpl::release(uint64_t body_size) { auto consumer = consumer_.lock(); if (!consumer) { return; @@ -229,17 +185,6 @@ void ProcessQueueImpl::release(uint64_t body_size, int64_t offset) { cached_message_quantity_.fetch_sub(1); cached_message_memory_.fetch_sub(body_size); - - if (MessageModel::BROADCASTING == consumer->messageModel()) { - absl::MutexLock lk(&offsets_mtx_); - if (offsets_.size() > 1) { - offsets_.erase(OffsetRecord(offset)); - } else { - assert(offsets_.begin()->offset_ == offset); - offsets_.erase(OffsetRecord(offset)); - offsets_.emplace(OffsetRecord(offset, true)); - } - } } void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expression) { @@ -248,7 +193,7 @@ void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expres if (!consumer) { return; } - auto&& optional = consumer->getFilterExpression(message_queue_.getTopic()); + auto&& optional = consumer->getFilterExpression(message_queue_.topic().name()); if (optional.has_value()) { auto expression = optional.value(); switch (expression.type_) { @@ -269,34 +214,18 @@ void ProcessQueueImpl::wrapFilterExpression(rmq::FilterExpression* filter_expres void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_map& metadata, rmq::ReceiveMessageRequest& request) { - std::shared_ptr consumer = consumer_.lock(); + std::shared_ptr consumer = consumer_.lock(); assert(consumer); - request.set_client_id(consumer->clientId()); - request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); - request.mutable_partition()->set_id(message_queue_.getQueueId()); - request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); - request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); + request.mutable_group()->CopyFrom(consumer->config().subscriber.group); + request.mutable_message_queue()->CopyFrom(message_queue_); wrapFilterExpression(request.mutable_filter_expression()); - switch (consumer->getConsumeMessageService()->messageListenerType()) { - case MessageListenerType::STANDARD: { - request.set_fifo_flag(false); - break; - } - case MessageListenerType::FIFO: { - request.set_fifo_flag(true); - break; - } - } - // Batch size request.set_batch_size(consumer->receiveBatchSize()); - // Consume policy - request.set_consume_policy(rmq::ConsumePolicy::RESUME); + // Let server to automatically extend invisible duration. + request.set_auto_renew(true); // Set invisible time request.mutable_invisible_duration()->set_seconds( @@ -306,23 +235,7 @@ void ProcessQueueImpl::wrapPopMessageRequest(absl::flat_hash_mapset_nanos(nano_seconds); } -void ProcessQueueImpl::wrapPullMessageRequest(rmq::PullMessageRequest& request) { - std::shared_ptr consumer = consumer_.lock(); - assert(consumer); - request.set_client_id(consumer->clientId()); - request.mutable_group()->set_name(consumer->getGroupName()); - request.mutable_group()->set_resource_namespace(consumer->resourceNamespace()); - request.mutable_partition()->set_id(message_queue_.getQueueId()); - request.mutable_partition()->mutable_broker()->set_name(message_queue_.getBrokerName()); - request.mutable_partition()->mutable_topic()->set_name(message_queue_.getTopic()); - request.mutable_partition()->mutable_topic()->set_resource_namespace(consumer->resourceNamespace()); - request.set_offset(next_offset_); - request.set_batch_size(consumer->receiveBatchSize()); - - wrapFilterExpression(request.mutable_filter_expression()); -} - -std::weak_ptr ProcessQueueImpl::getConsumer() { +std::weak_ptr ProcessQueueImpl::getConsumer() { return consumer_; } @@ -330,10 +243,6 @@ std::shared_ptr ProcessQueueImpl::getClientManager() { return client_manager_; } -MQMessageQueue ProcessQueueImpl::getMQMessageQueue() { - return message_queue_; -} - const FilterExpression& ProcessQueueImpl::getFilterExpression() const { return filter_expression_; } diff --git a/src/main/cpp/rocketmq/Producer.cpp b/src/main/cpp/rocketmq/Producer.cpp new file mode 100644 index 000000000..b2bc5e014 --- /dev/null +++ b/src/main/cpp/rocketmq/Producer.cpp @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rocketmq/Producer.h" + +#include +#include +#include +#include + +#include "DynamicNameServerResolver.h" +#include "LoggerImpl.h" +#include "MixAll.h" +#include "ProducerImpl.h" +#include "StaticNameServerResolver.h" +#include "absl/strings/str_split.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/SendReceipt.h" +#include "rocketmq/Transaction.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void Producer::start() { + impl_->start(); +} + +SendReceipt Producer::send(MessageConstPtr message, std::error_code& ec) noexcept { + if (!message) { + ec = ErrorCode::BadRequest; + return {}; + } + + return impl_->send(std::move(message), ec); +} + +void Producer::send(MessageConstPtr message, const SendCallback& callback) noexcept { + if (!message) { + std::error_code ec = ErrorCode::BadRequest; + SendReceipt send_receipt = {}; + callback(ec, send_receipt); + return; + } + + if (message->group().has_value()) { + SendReceipt empty; + std::error_code ec = ErrorCode::BadRequestAsyncPubFifoMessage; + callback(ec, empty); + return; + } + + impl_->send(std::move(message), callback); +} + +ProducerBuilder Producer::newBuilder() { + return {}; +} + +ProducerBuilder::ProducerBuilder() : impl_(std::make_shared()){}; + +ProducerBuilder& ProducerBuilder::withConfiguration(Configuration configuration) { + auto name_server_resolver = std::make_shared(configuration.endpoints()); + impl_->withNameServerResolver(std::move(name_server_resolver)); + impl_->withCredentialsProvider(configuration.credentialsProvider()); + impl_->withRequestTimeout(configuration.requestTimeout()); + return *this; +} + +ProducerBuilder& ProducerBuilder::withTopics(const std::vector& topics) { + impl_->topicsOfInterest(topics); + return *this; +} + +ProducerBuilder& ProducerBuilder::withTransactionChecker(const TransactionChecker& checker) { + return *this; +} + +Producer ProducerBuilder::build() { + Producer producer(impl_); + producer.start(); + return producer; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 38bc99e6a..4c203d83e 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -17,44 +17,46 @@ #include "ProducerImpl.h" #include +#include #include #include +#include #include #include #include "Client.h" -#include "absl/strings/str_join.h" -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" - -#include "MessageAccessor.h" #include "MessageGroupQueueSelector.h" #include "MetadataConstants.h" #include "MixAll.h" -#include "OtlpExporter.h" #include "Protocol.h" +#include "PublishInfoCallback.h" #include "RpcClient.h" -#include "SendCallbacks.h" +#include "SendContext.h" #include "SendMessageContext.h" #include "Signature.h" #include "TracingUtility.h" #include "TransactionImpl.h" #include "UniqueIdGenerator.h" #include "UtilAll.h" +#include "absl/strings/str_join.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" #include "rocketmq/ErrorCode.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" +#include "rocketmq/Message.h" +#include "rocketmq/SendReceipt.h" +#include "rocketmq/Tracing.h" #include "rocketmq/Transaction.h" +#include "rocketmq/TransactionChecker.h" ROCKETMQ_NAMESPACE_BEGIN -ProducerImpl::ProducerImpl(absl::string_view group_name) - : ClientImpl(group_name), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { +ProducerImpl::ProducerImpl() : ClientImpl(""), compress_body_threshold_(MixAll::DEFAULT_COMPRESS_BODY_THRESHOLD_) { // TODO: initialize client_config_ and fault_strategy_ } ProducerImpl::~ProducerImpl() { SPDLOG_INFO("Producer instance is destructed"); + shutdown(); } void ProducerImpl::start() { @@ -86,11 +88,6 @@ void ProducerImpl::shutdown() { } void ProducerImpl::notifyClientTermination() { - NotifyClientTerminationRequest request; - request.mutable_producer_group()->set_resource_namespace(resource_namespace_); - request.mutable_producer_group()->set_name(group_name_); - request.set_client_id(clientId()); - ClientImpl::notifyClientTermination(request); } bool ProducerImpl::isRunning() const { @@ -103,332 +100,310 @@ void ProducerImpl::ensureRunning(std::error_code& ec) const noexcept { } } -bool ProducerImpl::validate(const MQMessage& message) { +bool ProducerImpl::validate(const Message& message) { return MixAll::validate(message); } -void ProducerImpl::wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, - const MQMessageQueue& message_queue) { - request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_message()->mutable_topic()->set_name(message.getTopic()); +void ProducerImpl::wrapSendMessageRequest(const Message& message, SendMessageRequest& request, + const rmq::MessageQueue& message_queue) { + auto msg = new rmq::Message(); + + msg->mutable_topic()->set_resource_namespace(resourceNamespace()); + msg->mutable_topic()->set_name(message.topic()); - auto system_attribute = request.mutable_message()->mutable_system_attribute(); + auto system_properties = msg->mutable_system_properties(); // Handle Tag - auto&& tag = message.getTags(); - if (!tag.empty()) { - system_attribute->set_tag(tag); + if (message.tag().has_value()) { + system_properties->set_tag(message.tag().value()); } // Handle Key - const auto& keys = message.getKeys(); + const auto& keys = message.keys(); if (!keys.empty()) { - system_attribute->mutable_keys()->Add(keys.begin(), keys.end()); + system_properties->mutable_keys()->Add(keys.begin(), keys.end()); } // TraceContext - const auto& trace_context = message.traceContext(); - if (!trace_context.empty()) { - system_attribute->set_trace_context(trace_context); + if (message.traceContext().has_value()) { + const auto& trace_context = message.traceContext().value(); + if (!trace_context.empty()) { + system_properties->set_trace_context(trace_context); + } } // Delivery Timestamp - auto delivery_timestamp = message.deliveryTimestamp(); - if (delivery_timestamp.time_since_epoch().count()) { - auto duration = delivery_timestamp.time_since_epoch(); - system_attribute->set_delivery_attempt(std::chrono::duration_cast(duration).count()); - } - - // Delay Level - if (message.getDelayTimeLevel()) { - system_attribute->set_delay_level(message.getDelayTimeLevel()); + if (message.deliveryTimestamp().has_value()) { + auto delivery_timestamp = message.deliveryTimestamp().value(); + if (delivery_timestamp.time_since_epoch().count()) { + auto duration = delivery_timestamp.time_since_epoch(); + system_properties->set_delivery_attempt(std::chrono::duration_cast(duration).count()); + } } // Born-time auto duration = absl::Now() - absl::UnixEpoch(); int64_t seconds = absl::ToInt64Seconds(duration); - system_attribute->mutable_born_timestamp()->set_seconds(seconds); - system_attribute->mutable_born_timestamp()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); + system_properties->mutable_born_timestamp()->set_seconds(seconds); + system_properties->mutable_born_timestamp()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); - system_attribute->set_born_host(UtilAll::hostname()); + system_properties->set_born_host(UtilAll::hostname()); - system_attribute->mutable_producer_group()->set_resource_namespace(resource_namespace_); - system_attribute->mutable_producer_group()->set_name(group_name_); - - switch (message.messageType()) { - case MessageType::NORMAL: - system_attribute->set_message_type(rmq::MessageType::NORMAL); - break; - case MessageType::DELAY: - system_attribute->set_message_type(rmq::MessageType::DELAY); - break; - case MessageType::TRANSACTION: - system_attribute->set_message_type(rmq::MessageType::TRANSACTION); - break; - case MessageType::FIFO: - system_attribute->set_message_type(rmq::MessageType::FIFO); - break; + if (message.deliveryTimestamp().has_value()) { + system_properties->set_message_type(rmq::MessageType::DELAY); + } else if (message.group().has_value()) { + system_properties->set_message_type(rmq::MessageType::FIFO); + } else { + system_properties->set_message_type(rmq::MessageType::NORMAL); } - if (message.bodyLength() >= compress_body_threshold_) { + if (message.body().size() >= compress_body_threshold_) { std::string compressed_body; - UtilAll::compress(message.getBody(), compressed_body); - request.mutable_message()->set_body(compressed_body); - system_attribute->set_body_encoding(rmq::Encoding::GZIP); + UtilAll::compress(message.body(), compressed_body); + msg->set_body(compressed_body); + system_properties->set_body_encoding(rmq::Encoding::GZIP); } else { - request.mutable_message()->set_body(message.getBody()); - system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); + msg->set_body(message.body()); + system_properties->set_body_encoding(rmq::Encoding::IDENTITY); + } + + if (message.group().has_value()) { + system_properties->set_message_group(message.group().value()); } - system_attribute->set_message_group(message.messageGroup()); - system_attribute->set_message_id(message.getMsgId()); - system_attribute->set_partition_id(message_queue.getQueueId()); + system_properties->set_message_id(message.id()); + system_properties->set_queue_id(message_queue.id()); // Forward user-defined-properties - for (auto& item : message.getProperties()) { - request.mutable_message()->mutable_user_attribute()->insert({item.first, item.second}); + for (auto& item : message.properties()) { + msg->mutable_user_properties()->insert({item.first, item.second}); } + + // Add into request + request.mutable_messages()->AddAllocated(msg); SPDLOG_TRACE("SendMessageRequest: {}", request.DebugString()); } -SendResult ProducerImpl::send(const MQMessage& message, std::error_code& ec) noexcept { +SendReceipt ProducerImpl::send(MessageConstPtr message, std::error_code& ec) noexcept { ensureRunning(ec); if (ec) { return {}; } - auto topic_publish_info = getPublishInfo(message.getTopic()); + auto topic_publish_info = getPublishInfo(message->topic()); if (!topic_publish_info) { ec = ErrorCode::NotFound; return {}; } - std::vector message_queue_list; - - if (!message.messageGroup().empty() || message.messageQueue()) { - auto&& list = listMessageQueue(message.getTopic(), ec); - if (ec) { - return {}; - } + std::vector message_queue_list; + if (!topic_publish_info->selectMessageQueues(absl::make_optional(), message_queue_list)) { + ec = ErrorCode::NotFound; + return {}; + } - if (list.empty()) { - ec = ErrorCode::ServiceUnavailable; - return {}; - } + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed = false; + SendReceipt send_receipt; - if (!message.messageGroup().empty()) { - std::size_t hash_code = std::hash{}(message.messageGroup()); - hash_code = hash_code & std::numeric_limits::max(); - std::size_t r = hash_code % list.size(); - message_queue_list.push_back(list[r]); - } else { - for (const auto& entry : list) { - if (entry == message.messageQueue()) { - message_queue_list.push_back(entry); - break; - } - } + // Define callback procedureq + auto callback = [&, mtx, cv](const std::error_code& code, const SendReceipt& receipt) { + ec = code; + send_receipt = receipt; + { + absl::MutexLock lk(mtx.get()); + completed = true; } - } else { - takeMessageQueuesRoundRobin(topic_publish_info, message_queue_list, max_attempt_times_); - } - - if (message_queue_list.empty()) { - ec = ErrorCode::ServiceUnavailable; - return {}; - } + cv->Signal(); + }; - AwaitSendCallback callback; - send0(message, &callback, message_queue_list, max_attempt_times_); - callback.await(); + send0(std::move(message), callback, message_queue_list); - if (callback) { - return callback.sendResult(); + { + absl::MutexLock lk(mtx.get()); + if (!completed) { + cv->Wait(mtx.get()); + } } - ec = callback.errorCode(); - return {}; + return send_receipt; } -void ProducerImpl::send(const MQMessage& message, SendCallback* cb) { +void ProducerImpl::send(MessageConstPtr message, SendCallback cb) { std::error_code ec; ensureRunning(ec); if (ec) { - cb->onFailure(ec); + SendReceipt send_receipt; + cb(ec, send_receipt); } - auto callback = [this, message, cb](const std::error_code& ec, const TopicPublishInfoPtr& publish_info) { + std::string topic = message->topic(); + std::weak_ptr client(shared_from_this()); + + // Walk-around: Use capture by move when C++14 is possible + const Message* msg = message.release(); + auto callback = [client, cb, msg](const std::error_code& ec, const TopicPublishInfoPtr& publish_info) { + MessageConstPtr ptr(msg); + // No route entries of the given topic is available if (ec) { - cb->onFailure(ec); + SendReceipt send_receipt; + cb(ec, send_receipt); return; } - std::vector message_queue_list; - if (!message.messageGroup().empty() || message.messageQueue()) { - auto&& list = publish_info->getMessageQueueList(); - if (!message.messageGroup().empty()) { - std::size_t hash_code = std::hash{}(message.messageGroup()); - hash_code = hash_code & std::numeric_limits::max(); - std::size_t r = hash_code % list.size(); - message_queue_list.push_back(list[r]); - } else { - for (const auto& entry : list) { - if (entry == message.messageQueue()) { - message_queue_list.push_back(entry); - break; - } - } - } - } else { - takeMessageQueuesRoundRobin(publish_info, message_queue_list, max_attempt_times_); + if (!publish_info) { + std::error_code ec = ErrorCode::NotFound; + SendReceipt send_receipt; + cb(ec, send_receipt); + return; } - if (message_queue_list.empty()) { - cb->onFailure(ErrorCode::ServiceUnavailable); + auto publisher = client.lock(); + std::vector message_queue_list; + if (!publish_info->selectMessageQueues(ptr->group(), message_queue_list)) { + std::error_code ec = ErrorCode::NotFound; + SendReceipt send_receipt; + cb(ec, send_receipt); return; } - send0(message, cb, message_queue_list, max_attempt_times_); + publisher->send0(std::move(ptr), cb, message_queue_list); }; - asyncPublishInfo(message.getTopic(), callback); + getPublishInfoAsync(topic, callback); } -void ProducerImpl::sendOneway(const MQMessage& message, std::error_code& ec) { - send(message, ec); +void ProducerImpl::setTransactionChecker(TransactionChecker checker) { + transaction_checker_ = std::move(checker); } -void ProducerImpl::setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker) { - transaction_state_checker_ = std::move(checker); -} - -void ProducerImpl::sendImpl(RetrySendCallback* callback) { - const std::string& target = callback->messageQueue().serviceAddress(); +void ProducerImpl::sendImpl(std::shared_ptr context) { + const std::string& target = urlOf(context->messageQueue()); if (target.empty()) { SPDLOG_WARN("Failed to resolve broker address from MessageQueue"); std::error_code ec = ErrorCode::BadGateway; - callback->onFailure(ec); + context->onFailure(ec); return; } - { - // Trace Send RPC - auto& message = callback->message(); - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); - - auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = - resourceNamespace() + "/" + message.getTopic() + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); - } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); - TracingUtility::addUniversalSpanAttributes(message, *this, span); - // Note: attempt-time is 0-based - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); - - if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, - absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); - } - callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); - callback->span() = span; - } + // { + // Trace Send RPC + // auto span_context = + // opencensus::trace::propagation::FromTraceParentHeader(context->message_.traceContext().value()); auto span = + // opencensus::trace::Span::BlankSpan(); std::string span_name = resourceNamespace() + "/" + context->message_.topic() + // + " " + + // MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; + // if (span_context.IsValid()) { + // span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); + // } else { + // span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + // } + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + // MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + // MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); + // TracingUtility::addUniversalSpanAttributes(context->message_, *this, span); + // Note: attempt-time is 0-based + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); + // if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, + // absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); + // } + // callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); + // callback->span() = span; + // } SendMessageRequest request; - wrapSendMessageRequest(callback->message(), request, callback->messageQueue()); + wrapSendMessageRequest(*context->message_, request, context->messageQueue()); Metadata metadata; - Signature::sign(this, metadata); + Signature::sign(client_config_, metadata); + + auto callback = [context](const std::error_code& ec, const SendReceipt& send_receipt) { + if (ec) { + context->onFailure(ec); + return; + } + context->onSuccess(send_receipt); + }; client_manager_->send(target, metadata, request, callback); } -void ProducerImpl::send0(const MQMessage& message, SendCallback* callback, std::vector list, - int max_attempt_times) { - assert(callback); +void ProducerImpl::send0(MessageConstPtr message, SendCallback callback, std::vector list) { + SendReceipt send_receipt; + std::error_code ec; - if (!validate(message)) { - std::error_code ec = ErrorCode::BadRequest; - callback->onFailure(ec); + if (!validate(*message)) { + ec = ErrorCode::BadRequest; + callback(ec, send_receipt); return; } if (list.empty()) { - std::error_code ec = ErrorCode::NotFound; - callback->onFailure(ec); + ec = ErrorCode::NotFound; + callback(ec, send_receipt); return; } - if (max_attempt_times <= 0) { - std::error_code ec = ErrorCode::BadConfiguration; - callback->onFailure(ec); - return; - } - MQMessageQueue message_queue = list[0]; - auto retry_callback = - new RetrySendCallback(shared_from_this(), message, max_attempt_times, callback, std::move(list)); - sendImpl(retry_callback); - const_cast(message).traceContext( - opencensus::trace::propagation::ToTraceParentHeader(retry_callback->span().context())); + auto context = std::make_shared(shared_from_this(), std::move(message), callback, std::move(list)); + sendImpl(context); + // const_cast(message).traceContext( + // opencensus::trace::propagation::ToTraceParentHeader(context->span().context())); } -bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& message, - const std::string& transaction_id, TransactionState resolution) { - +bool ProducerImpl::endTransaction0(const Transaction& transaction, TransactionState resolution) { EndTransactionRequest request; - request.set_message_id(message.getMsgId()); - request.set_transaction_id(transaction_id); - request.mutable_group()->set_name(group_name_); - request.mutable_group()->set_resource_namespace(resource_namespace_); + request.mutable_topic()->set_name(transaction.topic()); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.set_message_id(transaction.messageId()); + request.set_transaction_id(transaction.messageId()); std::string action; switch (resolution) { case TransactionState::COMMIT: - request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_COMMIT); + request.set_resolution(rmq::COMMIT); action = "commit"; break; case TransactionState::ROLLBACK: - request.set_resolution(rmq::EndTransactionRequest_TransactionResolution_ROLLBACK); + request.set_resolution(rmq::ROLLBACK); action = "rollback"; break; } absl::flat_hash_map metadata; - Signature::sign(this, metadata); + Signature::sign(config(), metadata); bool completed = false; bool success = false; - // Trace transactional message - opencensus::trace::SpanContext span_context = - opencensus::trace::propagation::FromTraceParentHeader(message.traceContext()); auto span = opencensus::trace::Span::BlankSpan(); - std::string trace_operation_name = TransactionState::COMMIT == resolution - ? MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION - : MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; - std::string span_name = resourceNamespace() + "/" + message.getTopic() + " " + trace_operation_name; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); - } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); + if (!transaction.traceContext().empty()) { + // Trace transactional message + opencensus::trace::SpanContext span_context = + opencensus::trace::propagation::FromTraceParentHeader(transaction.traceContext()); + std::string trace_operation_name = TransactionState::COMMIT == resolution + ? MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_COMMIT_OPERATION + : MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_ROLLBACK_OPERATION; + std::string span_name = resourceNamespace() + "/" + transaction.topic() + " " + trace_operation_name; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {traceSampler()}); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, trace_operation_name); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, trace_operation_name); + // TracingUtility::addUniversalSpanAttributes(message, config(), span); } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, trace_operation_name); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, trace_operation_name); - TracingUtility::addUniversalSpanAttributes(message, *this, span); - - absl::Mutex mtx; - absl::CondVar cv; - auto cb = [&, span](const std::error_code& ec, const EndTransactionResponse& response) { - completed = true; + + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + const auto& endpoint = transaction.endpoint(); + auto cb = [&, span, endpoint, mtx, cv](const std::error_code& ec, const EndTransactionResponse& response) { if (ec) { { span.SetStatus(opencensus::trace::StatusCode::ABORTED); span.AddAnnotation(ec.message()); span.End(); } - SPDLOG_WARN("Failed to send {} transaction request to {}. Cause: ", action, target, ec.message()); + SPDLOG_WARN("Failed to send {} transaction request to {}. Cause: ", action, endpoint, ec.message()); success = false; } else { { @@ -439,15 +414,17 @@ bool ProducerImpl::endTransaction0(const std::string& target, const MQMessage& m } { - absl::MutexLock lk(&mtx); - cv.SignalAll(); + absl::MutexLock lk(mtx.get()); + completed = true; + cv->SignalAll(); } }; - client_manager_->endTransaction(target, metadata, request, absl::ToChronoMilliseconds(io_timeout_), cb); + client_manager_->endTransaction(transaction.endpoint(), metadata, request, + absl::ToChronoMilliseconds(requestTimeout()), cb); { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); + absl::MutexLock lk(mtx.get()); + cv->Wait(mtx.get()); } return success; } @@ -457,35 +434,6 @@ void ProducerImpl::isolatedEndpoints(absl::flat_hash_set& endpoints endpoints.insert(isolated_endpoints_.begin(), isolated_endpoints_.end()); } -MQMessageQueue ProducerImpl::withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec) { - if (!message_queue.serviceAddress().empty()) { - return message_queue; - } - - if (message_queue.getTopic().empty() || message_queue.getBrokerName().empty() || message_queue.getQueueId() < 0) { - ec = ErrorCode::BadRequest; - return {}; - } - - std::vector list = listMessageQueue(message_queue.getTopic(), ec); - if (ec) { - return {}; - } - - for (const auto& item : list) { - if (item == message_queue) { - return item; - } - } - - if (list.empty()) { - ec = ErrorCode::NotFound; - return {}; - } else { - return *list.begin(); - } -} - bool ProducerImpl::isEndpointIsolated(const std::string& target) { absl::MutexLock lk(&isolated_endpoints_mtx_); return isolated_endpoints_.contains(target); @@ -496,28 +444,31 @@ void ProducerImpl::isolateEndpoint(const std::string& target) { isolated_endpoints_.insert(target); } -std::unique_ptr ProducerImpl::prepare(MQMessage& message, std::error_code& ec) { - message.messageType(MessageType::TRANSACTION); - SendResult send_result = send(message, ec); +std::unique_ptr ProducerImpl::prepare(MessageConstPtr message, std::error_code& ec) { + std::weak_ptr producer(shared_from_this()); + auto transaction = absl::make_unique(message->topic(), message->id(), + message->traceContext().value_or(""), producer); + SendReceipt send_receipt = send(std::move(message), ec); if (ec) { return nullptr; } - return absl::make_unique(message, send_result.getTransactionId(), - send_result.getMessageQueue().serviceAddress(), send_result.traceContext(), - ProducerImpl::shared_from_this()); + transaction->transactionId(send_receipt.transaction_id); + + // TODO: endpoint id + // transaction->endpoint(xxx); + return transaction; } -bool ProducerImpl::commit(const MQMessage& message, const std::string& transaction_id, const std::string& target) { - return endTransaction0(target, message, transaction_id, TransactionState::COMMIT); +bool ProducerImpl::commit(const Transaction& transaction) { + return endTransaction0(transaction, TransactionState::COMMIT); } -bool ProducerImpl::rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target) { - return endTransaction0(target, message, transaction_id, TransactionState::ROLLBACK); +bool ProducerImpl::rollback(const Transaction& transaction) { + return endTransaction0(transaction, TransactionState::ROLLBACK); } -void ProducerImpl::asyncPublishInfo(const std::string& topic, - const std::function& cb) { +void ProducerImpl::getPublishInfoAsync(const std::string& topic, const PublishInfoCallback& cb) { TopicPublishInfoPtr ptr; { absl::MutexLock lock(&topic_publish_info_mtx_); @@ -525,101 +476,97 @@ void ProducerImpl::asyncPublishInfo(const std::string& topic, ptr = topic_publish_info_table_.at(topic); } } + std::error_code ec; if (ptr) { + // Serve with the cached one cb(ec, ptr); - } else { - auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& route) { - if (ec) { - cb(ec, nullptr); - return; - } + return; + } - auto publish_info = std::make_shared(topic, route); - { - absl::MutexLock lk(&topic_publish_info_mtx_); - topic_publish_info_table_.insert_or_assign(topic, publish_info); - } - cb(ec, publish_info); - }; + std::weak_ptr producer(shared_from_this()); + auto callback = [topic, cb, producer](const std::error_code& ec, const TopicRouteDataPtr& route) { + if (ec) { + cb(ec, nullptr); + return; + } + auto publish_info = std::make_shared(producer, topic, route); + auto impl = producer.lock(); + if (impl) { + impl->cachePublishInfo(topic, publish_info); + } + cb(ec, publish_info); + }; - getRouteFor(topic, callback); - } + getRouteFor(topic, callback); +} + +void ProducerImpl::cachePublishInfo(const std::string& topic, TopicPublishInfoPtr info) { + absl::MutexLock lk(&topic_publish_info_mtx_); + topic_publish_info_table_.insert_or_assign(topic, info); } TopicPublishInfoPtr ProducerImpl::getPublishInfo(const std::string& topic) { bool complete = false; - absl::Mutex mtx; - absl::CondVar cv; + auto mtx = std::make_shared(); + auto cv = std::make_shared(); TopicPublishInfoPtr topic_publish_info; std::error_code error_code; - auto cb = [&](const std::error_code& ec, const TopicPublishInfoPtr& ptr) { - absl::MutexLock lk(&mtx); + auto cb = [&, mtx, cv](const std::error_code& ec, const TopicPublishInfoPtr& ptr) { + absl::MutexLock lk(mtx.get()); topic_publish_info = ptr; error_code = ec; complete = true; - cv.SignalAll(); + cv->SignalAll(); }; - asyncPublishInfo(topic, cb); + getPublishInfoAsync(topic, cb); // Wait till acquiring topic publish info completes while (!complete) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); + absl::MutexLock lk(mtx.get()); + cv->Wait(mtx.get()); } // TODO: propogate error_code to caller return topic_publish_info; } -void ProducerImpl::takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, - std::vector& message_queues, int number) { - assert(publish_info); - absl::flat_hash_set isolated; - isolatedEndpoints(isolated); - publish_info->takeMessageQueues(isolated, message_queues, number); +void ProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + request.set_client_type(rmq::ClientType::PRODUCER); } -std::vector ProducerImpl::listMessageQueue(const std::string& topic, std::error_code& ec) { - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - TopicPublishInfoPtr ptr; - auto await_callback = [&](const std::error_code& error_code, const TopicPublishInfoPtr& publish_info) { - absl::MutexLock lk(&mtx); - ptr = publish_info; - ec = error_code; - completed = true; - cv.SignalAll(); - }; - - asyncPublishInfo(topic, await_callback); - - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } +void ProducerImpl::onOrphanedTransactionalMessage(MessageConstSharedPtr message) { + if (transaction_checker_) { + std::weak_ptr producer(shared_from_this()); - if (ec) { - return {}; + auto transaction = absl::make_unique(message->topic(), message->id(), + message->traceContext().value_or(""), producer); + transaction->endpoint(message->extension().target_endpoint); + transaction->transactionId(message->extension().transaction_id); + TransactionState state = transaction_checker_(*message); + endTransaction0(*transaction, state); + } else { + SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); } - - return ptr->getMessageQueueList(); } -void ProducerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - request.set_client_id(clientId()); - request.mutable_producer_data()->mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_producer_data()->mutable_group()->set_name(group_name_); +void ProducerImpl::topicsOfInterest(std::vector topics) { + absl::MutexLock lk(&topics_mtx_); + topics_.swap(topics); } -void ProducerImpl::resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { - if (transaction_state_checker_) { - TransactionState state = transaction_state_checker_->checkLocalTransactionState(message); - const std::string& target_host = MessageAccessor::targetEndpoint(message); - endTransaction0(target_host, message, transaction_id, state); - } else { - SPDLOG_WARN("LocalTransactionStateChecker is unexpectedly nullptr"); +void ProducerImpl::buildClientSettings(rmq::Settings& settings) { + settings.set_client_type(rmq::ClientType::PRODUCER); + + auto topics = settings.mutable_publishing()->mutable_topics(); + { + absl::MutexLock lk(&topics_mtx_); + for (const auto& item : topics_) { + auto topic = new rmq::Resource; + topic->set_resource_namespace(resourceNamespace()); + topic->set_name(item); + topics->AddAllocated(topic); + } } } diff --git a/src/main/cpp/rocketmq/PullConsumerImpl.cpp b/src/main/cpp/rocketmq/PullConsumerImpl.cpp deleted file mode 100644 index 530f2f3c8..000000000 --- a/src/main/cpp/rocketmq/PullConsumerImpl.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "PullConsumerImpl.h" -#include "ClientManagerFactory.h" -#include "InvocationContext.h" -#include "Signature.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "rocketmq/ErrorCode.h" -#include "rocketmq/MQClientException.h" -#include "rocketmq/MessageModel.h" -#include "rocketmq/PullResult.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -void PullConsumerImpl::start() { - ClientImpl::start(); - if (State::STARTED != state_.load(std::memory_order_relaxed)) { - SPDLOG_WARN("Unexpected state: {}", state_.load(std::memory_order_relaxed)); - return; - } - client_manager_->addClientObserver(shared_from_this()); -} - -void PullConsumerImpl::shutdown() { - // Shutdown services started by current tier - - notifyClientTermination(); - - // Shutdown services that are started by the parent - ClientImpl::shutdown(); - State expected = State::STOPPING; - if (state_.compare_exchange_strong(expected, State::STOPPED)) { - SPDLOG_INFO("DefaultMQPullConsumerImpl stopped"); - } -} - -std::future> PullConsumerImpl::queuesFor(const std::string& topic) { - auto promise = std::make_shared>>(); - { - absl::MutexLock lk(&topic_route_table_mtx_); - if (topic_route_table_.contains(topic)) { - TopicRouteDataPtr topic_route = topic_route_table_.at(topic); - auto partitions = topic_route->partitions(); - std::vector message_queues; - message_queues.reserve(partitions.size()); - for (const auto& partition : partitions) { - message_queues.emplace_back(partition.asMessageQueue()); - } - promise->set_value(std::move(message_queues)); - return promise->get_future(); - } - } - - auto callback = [promise](const std::error_code& ec, const TopicRouteDataPtr& route) { - if (ec) { - MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); - promise->set_exception(std::make_exception_ptr(e)); - return; - } - - std::vector message_queues; - for (const auto& partition : route->partitions()) { - message_queues.emplace_back(partition.asMessageQueue()); - } - promise->set_value(message_queues); - }; - - getRouteFor(topic, callback); - return promise->get_future(); -} - -std::future PullConsumerImpl::queryOffset(const OffsetQuery& query) { - QueryOffsetRequest request; - switch (query.policy) { - case QueryOffsetPolicy::BEGINNING: - request.set_policy(rmq::QueryOffsetPolicy::BEGINNING); - break; - case QueryOffsetPolicy::END: - request.set_policy(rmq::QueryOffsetPolicy::END); - break; - case QueryOffsetPolicy::TIME_POINT: - request.set_policy(rmq::QueryOffsetPolicy::TIME_POINT); - auto duration = absl::FromChrono(query.time_point.time_since_epoch()); - int64_t seconds = absl::ToInt64Seconds(duration); - request.mutable_time_point()->set_seconds(seconds); - request.mutable_time_point()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); - break; - } - - request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); - request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - - request.mutable_partition()->set_id(query.message_queue.getQueueId()); - - absl::flat_hash_map metadata; - - Signature::sign(this, metadata); - - // TODO: Use std::unique_ptr if C++14 is adopted. - auto promise_ptr = std::make_shared>(); - auto callback = [promise_ptr](const std::error_code& ec, const QueryOffsetResponse& response) { - if (ec) { - MQClientException e(ec.message(), ec.value(), __FILE__, __LINE__); - promise_ptr->set_exception(std::make_exception_ptr(e)); - return; - } - promise_ptr->set_value(response.offset()); - }; - - client_manager_->queryOffset(query.message_queue.serviceAddress(), metadata, request, - absl::ToChronoMilliseconds(io_timeout_), callback); - return promise_ptr->get_future(); -} - -void PullConsumerImpl::pull(const PullMessageQuery& query, PullCallback* cb) { - PullMessageRequest request; - request.set_offset(query.offset); - auto duration = absl::FromChrono(query.await_time); - int64_t seconds = absl::ToInt64Seconds(duration); - request.mutable_await_time()->set_seconds(seconds); - request.mutable_await_time()->set_nanos(absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); - request.mutable_group()->set_name(group_name_); - request.mutable_group()->set_resource_namespace(resource_namespace_); - - request.mutable_partition()->mutable_topic()->set_name(query.message_queue.getTopic()); - request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_partition()->set_id(query.message_queue.getQueueId()); - request.set_client_id(clientId()); - - std::string target_host = query.message_queue.serviceAddress(); - assert(!target_host.empty()); - - auto callback = [target_host, cb](const std::error_code& ec, const ReceiveMessageResult& result) { - if (ec) { - cb->onFailure(ec); - return; - } - - PullResult pull_result(result.min_offset, result.max_offset, result.next_offset, result.messages); - cb->onSuccess(pull_result); - }; - - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - - client_manager_->pullMessage(target_host, metadata, request, absl::ToChronoMilliseconds(long_polling_timeout_), - callback); -} - -void PullConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - request.set_client_id(clientId()); - auto consumer_data = request.mutable_consumer_data(); - consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); - consumer_data->mutable_group()->set_name(group_name_); - switch (message_model_) { - case MessageModel::BROADCASTING: - consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); - break; - case MessageModel::CLUSTERING: - consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); - break; - } -} - -void PullConsumerImpl::notifyClientTermination() { - NotifyClientTerminationRequest request; - request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); - request.mutable_consumer_group()->set_name(group_name_); - request.set_client_id(clientId()); - ClientImpl::notifyClientTermination(request); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumer.cpp b/src/main/cpp/rocketmq/PushConsumer.cpp new file mode 100644 index 000000000..daabf015a --- /dev/null +++ b/src/main/cpp/rocketmq/PushConsumer.cpp @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "DynamicNameServerResolver.h" +#include "PushConsumerImpl.h" +#include "StaticNameServerResolver.h" +#include "rocketmq/PushConsumer.h" + +ROCKETMQ_NAMESPACE_BEGIN + +void PushConsumer::subscribe(std::string topic, FilterExpression filter_expression) { + impl_->subscribe(std::move(topic), filter_expression.content_, filter_expression.type_); +} + +void PushConsumer::unsubscribe(const std::string& topic) { + impl_->unsubscribe(topic); +} + +PushConsumerBuilder PushConsumer::newBuilder() { + return {}; +} + +PushConsumer PushConsumerBuilder::build() { + auto impl = std::make_shared(group_); + impl->registerMessageListener(listener_); + for (const auto& entry : subscriptions_) { + impl->subscribe(entry.first, entry.second.content_, entry.second.type_); + } + impl->consumeThreadPoolSize(consume_thread_); + impl->withNameServerResolver(std::make_shared(configuration_.endpoints())); + impl->withCredentialsProvider(configuration_.credentialsProvider()); + impl->withRequestTimeout(configuration_.requestTimeout()); + impl->start(); + return PushConsumer(impl); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index 6691716dc..cd94d9f41 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -22,20 +22,18 @@ #include #include -#include "apache/rocketmq/v1/definition.pb.h" - #include "AsyncReceiveMessageCallback.h" #include "ClientManagerFactory.h" #include "ConsumeFifoMessageService.h" #include "ConsumeStandardMessageService.h" -#include "MessageAccessor.h" #include "MixAll.h" #include "ProcessQueueImpl.h" +#include "Protocol.h" #include "RpcClient.h" #include "Signature.h" +#include "google/protobuf/util/time_util.h" #include "rocketmq/MQClientException.h" #include "rocketmq/MessageListener.h" -#include "rocketmq/MessageModel.h" ROCKETMQ_NAMESPACE_BEGIN @@ -44,6 +42,14 @@ PushConsumerImpl::PushConsumerImpl(absl::string_view group_name) : ClientImpl(gr PushConsumerImpl::~PushConsumerImpl() { SPDLOG_DEBUG("DefaultMQPushConsumerImpl is destructed"); + shutdown(); +} + +void PushConsumerImpl::topicsOfInterest(std::vector& topics) { + absl::MutexLock lk(&topic_filter_expression_table_mtx_); + for (const auto& entry : topic_filter_expression_table_) { + topics.push_back(entry.first); + } } void PushConsumerImpl::start() { @@ -57,7 +63,7 @@ void PushConsumerImpl::start() { } if (!message_listener_) { - SPDLOG_ERROR("Required message listener is nullptr"); + SPDLOG_ERROR("Required message listener is missing"); abort(); return; } @@ -66,14 +72,14 @@ void PushConsumerImpl::start() { fetchRoutes(); - if (message_listener_->listenerType() == MessageListenerType::FIFO) { - SPDLOG_INFO("start orderly consume service: {}", group_name_); + if (client_config_.subscriber.fifo) { + SPDLOG_INFO("start orderly consume service: {}", client_config_.subscriber.group.name()); consume_message_service_ = std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); consume_batch_size_ = 1; } else { // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. - SPDLOG_INFO("start concurrently consume service: {}", group_name_); + SPDLOG_INFO("start concurrently consume service: {}", client_config_.subscriber.group.name()); consume_message_service_ = std::make_shared( shared_from_this(), consume_thread_pool_size_, message_listener_); } @@ -100,8 +106,7 @@ void PushConsumerImpl::start() { scan_assignment_handle_ = client_manager_->getScheduler()->schedule( scan_assignment_functor, SCAN_ASSIGNMENT_TASK_NAME, std::chrono::milliseconds(100), std::chrono::seconds(5)); - - SPDLOG_INFO("PushConsumer started, groupName={}", group_name_); + SPDLOG_INFO("PushConsumer started, groupName={}", client_config_.subscriber.group.name()); } const char* PushConsumerImpl::SCAN_ASSIGNMENT_TASK_NAME = "scan-assignment-task"; @@ -151,15 +156,11 @@ absl::optional PushConsumerImpl::getFilterExpression(const std if (topic_filter_expression_table_.contains(topic)) { return absl::make_optional(topic_filter_expression_table_.at(topic)); } else { - return absl::optional(); + return {}; } } } -void PushConsumerImpl::setConsumeFromWhere(ConsumeFromWhere consume_from_where) { - consume_from_where_ = consume_from_where; -} - void PushConsumerImpl::scanAssignments() { SPDLOG_DEBUG("Start of assignment scanning"); if (!active()) { @@ -188,81 +189,62 @@ void PushConsumerImpl::scanAssignments() { } bool PushConsumerImpl::selectBroker(const TopicRouteDataPtr& topic_route_data, std::string& broker_host) { - if (topic_route_data && !topic_route_data->partitions().empty()) { + if (topic_route_data && !topic_route_data->messageQueues().empty()) { uint32_t index = TopicAssignment::getAndIncreaseQueryWhichBroker(); - for (uint32_t i = index; i < index + topic_route_data->partitions().size(); i++) { - auto partition = topic_route_data->partitions().at(i % topic_route_data->partitions().size()); - if (MixAll::MASTER_BROKER_ID != partition.broker().id() || Permission::NONE == partition.permission()) { + for (uint32_t i = index; i < index + topic_route_data->messageQueues().size(); i++) { + auto message_queue = topic_route_data->messageQueues().at(i % topic_route_data->messageQueues().size()); + if (MixAll::MASTER_BROKER_ID != message_queue.broker().id() || !readable(message_queue.permission())) { continue; } - - if (partition.broker()) { - broker_host = partition.broker().serviceAddress(); - return true; - } + broker_host = urlOf(message_queue); + return true; } } return false; } -void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, - const std::string& client_id, const std::string& strategy_name, +void PushConsumerImpl::wrapQueryAssignmentRequest(const std::string& topic, + const std::string& consumer_group, + const std::string& strategy_name, QueryAssignmentRequest& request) { + request.mutable_endpoints()->CopyFrom(accessPoint()); request.mutable_topic()->set_name(topic); request.mutable_topic()->set_resource_namespace(resourceNamespace()); request.mutable_group()->set_name(consumer_group); request.mutable_group()->set_resource_namespace(resourceNamespace()); - request.set_client_id(client_id); } void PushConsumerImpl::queryAssignment( const std::string& topic, const std::function& cb) { - auto callback = [this, topic, cb](const std::error_code& ec, const TopicRouteDataPtr& topic_route) { TopicAssignmentPtr topic_assignment; - if (MessageModel::BROADCASTING == message_model_) { - if (ec) { - SPDLOG_WARN("Failed to get valid route entries for topic={}. Cause: {}", topic, ec.message()); - cb(ec, topic_assignment); - } - - std::vector assignments; - assignments.reserve(topic_route->partitions().size()); - for (const auto& partition : topic_route->partitions()) { - assignments.emplace_back(Assignment(partition.asMessageQueue())); - } - topic_assignment = std::make_shared(std::move(assignments)); - cb(ec, topic_assignment); - return; - } - std::string broker_host; if (!selectBroker(topic_route, broker_host)) { - SPDLOG_WARN("Failed to select a broker to query assignment for group={}, topic={}", group_name_, topic); + SPDLOG_WARN("Failed to select a broker to query assignment for group={}, topic={}", + client_config_.subscriber.group.name(), topic); } QueryAssignmentRequest request; - setAccessPoint(request.mutable_endpoints()); - wrapQueryAssignmentRequest(topic, group_name_, clientId(), MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_, request); + wrapQueryAssignmentRequest(topic, groupName(), MixAll::DEFAULT_LOAD_BALANCER_STRATEGY_NAME_, request); SPDLOG_DEBUG("QueryAssignmentRequest: {}", request.DebugString()); absl::flat_hash_map metadata; - Signature::sign(this, metadata); + Signature::sign(client_config_, metadata); auto assignment_callback = [this, cb, topic, broker_host](const std::error_code& ec, const QueryAssignmentResponse& response) { if (ec) { SPDLOG_WARN("Failed to acquire queue assignment of topic={} from brokerAddress={}", topic, broker_host); cb(ec, nullptr); } else { - SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, group_name_, + SPDLOG_DEBUG("Query topic assignment OK. Topic={}, group={}, assignment-size={}", topic, groupName(), response.assignments().size()); SPDLOG_TRACE("Query assignment response for {} is: {}", topic, response.DebugString()); cb(ec, std::make_shared(response)); } }; - client_manager_->queryAssignment(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), - assignment_callback); + client_manager_->queryAssignment(broker_host, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout), assignment_callback); }; getRouteFor(topic, callback); } @@ -276,35 +258,36 @@ void PushConsumerImpl::queryAssignment( void PushConsumerImpl::syncProcessQueue(const std::string& topic, const std::shared_ptr& topic_assignment, const FilterExpression& filter_expression) { - const std::vector& assignment_list = topic_assignment->assignmentList(); - std::vector message_queue_list; + const std::vector& assignment_list = topic_assignment->assignmentList(); + std::vector message_queue_list; message_queue_list.reserve(assignment_list.size()); for (const auto& assignment : assignment_list) { - message_queue_list.push_back(assignment.messageQueue()); + message_queue_list.push_back(assignment.message_queue()); } - std::vector current; + std::vector current; { absl::MutexLock lock(&process_queue_table_mtx_); for (auto it = process_queue_table_.begin(); it != process_queue_table_.end();) { - if (topic != it->first.getTopic()) { + if (topic != it->second->messageQueue().topic().name()) { it++; continue; } - if (std::none_of(message_queue_list.cbegin(), message_queue_list.cend(), - [&](const MQMessageQueue& message_queue) { return it->first == message_queue; })) { + if (std::none_of( + message_queue_list.cbegin(), message_queue_list.cend(), + [&](const rmq::MessageQueue& message_queue) { return it->second->messageQueue() == message_queue; })) { SPDLOG_INFO("Stop receiving messages from {} as it is not assigned to current client according to latest " "assignment result from load balancer", - it->first.simpleName()); + simpleNameOf(it->second->messageQueue())); process_queue_table_.erase(it++); } else { if (!it->second || it->second->expired()) { - SPDLOG_WARN("ProcessQueue={} is expired. Remove it for now.", it->first.simpleName()); + SPDLOG_WARN("ProcessQueue={} is expired. Remove it for now.", it->first); process_queue_table_.erase(it++); continue; } - current.push_back(it->first); + current.push_back(it->second->messageQueue()); it++; } } @@ -312,12 +295,12 @@ void PushConsumerImpl::syncProcessQueue(const std::string& topic, for (const auto& message_queue : message_queue_list) { if (std::none_of(current.cbegin(), current.cend(), - [&](const MQMessageQueue& item) { return item == message_queue; })) { + [&](const rmq::MessageQueue& item) { return item == message_queue; })) { SPDLOG_INFO("Start to receive message from {} according to latest assignment info from load balancer", - message_queue.simpleName()); + simpleNameOf(message_queue)); if (!receiveMessage(message_queue, filter_expression)) { if (!active()) { - SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", message_queue.simpleName()); + SPDLOG_WARN("Failed to initiate receive message request-response-cycle for {}", simpleNameOf(message_queue)); // TODO: remove it from current assignment such that a second attempt will be made again in the next round. } } @@ -325,9 +308,9 @@ void PushConsumerImpl::syncProcessQueue(const std::string& topic, } } -ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression) { - ProcessQueueSharedPtr process_queue; +std::shared_ptr PushConsumerImpl::getOrCreateProcessQueue(const rmq::MessageQueue& message_queue, + const FilterExpression& filter_expression) { + std::shared_ptr process_queue; { absl::MutexLock lock(&process_queue_table_mtx_); if (!active()) { @@ -335,72 +318,40 @@ ProcessQueueSharedPtr PushConsumerImpl::getOrCreateProcessQueue(const MQMessageQ return process_queue; } - if (process_queue_table_.contains(message_queue)) { - process_queue = process_queue_table_.at(message_queue); + if (process_queue_table_.contains(simpleNameOf(message_queue))) { + process_queue = process_queue_table_.at(simpleNameOf(message_queue)); } else { - SPDLOG_INFO("Create ProcessQueue for message queue[{}]", message_queue.simpleName()); + SPDLOG_INFO("Create ProcessQueue for message queue[{}]", simpleNameOf(message_queue)); // create ProcessQueue process_queue = std::make_shared(message_queue, filter_expression, shared_from_this(), client_manager_); std::shared_ptr receive_callback = std::make_shared(process_queue); process_queue->callback(receive_callback); - process_queue_table_.emplace(std::make_pair(message_queue, process_queue)); + process_queue_table_.emplace(std::make_pair(simpleNameOf(message_queue), process_queue)); } } return process_queue; } -bool PushConsumerImpl::receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) { +bool PushConsumerImpl::receiveMessage(const rmq::MessageQueue& message_queue, + const FilterExpression& filter_expression) { if (!active()) { SPDLOG_INFO("PushConsumer has stopped. Drop further receive message request"); return false; } - ProcessQueueSharedPtr process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression); + auto process_queue_ptr = getOrCreateProcessQueue(message_queue, filter_expression); if (!process_queue_ptr) { SPDLOG_INFO("Consumer has stopped. Stop creating processQueue"); return false; } - - const std::string& broker_host = message_queue.serviceAddress(); + const std::string& broker_host = urlOf(message_queue); if (broker_host.empty()) { - SPDLOG_ERROR("Failed to resolve address for brokerName={}", message_queue.getBrokerName()); + SPDLOG_ERROR("Failed to resolve address for brokerName={}", message_queue.broker().name()); return false; } - - switch (message_model_) { - case MessageModel::BROADCASTING: { - int64_t offset = -1; - if (!offset_store_ || !offset_store_->readOffset(message_queue, offset)) { - // Query latest offset from server. - QueryOffsetRequest request; - request.mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_partition()->mutable_topic()->set_name(message_queue.getTopic()); - request.mutable_partition()->set_id(message_queue.getQueueId()); - request.mutable_partition()->mutable_broker()->set_name(message_queue.getBrokerName()); - request.set_policy(rmq::QueryOffsetPolicy::END); - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - auto callback = [broker_host, message_queue, process_queue_ptr](const std::error_code& ec, - const QueryOffsetResponse& response) { - if (ec) { - SPDLOG_WARN("Failed to acquire latest offset for partition[{}] from server[host={}]. Cause: {}", - message_queue.simpleName(), broker_host, ec.message()); - } else { - assert(response.offset() >= 0); - process_queue_ptr->nextOffset(response.offset()); - process_queue_ptr->receiveMessage(); - } - }; - client_manager_->queryOffset(broker_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); - } - break; - } - case MessageModel::CLUSTERING: - process_queue_ptr->receiveMessage(); - break; - } + process_queue_ptr->receiveMessage(); return true; } @@ -408,73 +359,72 @@ std::shared_ptr PushConsumerImpl::getConsumeMessageServic return consume_message_service_; } -void PushConsumerImpl::ack(const MQMessageExt& msg, const std::function& callback) { - const std::string& target_host = MessageAccessor::targetEndpoint(msg); +void PushConsumerImpl::ack(const Message& msg, const std::function& callback) { + const std::string& target_host = msg.extension().target_endpoint; assert(!target_host.empty()); SPDLOG_DEBUG("Prepare to send ack to broker. BrokerAddress={}, topic={}, queueId={}, msgId={}", target_host, - msg.getTopic(), msg.getQueueId(), msg.getMsgId()); + msg.topic(), msg.extension().queue_id, msg.id()); AckMessageRequest request; wrapAckMessageRequest(msg, request); absl::flat_hash_map metadata; - Signature::sign(this, metadata); - client_manager_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); + Signature::sign(client_config_, metadata); + client_manager_->ack(target_host, metadata, request, absl::ToChronoMilliseconds(client_config_.request_timeout), + callback); } -void PushConsumerImpl::nack(const MQMessageExt& msg, const std::function& callback) { - std::string target_host = MessageAccessor::targetEndpoint(msg); - - absl::flat_hash_map metadata; - Signature::sign(this, metadata); - - rmq::NackMessageRequest request; - - // Group - request.mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_name(group_name_); - // Topic - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_topic()->set_name(msg.getTopic()); - request.set_client_id(clientId()); - request.set_receipt_handle(msg.receiptHandle()); - request.set_message_id(msg.getMsgId()); - request.set_delivery_attempt(msg.getDeliveryAttempt() + 1); - request.set_max_delivery_attempts(max_delivery_attempts_); +std::chrono::milliseconds PushConsumerImpl::invisibleDuration(std::size_t attempt) { + return std::chrono::milliseconds(client_config_.backoff_policy.backoff(attempt)); +} - client_manager_->nack(target_host, metadata, request, absl::ToChronoMilliseconds(io_timeout_), callback); - SPDLOG_DEBUG("Send message nack to broker server[host={}]", target_host); +void PushConsumerImpl::nack(const Message& message, const std::function& callback) { + const auto& target_host = message.extension().target_endpoint; + SPDLOG_DEBUG("Prepare to nack message[topic={}, message-id={}]", message.topic(), message.id()); + auto duration = invisibleDuration(message.extension().delivery_attempt + 1); + Metadata metadata; + Signature::sign(client_config_, metadata); + rmq::ChangeInvisibleDurationRequest request; + request.mutable_group()->CopyFrom(client_config_.subscriber.group); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_topic()->set_name(message.topic()); + request.set_receipt_handle(message.extension().receipt_handle); + request.set_message_id(message.id()); + request.mutable_invisible_duration()->CopyFrom( + google::protobuf::util::TimeUtil::MillisecondsToDuration(duration.count())); + client_manager_->changeInvisibleDuration(target_host, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout), callback); } -void PushConsumerImpl::forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) { - std::string target_host = MessageAccessor::targetEndpoint(message); +void PushConsumerImpl::forwardToDeadLetterQueue(const Message& message, const std::function& cb) { + std::string target_host = message.extension().target_endpoint; absl::flat_hash_map metadata; - Signature::sign(this, metadata); + Signature::sign(client_config_, metadata); ForwardMessageToDeadLetterQueueRequest request; - request.mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_name(group_name_); + request.mutable_group()->set_resource_namespace(resourceNamespace()); + request.mutable_group()->set_name(groupName()); - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_topic()->set_name(message.getTopic()); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_topic()->set_name(message.topic()); - request.set_client_id(clientId()); - request.set_message_id(message.getMsgId()); + request.set_message_id(message.id()); - request.set_delivery_attempt(message.getDeliveryAttempt()); + request.set_delivery_attempt(message.extension().delivery_attempt); request.set_max_delivery_attempts(max_delivery_attempts_); client_manager_->forwardMessageToDeadLetterQueue(target_host, metadata, request, - absl::ToChronoMilliseconds(io_timeout_), cb); + absl::ToChronoMilliseconds(client_config_.request_timeout), cb); } -void PushConsumerImpl::wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request) { - request.mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_name(group_name_); - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_topic()->set_name(msg.getTopic()); - request.set_client_id(clientId()); - request.set_message_id(msg.getMsgId()); - request.set_receipt_handle(msg.receiptHandle()); +void PushConsumerImpl::wrapAckMessageRequest(const Message& msg, AckMessageRequest& request) { + request.mutable_group()->set_resource_namespace(resourceNamespace()); + request.mutable_group()->set_name(groupName()); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_topic()->set_name(msg.topic()); + auto entry = new rmq::AckMessageEntry(); + entry->set_message_id(msg.id()); + entry->set_receipt_handle(msg.extension().receipt_handle); + request.mutable_entries()->AddAllocated(entry); } uint32_t PushConsumerImpl::consumeThreadPoolSize() const { @@ -487,23 +437,7 @@ void PushConsumerImpl::consumeThreadPoolSize(int thread_pool_size) { } } -uint32_t PushConsumerImpl::consumeBatchSize() const { - return consume_batch_size_; -} - -void PushConsumerImpl::consumeBatchSize(uint32_t consume_batch_size) { - - // For FIFO messages, consume batch size should always be 1. - if (message_listener_ && message_listener_->listenerType() == MessageListenerType::FIFO) { - return; - } - - if (consume_batch_size >= 1) { - consume_batch_size_ = consume_batch_size; - } -} - -void PushConsumerImpl::registerMessageListener(MessageListener* message_listener) { +void PushConsumerImpl::registerMessageListener(MessageListener message_listener) { message_listener_ = message_listener; } @@ -521,7 +455,7 @@ void PushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) } } -void PushConsumerImpl::iterateProcessQueue(const std::function& callback) { +void PushConsumerImpl::iterateProcessQueue(const std::function)>& callback) { absl::MutexLock lock(&process_queue_table_mtx_); for (const auto& item : process_queue_table_) { if (item.second->hasPendingMessages()) { @@ -544,15 +478,20 @@ void PushConsumerImpl::fetchRoutes() { } std::vector::size_type countdown = topics.size(); - absl::Mutex mtx; - absl::CondVar cv; + auto mtx = std::make_shared(); + auto cv = std::make_shared(); int acquired = 0; - auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr& route) { - absl::MutexLock lk(&mtx); - countdown--; - cv.SignalAll(); - if (!ec) { - acquired++; + auto callback = [&, mtx, cv](const std::error_code& ec, const TopicRouteDataPtr& route) { + { + absl::MutexLock lk(mtx.get()); + countdown--; + if (!ec) { + acquired++; + } + } + + if (countdown <= 0) { + cv->SignalAll(); } }; @@ -561,97 +500,92 @@ void PushConsumerImpl::fetchRoutes() { } while (countdown) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); + absl::MutexLock lk(mtx.get()); + cv->Wait(mtx.get()); } SPDLOG_INFO("Fetched route for {} out of {} topics", acquired, topics.size()); } -void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { - request.set_client_id(clientId()); - - if (message_listener_) { - switch (message_listener_->listenerType()) { - case MessageListenerType::FIFO: - request.set_fifo_flag(true); - break; - case MessageListenerType::STANDARD: - request.set_fifo_flag(false); - break; - } - } +void PushConsumerImpl::buildClientSettings(rmq::Settings& settings) { + settings.set_client_type(rmq::ClientType::PUSH_CONSUMER); + auto subscription = settings.mutable_subscription(); + subscription->mutable_group()->CopyFrom(client_config_.subscriber.group); + auto polling_timeout = google::protobuf::util::TimeUtil::MillisecondsToDuration( + absl::ToInt64Milliseconds(client_config_.subscriber.polling_timeout)); + subscription->mutable_long_polling_timeout()->set_seconds(polling_timeout.seconds()); + subscription->mutable_long_polling_timeout()->set_nanos(polling_timeout.nanos()); + subscription->set_receive_batch_size(client_config_.subscriber.receive_batch_size); - auto consumer_data = request.mutable_consumer_data(); - consumer_data->mutable_group()->set_name(group_name_); - consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); - - switch (message_model_) { - case MessageModel::BROADCASTING: - consumer_data->set_consume_model(rmq::ConsumeModel::BROADCASTING); - break; - case MessageModel::CLUSTERING: - consumer_data->set_consume_model(rmq::ConsumeModel::CLUSTERING); - break; - default: - break; - } - - auto subscriptions = consumer_data->mutable_subscriptions(); { absl::MutexLock lk(&topic_filter_expression_table_mtx_); for (const auto& entry : topic_filter_expression_table_) { - auto subscription = new rmq::SubscriptionEntry; - subscription->mutable_topic()->set_resource_namespace(resource_namespace_); - subscription->mutable_topic()->set_name(entry.first); - subscription->mutable_expression()->set_expression(entry.second.content_); + auto subscription_entry = new rmq::SubscriptionEntry; + subscription_entry->mutable_topic()->set_resource_namespace(resourceNamespace()); + subscription_entry->mutable_topic()->set_name(entry.first); + + subscription_entry->mutable_expression()->set_expression(entry.second.content_); switch (entry.second.type_) { - case ExpressionType::TAG: - subscription->mutable_expression()->set_type(rmq::FilterType::TAG); + case ExpressionType::TAG: { + subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); break; - case ExpressionType::SQL92: - subscription->mutable_expression()->set_type(rmq::FilterType::SQL); + } + case ExpressionType::SQL92: { + subscription_entry->mutable_expression()->set_type(rmq::FilterType::SQL); break; + } } - subscriptions->AddAllocated(subscription); - } - } - - assert(consume_message_service_); - switch (consume_message_service_->messageListenerType()) { - case MessageListenerType::FIFO: { - // TODO: Use enumeration in the protocol buffer specification? - request.set_fifo_flag(true); - break; - } - case MessageListenerType::STANDARD: { - request.set_fifo_flag(false); - break; + subscription->mutable_subscriptions()->AddAllocated(subscription_entry); } } - - consumer_data->set_consume_policy(rmq::ConsumePolicy::RESUME); - consumer_data->mutable_dead_letter_policy()->set_max_delivery_attempts(maxDeliveryAttempts()); - consumer_data->set_consume_type(rmq::ConsumeMessageType::PASSIVE); } -ClientResourceBundle PushConsumerImpl::resourceBundle() { - auto&& resource_bundle = ClientImpl::resourceBundle(); - resource_bundle.group_type = GroupType::SUBSCRIBER; - { - absl::MutexLock lk(&topic_filter_expression_table_mtx_); - for (auto& item : topic_filter_expression_table_) { - resource_bundle.topics.emplace_back(item.first); - } - } - return std::move(resource_bundle); +void PushConsumerImpl::prepareHeartbeatData(HeartbeatRequest& request) { + request.set_client_type(rmq::ClientType::PUSH_CONSUMER); + request.mutable_group()->set_resource_namespace(resourceNamespace()); + request.mutable_group()->set_name(groupName()); } void PushConsumerImpl::notifyClientTermination() { NotifyClientTerminationRequest request; - request.mutable_consumer_group()->set_resource_namespace(resource_namespace_); - request.mutable_consumer_group()->set_name(group_name_); - request.set_client_id(clientId()); + request.mutable_group()->set_resource_namespace(resourceNamespace()); + request.mutable_group()->set_name(groupName()); ClientImpl::notifyClientTermination(request); } +void PushConsumerImpl::onVerifyMessage(MessageConstSharedPtr message, std::function cb) { + auto listener = messageListener(); + TelemetryCommand cmd; + auto verify_result = cmd.mutable_verify_message_result(); + verify_result->set_nonce(message->extension().nonce); + if (message) { + if (!client_config_.subscriber.fifo) { + try { + auto result = listener(*message); + switch (result) { + case ConsumeResult::SUCCESS: { + cmd.mutable_status()->set_code(rmq::Code::OK); + cmd.mutable_status()->set_message("OK"); + } + case ConsumeResult::FAILURE: { + cmd.mutable_status()->set_code(rmq::Code::FAILED_TO_CONSUME_MESSAGE); + cmd.mutable_status()->set_message("Consume message failed"); + } + } + } catch (const std::exception& e) { + cmd.mutable_status()->set_code(rmq::Code::FAILED_TO_CONSUME_MESSAGE); + cmd.mutable_status()->set_message(e.what()); + } catch (...) { + cmd.mutable_status()->set_code(rmq::Code::FAILED_TO_CONSUME_MESSAGE); + cmd.mutable_status()->set_message("Unexpected exception raised"); + } + } else { + cmd.mutable_status()->set_code(rmq::Code::VERIFY_MESSAGE_FORBIDDEN); + cmd.mutable_status()->set_message("Unsupported Operation For FIFO Message"); + } + } else { + cmd.mutable_status()->set_code(rmq::Code::MESSAGE_CORRUPTED); + cmd.mutable_status()->set_message("Checksum Mismatch"); + } +} + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SendCallbacks.cpp b/src/main/cpp/rocketmq/SendContext.cpp similarity index 51% rename from src/main/cpp/rocketmq/SendCallbacks.cpp rename to src/main/cpp/rocketmq/SendContext.cpp index cddf1fc5a..73db9a1c0 100644 --- a/src/main/cpp/rocketmq/SendCallbacks.cpp +++ b/src/main/cpp/rocketmq/SendContext.cpp @@ -14,95 +14,65 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "SendCallbacks.h" +#include "SendContext.h" + +#include -#include "ProducerImpl.h" -#include "TransactionImpl.h" #include "opencensus/trace/propagation/trace_context.h" #include "opencensus/trace/span.h" -#include "rocketmq/Logger.h" -#include "rocketmq/MQMessageQueue.h" #include "spdlog/spdlog.h" -ROCKETMQ_NAMESPACE_BEGIN - -void OnewaySendCallback::onFailure(const std::error_code& ec) noexcept { - SPDLOG_WARN("Failed to one-way send message. Message: {}", ec.message()); -} - -void OnewaySendCallback::onSuccess(SendResult& send_result) noexcept { - SPDLOG_DEBUG("Send message in one-way OK. MessageId: {}", send_result.getMsgId()); -} - -OnewaySendCallback* onewaySendCallback() { - static OnewaySendCallback callback; - return &callback; -} - -void AwaitSendCallback::await() { - absl::MutexLock lk(&mtx_); - while (!completed_) { - cv_.Wait(&mtx_); - } -} - -void AwaitSendCallback::onSuccess(SendResult& send_result) noexcept { - send_result_ = send_result; - completed_ = true; - absl::MutexLock lk(&mtx_); - cv_.SignalAll(); -} +#include "ProducerImpl.h" +#include "TransactionImpl.h" +#include "rocketmq/Logger.h" +#include "rocketmq/SendReceipt.h" -void AwaitSendCallback::onFailure(const std::error_code& ec) noexcept { - completed_ = true; - ec_ = ec; - absl::MutexLock lk(&mtx_); - cv_.SignalAll(); -} +ROCKETMQ_NAMESPACE_BEGIN -void RetrySendCallback::onSuccess(SendResult& send_result) noexcept { +void SendContext::onSuccess(const SendReceipt& send_receipt) noexcept { { // Mark end of send-message span. span_.SetStatus(opencensus::trace::StatusCode::OK); span_.End(); } - send_result.setMessageQueue(messageQueue()); - send_result.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); - callback_->onSuccess(send_result); - delete this; + // send_receipt.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); + std::error_code ec; + callback_(ec, send_receipt); } -void RetrySendCallback::onFailure(const std::error_code& ec) noexcept { +void SendContext::onFailure(const std::error_code& ec) noexcept { { // Mark end of the send-message span. span_.SetStatus(opencensus::trace::StatusCode::INTERNAL); span_.End(); } - if (++attempt_times_ >= max_attempt_times_) { - SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, max_attempt_times_); - callback_->onFailure(ec); - delete this; + auto publisher = producer_.lock(); + if (!publisher) { + return; + } + + if (++attempt_times_ >= publisher->maxAttemptTimes()) { + SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, publisher->maxAttemptTimes()); + callback_(ec, {}); return; } std::shared_ptr producer = producer_.lock(); if (!producer) { SPDLOG_WARN("Producer has been destructed"); - callback_->onFailure(ec); - delete this; + callback_(ec, {}); return; } if (candidates_.empty()) { SPDLOG_WARN("No alternative hosts to perform additional retries"); - callback_->onFailure(ec); - delete this; + callback_(ec, {}); return; } - MQMessageQueue message_queue = candidates_[attempt_times_ % candidates_.size()]; - producer->sendImpl(this); + auto message_queue = candidates_[attempt_times_ % candidates_.size()]; + producer->sendImpl(shared_from_this()); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SimpleConsumer.cpp b/src/main/cpp/rocketmq/SimpleConsumer.cpp new file mode 100644 index 000000000..0c902d4b2 --- /dev/null +++ b/src/main/cpp/rocketmq/SimpleConsumer.cpp @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rocketmq/SimpleConsumer.h" + +#include "SimpleConsumerImpl.h" +#include "StaticNameServerResolver.h" + +ROCKETMQ_NAMESPACE_BEGIN + +SimpleConsumerBuilder::SimpleConsumerBuilder() : configuration_(Configuration::newBuilder().build()) { +} + +SimpleConsumerBuilder SimpleConsumer::newBuilder() { + return {}; +} + +SimpleConsumer::SimpleConsumer(std::string group) : impl_(std::make_shared(group)) { +} + +SimpleConsumer::~SimpleConsumer() { + impl_->shutdown(); +} + +void SimpleConsumer::start() { + impl_->start(); +} + +void SimpleConsumer::subscribe(std::string topic, FilterExpression filter_expression) { + impl_->subscribe(topic, filter_expression); +} + +void SimpleConsumer::unsubscribe(const std::string& topic) { + impl_->unsubscribe(topic); +} + +void SimpleConsumer::receive(std::size_t limit, + std::chrono::milliseconds invisible_duration, + std::error_code& ec, + std::vector& messages) { + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed = false; + auto callback = [&, mtx, cv](const std::error_code& code, const std::vector& result) { + { + absl::MutexLock lk(mtx.get()); + if (code) { + ec = code; + SPDLOG_WARN("Failed to receive message. Cause: {}", code.message()); + } + completed = true; + messages.insert(messages.end(), result.begin(), result.end()); + } + cv->SignalAll(); + }; + + impl_->receive(limit, invisible_duration, callback); + + { + absl::MutexLock lk(mtx.get()); + while (!completed) { + cv->Wait(mtx.get()); + } + } +} + +void SimpleConsumer::asyncReceive(std::size_t limit, + std::chrono::milliseconds invisible_duration, + ReceiveCallback callback) { + impl_->receive(limit, invisible_duration, callback); +} + +void SimpleConsumer::ack(const Message& message, std::error_code& ec) { + impl_->ack(message, ec); +} + +void SimpleConsumer::asyncAck(const Message& message, AckCallback callback) { + impl_->ackAsync(message, callback); +} + +void SimpleConsumer::changeInvisibleDuration(const Message& message, + std::chrono::milliseconds duration, + std::error_code& ec) { + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed = false; + auto callback = [&, mtx, cv](const std::error_code& code) { + { + absl::MutexLock lk(mtx.get()); + completed = true; + ec = code; + } + cv->Signal(); + }; + + impl_->changeInvisibleDuration(message, duration, callback); + + { + absl::MutexLock lk(mtx.get()); + if (!completed) { + cv->Wait(mtx.get()); + } + } +} + +void SimpleConsumer::asyncChangeInvisibleDuration(const Message& message, + std::chrono::milliseconds duration, + ChangeInvisibleDurationCallback callback) { + impl_->changeInvisibleDuration(message, duration, callback); +} + +SimpleConsumer SimpleConsumerBuilder::build() { + SimpleConsumer simple_consumer(group_); + + simple_consumer.impl_->withRequestTimeout(configuration_.requestTimeout()); + simple_consumer.impl_->withNameServerResolver(std::make_shared(configuration_.endpoints())); + simple_consumer.impl_->withCredentialsProvider(configuration_.credentialsProvider()); + + for (const auto& entry : subscriptions_) { + simple_consumer.impl_->subscribe(entry.first, entry.second); + } + + simple_consumer.start(); + return simple_consumer; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/SimpleConsumerImpl.cpp b/src/main/cpp/rocketmq/SimpleConsumerImpl.cpp new file mode 100644 index 000000000..82e6b4259 --- /dev/null +++ b/src/main/cpp/rocketmq/SimpleConsumerImpl.cpp @@ -0,0 +1,400 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SimpleConsumerImpl.h" + +#include "Signature.h" +#include "google/protobuf/util/time_util.h" +#include "rocketmq/ErrorCode.h" + +ROCKETMQ_NAMESPACE_BEGIN + +SimpleConsumerImpl::SimpleConsumerImpl(std::string group) : ClientImpl(group) { + client_config_.subscriber.polling_timeout = absl::FromChrono(MixAll::DefaultReceiveMessageTimeout); +} + +SimpleConsumerImpl::~SimpleConsumerImpl() { + shutdown(); +} + +void SimpleConsumerImpl::prepareHeartbeatData(rmq::HeartbeatRequest& request) { + request.set_client_type(rmq::ClientType::SIMPLE_CONSUMER); + request.mutable_group()->CopyFrom(client_config_.subscriber.group); +} + +void SimpleConsumerImpl::buildClientSettings(rmq::Settings& settings) { + settings.set_client_type(rmq::ClientType::SIMPLE_CONSUMER); + + settings.mutable_subscription()->mutable_group()->CopyFrom(client_config_.subscriber.group); + + auto subscriptions = settings.mutable_subscription()->mutable_subscriptions(); + + settings.mutable_access_point()->CopyFrom(accessPoint()); + { + absl::MutexLock lk(&subscriptions_mtx_); + for (const auto& entry : subscriptions_) { + auto subscription_entry = new rmq::SubscriptionEntry; + subscription_entry->mutable_topic()->set_name(entry.first); + subscription_entry->mutable_topic()->set_resource_namespace(resourceNamespace()); + + switch (entry.second.type_) { + case ExpressionType::TAG: { + subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); + break; + } + case ExpressionType::SQL92: { + subscription_entry->mutable_expression()->set_type(rmq::FilterType::SQL); + break; + } + } + subscription_entry->mutable_expression()->set_expression(entry.second.content_); + subscriptions->AddAllocated(subscription_entry); + } + } +} + +void SimpleConsumerImpl::topicsOfInterest(std::vector& topics) { + absl::MutexLock lk(&subscriptions_mtx_); + for (const auto& entry : subscriptions_) { + if (std::find(topics.begin(), topics.end(), entry.first) == topics.end()) { + topics.push_back(entry.first); + } + } +} + +/** + * @brief Start SimpleConsumer + * + * During start, we need synchronously fetch routes and query assignments + */ +void SimpleConsumerImpl::start() { + ClientImpl::start(); + State expected = State::STARTING; + if (state_.compare_exchange_strong(expected, State::STARTED, std::memory_order_relaxed)) { + refreshAssignments(); + + std::weak_ptr consumer(shared_from_this()); + auto refresh_assignment_task = [consumer]() { + auto simple_consumer = consumer.lock(); + if (simple_consumer) { + simple_consumer->refreshAssignments0(); + } + }; + refresh_assignment_task_ = manager()->getScheduler()->schedule(refresh_assignment_task, "RefreshAssignmentTask", + std::chrono::seconds(3), std::chrono::seconds(3)); + } +} + +void SimpleConsumerImpl::shutdown() { + State expected = State::STARTED; + + if (state_.compare_exchange_strong(expected, State::STOPPING, std::memory_order_relaxed)) { + manager()->getScheduler()->cancel(refresh_assignment_task_); + ClientImpl::shutdown(); + } +} + +void SimpleConsumerImpl::subscribe(std::string topic, FilterExpression expression) { + absl::MutexLock lk(&subscriptions_mtx_); + subscriptions_.insert_or_assign(topic, expression); +} + +void SimpleConsumerImpl::unsubscribe(const std::string& topic) { + { + absl::MutexLock lk(&subscriptions_mtx_); + subscriptions_.erase(topic); + } + + removeAssignmentsByTopic(topic); +} + +void SimpleConsumerImpl::removeAssignmentsByTopic(const std::string& topic) { + std::vector assignments; + { + absl::MutexLock lk(&topic_assignments_mtx_); + if (!topic_assignments_.contains(topic)) { + return; + } + + const auto& items = topic_assignments_[topic]; + assignments.insert(assignments.end(), items.begin(), items.end()); + topic_assignments_.erase(topic); + } + + { + absl::MutexLock lk(&assignments_mtx_); + auto it = std::remove_if(assignments_.begin(), assignments_.end(), [&](const rmq::Assignment& assignment) { + return std::find_if(assignments.begin(), assignments.end(), + [&](const rmq::Assignment& e) { return e == assignment; }) != assignments.end(); + }); + assignments_.erase(it, assignments_.end()); + } +} + +void SimpleConsumerImpl::refreshAssignments0() { + std::vector topics; + { + absl::MutexLock lk(&subscriptions_mtx_); + for (const auto& entry : subscriptions_) { + topics.push_back(entry.first); + } + } + + auto no_op = [](const std::error_code& ec) {}; + for (const auto& topic : topics) { + refreshAssignment(topic, no_op); + } +} + +void SimpleConsumerImpl::updateAssignments(const std::string& topic, const std::vector& assignments) { + bool changed = false; + { + absl::MutexLock lk(&topic_assignments_mtx_); + if (!topic_assignments_.contains(topic)) { + changed = true; + topic_assignments_.insert({topic, assignments}); + { + absl::MutexLock assignment_lk(&assignments_mtx_); + assignments_.insert(assignments_.begin(), assignments.begin(), assignments.end()); + } + } else if (!assignments.empty()) { + const auto& prev = topic_assignments_[topic]; + std::vector to_remove; + std::vector to_add; + for (const auto& item : prev) { + if (std::find_if(assignments.begin(), assignments.end(), [&](const rmq::Assignment& e) { return item == e; }) == + assignments.end()) { + to_remove.push_back(item); + } + } + + for (const auto& entry : assignments) { + if (std::find_if(prev.begin(), prev.end(), [&](const rmq::Assignment e) { return e == entry; }) == prev.end()) { + to_add.push_back(entry); + } + } + + if (!to_remove.empty() || !to_add.empty()) { + changed = true; + absl::MutexLock lk(&assignments_mtx_); + for (const auto& item : to_remove) { + std::remove_if(assignments_.begin(), assignments_.end(), [&](const rmq::Assignment& e) { return e == item; }); + } + + for (const auto& item : to_add) { + assignments_.push_back(item); + } + topic_assignments_.insert_or_assign(topic, assignments); + } + } + } + + if (changed) { + SPDLOG_DEBUG("Assignments for topic={} change to: {}", topic, + absl::StrJoin(assignments.begin(), assignments.end(), ",", + [](std::string* out, const rmq::Assignment& assignment) { + out->append(assignment.DebugString()); + })); + } +} + +thread_local std::size_t SimpleConsumerImpl::assignment_index_ = 0; + +void SimpleConsumerImpl::refreshAssignment(const std::string& topic, std::function cb) { + absl::flat_hash_set endpoints; + endpointsInUse(endpoints); + if (endpoints.empty()) { + SPDLOG_WARN("No broker is available"); + return; + } + + rmq::QueryAssignmentRequest query_assignment_request; + query_assignment_request.mutable_topic()->set_name(topic); + query_assignment_request.mutable_topic()->set_resource_namespace(resourceNamespace()); + + query_assignment_request.mutable_group()->CopyFrom(client_config_.subscriber.group); + query_assignment_request.mutable_endpoints()->CopyFrom(accessPoint()); + + Metadata metadata; + Signature::sign(client_config_, metadata); + + std::weak_ptr consumer(shared_from_this()); + auto callback = [consumer, topic, cb](const std::error_code& ec, const rmq::QueryAssignmentResponse& response) { + auto simple_consumer = consumer.lock(); + const auto& assignments = response.assignments(); + if (assignments.empty()) { + cb(ec); + return; + } + + std::vector assigns; + assigns.insert(assigns.begin(), assignments.begin(), assignments.end()); + simple_consumer->updateAssignments(topic, assigns); + cb(ec); + }; + + manager()->queryAssignment(*endpoints.begin(), metadata, query_assignment_request, + absl::ToChronoMilliseconds(client_config_.request_timeout), callback); +} + +void SimpleConsumerImpl::refreshAssignments() { + std::vector topics; + { + absl::MutexLock lk(&subscriptions_mtx_); + for (const auto& entry : subscriptions_) { + topics.push_back(entry.first); + } + } + + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed; + for (const auto& topic : topics) { + completed = false; + auto callback = [&, mtx, cv](const std::error_code& ec) { + absl::MutexLock lk(mtx.get()); + completed = true; + cv->Signal(); + }; + + refreshAssignment(topic, callback); + + { + absl::MutexLock lk(mtx.get()); + if (!completed) { + cv->Wait(mtx.get()); + } + } + SPDLOG_INFO("Assignments for {} received", topic); + } +} + +void SimpleConsumerImpl::receive(std::size_t limit, + std::chrono::milliseconds invisible_duration, + ReceiveCallback callback) { + rmq::Assignment assignment; + { + absl::MutexLock lk(&assignments_mtx_); + if (assignments_.empty()) { + std::error_code ec = ErrorCode::NotFound; + std::vector messages; + callback(ec, messages); + return; + } + std::size_t idx = ++assignment_index_ % assignments_.size(); + assignment.CopyFrom(assignments_[idx]); + } + + const auto& target = urlOf(assignment.message_queue()); + Metadata metadata; + Signature::sign(client_config_, metadata); + + rmq::ReceiveMessageRequest request; + request.set_auto_renew(false); + request.mutable_group()->CopyFrom(config().subscriber.group); + request.mutable_message_queue()->CopyFrom(assignment.message_queue()); + request.set_batch_size(limit); + + auto duration = google::protobuf::util::TimeUtil::MillisecondsToDuration(invisible_duration.count()); + + request.mutable_invisible_duration()->set_nanos(duration.nanos()); + request.mutable_invisible_duration()->set_seconds(duration.seconds()); + + auto cb = [callback](const std::error_code& ec, const ReceiveMessageResult& result) { + std::vector messages; + if (ec) { + callback(ec, messages); + return; + } + + callback(ec, result.messages); + }; + + auto timeout = absl::ToChronoMilliseconds(config().subscriber.polling_timeout); + SPDLOG_DEBUG("ReceiveMessage.polling_timeout: {}ms", timeout.count()); + manager()->receiveMessage(target, metadata, request, timeout, cb); +} + +void SimpleConsumerImpl::wrapAckRequest(const Message& message, AckMessageRequest& request) { + request.mutable_group()->CopyFrom(client_config_.subscriber.group); + + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_topic()->set_name(message.topic()); + + auto entry = new rmq::AckMessageEntry(); + entry->set_message_id(message.id()); + entry->set_receipt_handle(message.extension().receipt_handle); + + request.mutable_entries()->AddAllocated(entry); +} + +void SimpleConsumerImpl::ack(const Message& message, std::error_code& ec) { + Metadata metadata; + Signature::sign(client_config_, metadata); + AckMessageRequest request; + wrapAckRequest(message, request); + + auto mtx = std::make_shared(); + auto cv = std::make_shared(); + bool completed = false; + + auto callback = [&, mtx, cv](const std::error_code& err) { + absl::MutexLock lk(mtx.get()); + completed = true; + ec = err; + cv->Signal(); + }; + + manager()->ack(message.extension().target_endpoint, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout), callback); + + { + absl::MutexLock lk(mtx.get()); + if (!completed) { + cv->Wait(mtx.get()); + } + } +} + +void SimpleConsumerImpl::ackAsync(const Message& message, AckCallback callback) { + Metadata metadata; + Signature::sign(client_config_, metadata); + AckMessageRequest request; + wrapAckRequest(message, request); + manager()->ack(message.extension().target_endpoint, metadata, request, + absl::ToChronoMilliseconds(client_config_.request_timeout), callback); +} + +void SimpleConsumerImpl::changeInvisibleDuration(const Message& message, + std::chrono::milliseconds duration, + ChangeInvisibleDurationCallback callback) { + Metadata metadata; + Signature::sign(client_config_, metadata); + + rmq::ChangeInvisibleDurationRequest request; + request.mutable_group()->CopyFrom(client_config_.subscriber.group); + request.mutable_topic()->set_resource_namespace(resourceNamespace()); + request.mutable_topic()->set_name(message.topic()); + request.set_message_id(message.id()); + request.set_receipt_handle(message.extension().receipt_handle); + auto d = google::protobuf::util::TimeUtil::MillisecondsToDuration(duration.count()); + request.mutable_invisible_duration()->CopyFrom(d); + + manager()->changeInvisibleDuration(message.extension().target_endpoint, metadata, request, duration, callback); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TopicPublishInfo.cpp b/src/main/cpp/rocketmq/TopicPublishInfo.cpp new file mode 100644 index 000000000..9eac1a265 --- /dev/null +++ b/src/main/cpp/rocketmq/TopicPublishInfo.cpp @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TopicPublishInfo.h" + +#include +#include + +#include "LoggerImpl.h" +#include "MixAll.h" +#include "ProducerImpl.h" +#include "TopicRouteData.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_join.h" + +ROCKETMQ_NAMESPACE_BEGIN + +thread_local uint32_t TopicPublishInfo::send_which_queue_ = MixAll::random(0, 100); + +TopicPublishInfo::TopicPublishInfo(std::weak_ptr producer, + absl::string_view topic, + TopicRouteDataPtr topic_route_data) + : producer_(std::move(producer)), + topic_(topic.data(), topic.length()), + topic_route_data_(std::move(topic_route_data)) { + updatePublishInfo(); +} + +bool TopicPublishInfo::selectMessageQueues(absl::optional message_group, + std::vector& result) { + { + absl::MutexLock lock(&queue_list_mtx_); + if (queue_list_.empty()) { + SPDLOG_WARN("Failed to screen viable message queues from TopicPublishInfo because there is no candidates at all"); + return false; + } + } + + auto producer = producer_.lock(); + if (!producer) { + return false; + } + + // Select a message queue according to given message group, which is used to send FIFO message. + if (message_group.has_value()) { + std::size_t hash_code = std::hash{}(message_group.value()); + absl::MutexLock lk(&queue_list_mtx_); + std::size_t index = hash_code % queue_list_.size(); + result.push_back(queue_list_[index]); + return true; + } + + unsigned int index = ++send_which_queue_; + // For standard messages, select at most "count" message queues in case retries are required. + { + absl::MutexLock lock(&queue_list_mtx_); + for (std::vector::size_type i = 0; i < queue_list_.size(); ++i) { + const rmq::MessageQueue& message_queue = queue_list_[index++ % (queue_list_.size())]; + if (!producer->isEndpointIsolated(urlOf(message_queue))) { + auto search = std::find_if(result.begin(), result.end(), [&](const rmq::MessageQueue& item) { + return item.broker().name() == message_queue.broker().name(); + }); + if (std::end(result) == search) { + result.emplace_back(message_queue); + } + if (result.size() >= producer->maxAttemptTimes()) { + return true; + } + } + } + } + return !result.empty(); +} + +void TopicPublishInfo::updatePublishInfo() { + std::vector writable_queue_list; + { + for (const auto& queue : topic_route_data_->messageQueues()) { + assert(queue.topic().name() == topic_); + if (!writable(queue.permission())) { + continue; + } + if (MixAll::MASTER_BROKER_ID != queue.broker().id()) { + continue; + } + writable_queue_list.push_back(queue); + } + } + + if (writable_queue_list.empty()) { + SPDLOG_WARN("No writable queue is current available. Skip updating publish table for topic={}", topic_); + return; + } + + { + absl::MutexLock lk(&queue_list_mtx_); + queue_list_.swap(writable_queue_list); + } +} + +void TopicPublishInfo::topicRouteData(TopicRouteDataPtr topic_route_data) { + SPDLOG_DEBUG("Update publish info according to renewed route data of topic={}", topic_); + topic_route_data_ = std::move(topic_route_data); + updatePublishInfo(); +} + +std::vector TopicPublishInfo::getMessageQueueList() { + absl::MutexLock lock(&queue_list_mtx_); + return queue_list_; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/TransactionImpl.cpp b/src/main/cpp/rocketmq/TransactionImpl.cpp index 11f9ea3c9..2d2bf66cc 100644 --- a/src/main/cpp/rocketmq/TransactionImpl.cpp +++ b/src/main/cpp/rocketmq/TransactionImpl.cpp @@ -26,7 +26,7 @@ bool TransactionImpl::commit() { return false; } - return producer->commit(message_, transaction_id_, endpoint_); + return producer->commit(*this); } bool TransactionImpl::rollback() { @@ -34,14 +34,14 @@ bool TransactionImpl::rollback() { if (!producer) { return false; } - return producer->rollback(message_, transaction_id_, endpoint_); + return producer->rollback(*this); } -std::string TransactionImpl::messageId() const { - return message_.getMsgId(); +const std::string& TransactionImpl::messageId() const { + return message_id_; } -std::string TransactionImpl::transactionId() const { +const std::string& TransactionImpl::transactionId() const { return transaction_id_; } diff --git a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h index 77df4d576..b3fe2b193 100644 --- a/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h +++ b/src/main/cpp/rocketmq/include/AsyncReceiveMessageCallback.h @@ -19,19 +19,17 @@ #include #include -#include "ProcessQueue.h" -#include "ReceiveMessageCallback.h" +#include "ReceiveMessageResult.h" ROCKETMQ_NAMESPACE_BEGIN -class AsyncReceiveMessageCallback : public ReceiveMessageCallback, - public std::enable_shared_from_this { -public: - explicit AsyncReceiveMessageCallback(ProcessQueueWeakPtr process_queue); +class ProcessQueue; - ~AsyncReceiveMessageCallback() override = default; +class AsyncReceiveMessageCallback : public std::enable_shared_from_this { +public: + explicit AsyncReceiveMessageCallback(std::weak_ptr process_queue); - void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result) override; + void onCompletion(const std::error_code& ec, const ReceiveMessageResult& result); void receiveMessageLater(); @@ -42,7 +40,7 @@ class AsyncReceiveMessageCallback : public ReceiveMessageCallback, * Hold a weak_ptr to ProcessQueue. Once ProcessQueue was released, stop the * pop-cycle immediately. */ - ProcessQueueWeakPtr process_queue_; + std::weak_ptr process_queue_; std::function receive_message_later_; diff --git a/src/main/cpp/rocketmq/include/AwaitPullCallback.h b/src/main/cpp/rocketmq/include/AwaitPullCallback.h deleted file mode 100644 index c3fd373ee..000000000 --- a/src/main/cpp/rocketmq/include/AwaitPullCallback.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "absl/synchronization/mutex.h" - -#include "rocketmq/AsyncCallback.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class AwaitPullCallback : public PullCallback { -public: - explicit AwaitPullCallback(PullResult& pull_result) : pull_result_(pull_result) { - } - - void onSuccess(const PullResult& pull_result) noexcept override; - - void onFailure(const std::error_code& ec) noexcept override; - - bool await(); - - bool hasFailure() const { - return ec_.operator bool(); - } - - bool isCompleted() const { - return completed_; - } - - const std::error_code& errorCode() const noexcept { - return ec_; - } - -private: - PullResult& pull_result_; - absl::Mutex mtx_; - absl::CondVar cv_; - bool completed_{false}; - std::error_code ec_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ClientImpl.h b/src/main/cpp/rocketmq/include/ClientImpl.h index 036179bfc..ff61af376 100644 --- a/src/main/cpp/rocketmq/include/ClientImpl.h +++ b/src/main/cpp/rocketmq/include/ClientImpl.h @@ -20,31 +20,29 @@ #include #include #include +#include #include -#include "RpcClient.h" -#include "absl/strings/string_view.h" -#include "apache/rocketmq/v1/definition.pb.h" - #include "Client.h" -#include "ClientConfigImpl.h" +#include "ClientConfig.h" #include "ClientManager.h" #include "ClientResourceBundle.h" #include "InvocationContext.h" +#include "MessageExt.h" #include "NameServerResolver.h" -#include "OtlpExporter.h" -#include "rocketmq/MQMessageExt.h" +#include "RpcClient.h" +#include "Session.h" +#include "TelemetryBidiReactor.h" +#include "absl/strings/string_view.h" #include "rocketmq/MessageListener.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN -class ClientImpl : public ClientConfigImpl, virtual public Client { +class ClientImpl : virtual public Client { public: explicit ClientImpl(absl::string_view group_name); - ~ClientImpl() override = default; - virtual void start(); virtual void shutdown(); @@ -69,15 +67,23 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void onRemoteEndpointRemoval(const std::vector& hosts) override LOCKS_EXCLUDED(isolated_endpoints_mtx_); - void healthCheck() override LOCKS_EXCLUDED(isolated_endpoints_mtx_); - void schedule(const std::string& task_name, const std::function& task, std::chrono::milliseconds delay) override; + void createSession(const std::string& target, bool verify) override; + void withNameServerResolver(std::shared_ptr name_server_resolver) { name_server_resolver_ = std::move(name_server_resolver); } + void withCredentialsProvider(std::shared_ptr credentials_provider) override { + client_config_.credentials_provider = std::move(credentials_provider); + } + + void withRequestTimeout(std::chrono::milliseconds request_timeout) { + client_config_.request_timeout = absl::FromChrono(request_timeout); + } + /** * Expose for test purpose only. */ @@ -85,9 +91,30 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { state_.store(state, std::memory_order_relaxed); } + void verify(MessageConstSharedPtr message, std::function cb) override; + + void recoverOrphanedTransaction(MessageConstSharedPtr message) override; + + virtual void doRecoverOrphanedTransaction(MessageConstSharedPtr message); + + ClientConfig& config() override { + return client_config_; + } + + std::shared_ptr manager() const override { + return client_manager_; + } + + rmq::Settings clientSettings() override; + + virtual void buildClientSettings(rmq::Settings& settings) { + } + protected: + ClientConfig client_config_; + ClientManagerPtr client_manager_; - std::shared_ptr exporter_; + std::atomic state_; absl::flat_hash_map topic_route_table_ GUARDED_BY(topic_route_table_mtx_); @@ -108,6 +135,12 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { absl::flat_hash_set isolated_endpoints_ GUARDED_BY(isolated_endpoints_mtx_); absl::Mutex isolated_endpoints_mtx_; + absl::flat_hash_map> session_map_ GUARDED_BY(session_map_mtx_); + absl::Mutex session_map_mtx_; + + virtual void topicsOfInterest(std::vector& topics) { + } + void updateRouteInfo() LOCKS_EXCLUDED(topic_route_table_mtx_); /** @@ -117,47 +150,32 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { virtual void prepareHeartbeatData(HeartbeatRequest& request) = 0; - virtual void verifyMessageConsumption(std::string remote_address, std::string command_id, MQMessageExt message); - /** * @brief Execute transaction-state-checker to commit or roll-back the orphan transactional message. * * It is no-op by default and Producer-subclass is supposed to override it. * - * @param transaction_id * @param message */ - virtual void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) { + virtual void onOrphanedTransactionalMessage(MessageConstSharedPtr message) { } - /** - * Concrete publisher/subscriber client is expected to fill other - * type-specific resources. - */ - virtual ClientResourceBundle resourceBundle() { - ClientResourceBundle resource_bundle; - resource_bundle.client_id = clientId(); - resource_bundle.resource_namespace = resource_namespace_; - return resource_bundle; - } - - void setAccessPoint(rmq::Endpoints* endpoints); + virtual void onVerifyMessage(MessageConstSharedPtr message, std::function cb); void notifyClientTermination() override; void notifyClientTermination(const NotifyClientTerminationRequest& request); - /** - * @brief Return application developer provided message listener if this client is of PushConsumer type. - * - * By default, it returns nullptr such that error messages are generated and directed to server immediately. - * - * @return nullptr by default. - */ - virtual MessageListener* messageListener() { - return nullptr; + const std::string& resourceNamespace() const { + return client_config_.resource_namespace; } + absl::Duration requestTimeout() const { + return client_config_.request_timeout; + } + + rmq::Endpoints accessPoint(); + private: /** * This is a low-level API that fetches route data from name server through @@ -177,11 +195,6 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void onTopicRouteReady(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(inflight_route_requests_mtx_); - /** - * Update Trace candidate hosts. - */ - void updateTraceHosts() LOCKS_EXCLUDED(topic_route_table_mtx_); - /** * Update local cache for the topic. Note, route differences are logged in * INFO level since route bears fundamental importance. @@ -192,14 +205,9 @@ class ClientImpl : public ClientConfigImpl, virtual public Client { void updateRouteCache(const std::string& topic, const std::error_code& ec, const TopicRouteDataPtr& route) LOCKS_EXCLUDED(topic_route_table_mtx_); - void pollCommand(const std::string& target); - - void onPollCommandResponse(const InvocationContext* ctx); - - void onHealthCheckResponse(const std::error_code& endpoint, const InvocationContext* ctx) - LOCKS_EXCLUDED(isolated_endpoints_mtx_); + void doVerify(std::string target, std::string command_id, MessageConstPtr message); - void doVerify(std::string target, std::string command_id, MQMessageExt message); + static std::string clientId(); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h index ea85888cf..5f95d9676 100644 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h @@ -21,7 +21,8 @@ ROCKETMQ_NAMESPACE_BEGIN class ConsumeFifoMessageService : public ConsumeMessageServiceBase, public std::enable_shared_from_this { public: - ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, + MessageListener message_listener); void start() override; void shutdown() override; @@ -31,25 +32,24 @@ class ConsumeFifoMessageService : public ConsumeMessageServiceBase, * * @param process_queue */ - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - - MessageListenerType messageListenerType() override; + void submitConsumeTask(const std::weak_ptr& process_queue) override; private: - void consumeTask(const ProcessQueueWeakPtr& process_queue, MQMessageExt& message); + void consumeTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - void submitConsumeTask0(const std::shared_ptr& consumer, const ProcessQueueWeakPtr& process_queue, - const MQMessageExt& message); + void submitConsumeTask0(const std::shared_ptr& consumer, + const std::weak_ptr& process_queue, + MessageConstSharedPtr message); - void scheduleAckTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + void scheduleAckTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - void onAck(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, const std::error_code& ec); + void onAck(const std::weak_ptr& process_queue, MessageConstSharedPtr message, const std::error_code& ec); - void scheduleConsumeTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + void scheduleConsumeTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - void onForwardToDeadLetterQueue(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message, bool ok); + void onForwardToDeadLetterQueue(const std::weak_ptr& process_queue, MessageConstSharedPtr message, bool ok); - void scheduleForwardDeadLetterQueueTask(const ProcessQueueWeakPtr& process_queue, const MQMessageExt& message); + void scheduleForwardDeadLetterQueueTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index e6980e4f1..0c541eac9 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -38,9 +38,7 @@ class ConsumeMessageService { */ virtual void shutdown() = 0; - virtual void submitConsumeTask(const ProcessQueueWeakPtr& process_queue_ptr) = 0; - - virtual MessageListenerType messageListenerType() = 0; + virtual void submitConsumeTask(const std::weak_ptr& process_queue) = 0; virtual void signalDispatcher() = 0; diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h index 50173a578..9e9ff43ef 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h @@ -30,11 +30,12 @@ ROCKETMQ_NAMESPACE_BEGIN -class PushConsumer; +class PushConsumerImpl; class ConsumeMessageServiceBase : public ConsumeMessageService { public: - ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, MessageListener* message_listener); + ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, + MessageListener message_listener); ~ConsumeMessageServiceBase() override = default; @@ -83,13 +84,13 @@ class ConsumeMessageServiceBase : public ConsumeMessageService { int thread_count_; std::unique_ptr pool_; - std::weak_ptr consumer_; + std::weak_ptr consumer_; absl::Mutex dispatch_mtx_; std::thread dispatch_thread_; absl::CondVar dispatch_cv_; - MessageListener* message_listener_; + MessageListener message_listener_; /** * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h index 52bad1406..e98f257ec 100644 --- a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h @@ -17,13 +17,15 @@ #pragma once #include "ConsumeMessageServiceBase.h" +#include "ProcessQueue.h" ROCKETMQ_NAMESPACE_BEGIN -class ConsumeStandardMessageService : public ConsumeMessageServiceBase { +class ConsumeStandardMessageService : public ConsumeMessageServiceBase, + public std::enable_shared_from_this { public: - ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, - MessageListener* message_listener_ptr); + ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, + MessageListener message_listener); ~ConsumeStandardMessageService() override = default; @@ -31,12 +33,14 @@ class ConsumeStandardMessageService : public ConsumeMessageServiceBase { void shutdown() override; - void submitConsumeTask(const ProcessQueueWeakPtr& process_queue) override; - - MessageListenerType messageListenerType() override; + void submitConsumeTask(const std::weak_ptr& process_queue) override; private: - void consumeTask(const ProcessQueueWeakPtr& process_queue, const std::vector& msgs); + static void consumeTask(std::weak_ptr service, + const std::weak_ptr& process_queue, + const std::vector& msgs); + + void consume(const std::shared_ptr& process_queue, const std::vector& msgs); }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/Consumer.h b/src/main/cpp/rocketmq/include/Consumer.h index ad9cdad52..4d63fa225 100644 --- a/src/main/cpp/rocketmq/include/Consumer.h +++ b/src/main/cpp/rocketmq/include/Consumer.h @@ -38,6 +38,8 @@ class Consumer : virtual public Client { virtual int32_t receiveBatchSize() const = 0; virtual std::shared_ptr getConsumeMessageService() = 0; + + virtual bool fifo() const = 0; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h index 270c494a8..dad3029be 100644 --- a/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h +++ b/src/main/cpp/rocketmq/include/MessageGroupQueueSelector.h @@ -16,15 +16,18 @@ */ #pragma once -#include "rocketmq/MQSelector.h" +#include +#include + +#include "Protocol.h" ROCKETMQ_NAMESPACE_BEGIN -class MessageGroupQueueSelector : public MessageQueueSelector { +class MessageGroupQueueSelector { public: explicit MessageGroupQueueSelector(std::string message_group); - MQMessageQueue select(const std::vector& mqs, const MQMessage& msg, void* arg) override; + rmq::MessageQueue select(const std::vector& mqs); private: std::string message_group_; diff --git a/src/main/cpp/rocketmq/include/NamingScheme.h b/src/main/cpp/rocketmq/include/NamingScheme.h index d9b4f62e7..433aef454 100644 --- a/src/main/cpp/rocketmq/include/NamingScheme.h +++ b/src/main/cpp/rocketmq/include/NamingScheme.h @@ -19,8 +19,8 @@ #include #include +#include "absl/strings/string_view.h" #include "re2/re2.h" - #include "rocketmq/RocketMQ.h" ROCKETMQ_NAMESPACE_BEGIN @@ -39,6 +39,10 @@ class NamingScheme { static const char* IPv4Regex; static const char* IPv6Regex; + bool isIPv4(const std::string& host); + + bool isIPv6(const std::string& host); + re2::RE2 ipv4_pattern_; re2::RE2 ipv6_pattern_; }; diff --git a/src/main/cpp/rocketmq/include/ProcessQueue.h b/src/main/cpp/rocketmq/include/ProcessQueue.h index e53edb76c..ac3576d96 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueue.h +++ b/src/main/cpp/rocketmq/include/ProcessQueue.h @@ -18,14 +18,14 @@ #include +#include "AsyncReceiveMessageCallback.h" #include "ConsumeMessageType.h" -#include "FilterExpression.h" -#include "ReceiveMessageCallback.h" -#include "rocketmq/MQMessageExt.h" +#include "MessageExt.h" +#include "rocketmq/FilterExpression.h" ROCKETMQ_NAMESPACE_BEGIN -class PushConsumer; +class PushConsumerImpl; class ClientManager; @@ -35,29 +35,23 @@ class ProcessQueue { virtual bool expired() const = 0; - virtual void callback(std::shared_ptr callback) = 0; + virtual void callback(std::shared_ptr callback) = 0; virtual void receiveMessage() = 0; - virtual void nextOffset(int64_t next_offset) = 0; - virtual bool hasPendingMessages() const = 0; virtual std::string topic() const = 0; - virtual bool take(uint32_t batch_size, std::vector& messages) = 0; + virtual bool take(uint32_t batch_size, std::vector& messages) = 0; - virtual std::weak_ptr getConsumer() = 0; + virtual std::weak_ptr getConsumer() = 0; virtual const std::string& simpleName() const = 0; - virtual MQMessageQueue getMQMessageQueue() = 0; - - virtual bool committedOffset(int64_t& offset) = 0; - - virtual void release(uint64_t body_size, int64_t offset) = 0; + virtual void release(uint64_t body_size) = 0; - virtual void cacheMessages(const std::vector& messages) = 0; + virtual void cacheMessages(const std::vector& messages) = 0; virtual bool shouldThrottle() const = 0; @@ -70,9 +64,8 @@ class ProcessQueue { virtual bool bindFifoConsumeTask() = 0; virtual bool unbindFifoConsumeTask() = 0; -}; -using ProcessQueueSharedPtr = std::shared_ptr; -using ProcessQueueWeakPtr = std::weak_ptr; + virtual const rmq::MessageQueue& messageQueue() const = 0; +}; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h index 68947720b..64797180c 100644 --- a/src/main/cpp/rocketmq/include/ProcessQueueImpl.h +++ b/src/main/cpp/rocketmq/include/ProcessQueueImpl.h @@ -16,55 +16,26 @@ */ #pragma once -#include #include #include #include #include #include -#include "Assignment.h" #include "ClientManager.h" -#include "FilterExpression.h" +#include "MessageExt.h" #include "MixAll.h" #include "ProcessQueue.h" #include "ReceiveMessageCallback.h" #include "TopicAssignmentInfo.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -#include "apache/rocketmq/v1/service.pb.h" -#include "rocketmq/ConsumeType.h" -#include "rocketmq/MQMessageExt.h" -#include "rocketmq/MQMessageQueue.h" #include "gtest/gtest_prod.h" +#include "rocketmq/FilterExpression.h" ROCKETMQ_NAMESPACE_BEGIN -struct OffsetRecord { - explicit OffsetRecord(int64_t offset) : offset_(offset), released_(false) { - } - OffsetRecord(int64_t offset, bool released) : offset_(offset), released_(released) { - } - int64_t offset_; - bool released_; -}; - -ROCKETMQ_NAMESPACE_END - -namespace std { - -template <> -struct less { - bool operator()(const ROCKETMQ_NAMESPACE::OffsetRecord& lhs, const ROCKETMQ_NAMESPACE::OffsetRecord& rhs) const { - return lhs.offset_ < rhs.offset_; - } -}; - -} // namespace std - -ROCKETMQ_NAMESPACE_BEGIN - -class PushConsumer; +class PushConsumerImpl; /** * @brief Once messages are fetched(either pulled or popped) from remote server, they are firstly put into cache. @@ -74,14 +45,14 @@ class PushConsumer; */ class ProcessQueueImpl : virtual public ProcessQueue { public: - ProcessQueueImpl(MQMessageQueue message_queue, FilterExpression filter_expression, - std::weak_ptr consumer, std::shared_ptr client_instance); + ProcessQueueImpl(rmq::MessageQueue message_queue, + FilterExpression filter_expression, + std::weak_ptr consumer, + std::shared_ptr client_instance); ~ProcessQueueImpl() override; - void callback(std::shared_ptr callback) override; - - MQMessageQueue getMQMessageQueue() override; + void callback(std::shared_ptr callback) override; bool expired() const override; @@ -89,7 +60,7 @@ class ProcessQueueImpl : virtual public ProcessQueue { const FilterExpression& getFilterExpression() const override; - std::weak_ptr getConsumer() override; + std::weak_ptr getConsumer() override; std::shared_ptr getClientManager() override; @@ -100,7 +71,7 @@ class ProcessQueueImpl : virtual public ProcessQueue { } std::string topic() const override { - return message_queue_.getTopic(); + return message_queue_.topic().name(); } bool hasPendingMessages() const override LOCKS_EXCLUDED(messages_mtx_); @@ -110,7 +81,7 @@ class ProcessQueueImpl : virtual public ProcessQueue { * * @param messages */ - void cacheMessages(const std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + void cacheMessages(const std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); /** * @return Number of messages that is not yet dispatched to thread pool, likely, due to topic-rate-limiting. @@ -126,24 +97,13 @@ class ProcessQueueImpl : virtual public ProcessQueue { * @param messages * @return true if there are more messages to consume in cache */ - bool take(uint32_t batch_size, std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); + bool take(uint32_t batch_size, std::vector& messages) override LOCKS_EXCLUDED(messages_mtx_); void syncIdleState() override { idle_since_ = std::chrono::steady_clock::now(); } - void nextOffset(int64_t next_offset) override { - assert(next_offset >= 0); - next_offset_ = next_offset; - } - - int64_t nextOffset() const { - return next_offset_; - } - - bool committedOffset(int64_t& offset) override LOCKS_EXCLUDED(offsets_mtx_); - - void release(uint64_t body_size, int64_t offset) override LOCKS_EXCLUDED(messages_mtx_, offsets_mtx_); + void release(uint64_t body_size) override LOCKS_EXCLUDED(messages_mtx_); bool unbindFifoConsumeTask() override { bool expected = true; @@ -155,8 +115,12 @@ class ProcessQueueImpl : virtual public ProcessQueue { return has_fifo_task_bound_.compare_exchange_strong(expected, true, std::memory_order_relaxed); } + const rmq::MessageQueue& messageQueue() const override { + return message_queue_; + } + private: - MQMessageQueue message_queue_; + rmq::MessageQueue message_queue_; /** * Expression used to filter message in the server side. @@ -171,15 +135,15 @@ class ProcessQueueImpl : virtual public ProcessQueue { std::string simple_name_; - std::weak_ptr consumer_; + std::weak_ptr consumer_; std::shared_ptr client_manager_; - std::shared_ptr receive_callback_; + std::shared_ptr receive_callback_; /** * Messages that are pending to be submitted to thread pool. */ - mutable std::vector cached_messages_ GUARDED_BY(messages_mtx_); + mutable std::vector cached_messages_ GUARDED_BY(messages_mtx_); mutable absl::Mutex messages_mtx_; @@ -195,23 +159,15 @@ class ProcessQueueImpl : virtual public ProcessQueue { */ std::atomic cached_message_memory_; - int64_t next_offset_{0}; - /** * If this process queue is used in FIFO scenario, this field marks if there is an task in thread pool. */ std::atomic_bool has_fifo_task_bound_{false}; - std::set offsets_ GUARDED_BY(offsets_mtx_); - absl::Mutex offsets_mtx_; - void popMessage(); void wrapPopMessageRequest(absl::flat_hash_map& metadata, rmq::ReceiveMessageRequest& request); - void pullMessage(); - void wrapPullMessageRequest(rmq::PullMessageRequest& request); - void wrapFilterExpression(rmq::FilterExpression* filter_expression); FRIEND_TEST(ProcessQueueTest, testExpired); diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index a2172cef6..b765fa616 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -22,27 +22,27 @@ #include #include -#include "absl/strings/string_view.h" - #include "ClientImpl.h" #include "ClientManagerImpl.h" #include "MixAll.h" -#include "SendCallbacks.h" +#include "PublishInfoCallback.h" +#include "SendContext.h" #include "TopicPublishInfo.h" #include "TransactionImpl.h" -#include "rocketmq/AsyncCallback.h" -#include "rocketmq/LocalTransactionStateChecker.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" -#include "rocketmq/MQSelector.h" -#include "rocketmq/SendResult.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "rocketmq/Message.h" +#include "rocketmq/SendCallback.h" +#include "rocketmq/SendReceipt.h" #include "rocketmq/State.h" +#include "rocketmq/TransactionChecker.h" ROCKETMQ_NAMESPACE_BEGIN class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_this { public: - explicit ProducerImpl(absl::string_view group_name); + explicit ProducerImpl(); ~ProducerImpl() override; @@ -52,19 +52,17 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void shutdown() override; - SendResult send(const MQMessage& message, std::error_code& ec) noexcept; - - void send(const MQMessage& message, SendCallback* callback); + SendReceipt send(MessageConstPtr message, std::error_code& ec) noexcept; - void sendOneway(const MQMessage& message, std::error_code& ec); + void send(MessageConstPtr message, SendCallback callback); - void setLocalTransactionStateChecker(LocalTransactionStateCheckerPtr checker); + void setTransactionChecker(TransactionChecker checker); - std::unique_ptr prepare(MQMessage& message, std::error_code& ec); + std::unique_ptr prepare(MessageConstPtr message, std::error_code& ec); - bool commit(const MQMessage& message, const std::string& transaction_id, const std::string& target); + bool commit(const Transaction& transaction); - bool rollback(const MQMessage& message, const std::string& transaction_id, const std::string& target); + bool rollback(const Transaction& transaction); /** * Check if the RPC client for the target host is isolated or not @@ -79,11 +77,11 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t */ void isolateEndpoint(const std::string& target) LOCKS_EXCLUDED(isolated_endpoints_mtx_); - int maxAttemptTimes() const { + std::size_t maxAttemptTimes() const { return max_attempt_times_; } - void maxAttemptTimes(int times) { + void maxAttemptTimes(std::size_t times) { max_attempt_times_ = times; } @@ -95,8 +93,6 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t failed_times_ = times; } - std::vector listMessageQueue(const std::string& topic, std::error_code& ec); - uint32_t compressBodyThreshold() const { return compress_body_threshold_; } @@ -105,61 +101,60 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t compress_body_threshold_ = threshold; } - /** - * @brief Send message with tracing. - * - * @param message - * @param callback - * @param message_queue - * @param attempt_time current attempt times, which starts from 0. - */ - void sendImpl(RetrySendCallback* callback); + void sendImpl(std::shared_ptr callback); + + void buildClientSettings(rmq::Settings& settings) override; + + void topicsOfInterest(std::vector topics) LOCKS_EXCLUDED(topics_mtx_); protected: std::shared_ptr self() override { return shared_from_this(); } - void resolveOrphanedTransactionalMessage(const std::string& transaction_id, const MQMessageExt& message) override; + void onOrphanedTransactionalMessage(MessageConstSharedPtr message) override; void notifyClientTermination() override; private: absl::flat_hash_map topic_publish_info_table_ GUARDED_BY(topic_publish_info_mtx_); - absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ - - int32_t max_attempt_times_{MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_}; - int32_t failed_times_{0}; // only for test + absl::Mutex topic_publish_info_mtx_; // protects topic_publish_info_ + std::size_t max_attempt_times_{MixAll::MAX_SEND_MESSAGE_ATTEMPT_TIMES_}; + int32_t failed_times_{0}; // only for test uint32_t compress_body_threshold_; + TransactionChecker transaction_checker_; + std::vector topics_ GUARDED_BY(topics_mtx_); + absl::Mutex topics_mtx_; - LocalTransactionStateCheckerPtr transaction_state_checker_; - - void asyncPublishInfo(const std::string& topic, - const std::function& cb) + /** + * @brief Acquire PublishInfo for the given topic. + * Generally speaking, it first checks presence of the desired info in local cache, aka, topic_publish_table_; + * If not found, query name servers. + */ + void getPublishInfoAsync(const std::string& topic, const PublishInfoCallback& cb) LOCKS_EXCLUDED(topic_publish_info_mtx_); - TopicPublishInfoPtr getPublishInfo(const std::string& topic); + void cachePublishInfo(const std::string&, TopicPublishInfoPtr info) LOCKS_EXCLUDED(topic_publish_info_mtx_); - void takeMessageQueuesRoundRobin(const TopicPublishInfoPtr& publish_info, std::vector& message_queues, - int number); + TopicPublishInfoPtr getPublishInfo(const std::string& topic); - void wrapSendMessageRequest(const MQMessage& message, SendMessageRequest& request, - const MQMessageQueue& message_queue); + void wrapSendMessageRequest(const Message& message, + SendMessageRequest& request, + const rmq::MessageQueue& message_queue); bool isRunning() const; void ensureRunning(std::error_code& ec) const noexcept; - bool validate(const MQMessage& message); + bool validate(const Message& message); - void send0(const MQMessage& message, SendCallback* callback, std::vector list, int max_attempt_times); + void send0(MessageConstPtr message, SendCallback callback, std::vector list); - bool endTransaction0(const std::string& target, const MQMessage& message, const std::string& transaction_id, - TransactionState resolution); + bool endTransaction0(const Transaction& transaction, TransactionState resolution); void isolatedEndpoints(absl::flat_hash_set& endpoints) LOCKS_EXCLUDED(isolated_endpoints_mtx_); - MQMessageQueue withServiceAddress(const MQMessageQueue& message_queue, std::error_code& ec); + friend class ProducerBuilder; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PublishInfoCallback.h b/src/main/cpp/rocketmq/include/PublishInfoCallback.h new file mode 100644 index 000000000..a4d7a6e25 --- /dev/null +++ b/src/main/cpp/rocketmq/include/PublishInfoCallback.h @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "TopicPublishInfo.h" + +ROCKETMQ_NAMESPACE_BEGIN + +using PublishInfoCallback = std::function; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PullConsumerImpl.h b/src/main/cpp/rocketmq/include/PullConsumerImpl.h deleted file mode 100644 index 30c7cc019..000000000 --- a/src/main/cpp/rocketmq/include/PullConsumerImpl.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "absl/strings/string_view.h" - -#include "ClientConfig.h" -#include "ClientImpl.h" -#include "ClientManagerImpl.h" -#include "rocketmq/ConsumeType.h" -#include "rocketmq/MQMessageQueue.h" -#include "rocketmq/MessageModel.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PullConsumerImpl : public ClientImpl, public std::enable_shared_from_this { -public: - explicit PullConsumerImpl(absl::string_view group_name) : ClientImpl(group_name) { - } - - void start() override; - - void shutdown() override; - - std::future> queuesFor(const std::string& topic); - - std::future queryOffset(const OffsetQuery& query); - - void pull(const PullMessageQuery& query, PullCallback* callback); - - void prepareHeartbeatData(HeartbeatRequest& request) override; - -protected: - std::shared_ptr self() override { - return shared_from_this(); - } - - void notifyClientTermination() override; - - MessageModel message_model_{MessageModel::CLUSTERING}; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumer.h b/src/main/cpp/rocketmq/include/PushConsumer.h deleted file mode 100644 index cf2da65b7..000000000 --- a/src/main/cpp/rocketmq/include/PushConsumer.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include - -#include "Consumer.h" -#include "ProcessQueue.h" -#include "rocketmq/Executor.h" -#include "rocketmq/MessageListener.h" -#include "rocketmq/MessageModel.h" -#include "rocketmq/OffsetStore.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PushConsumer : virtual public Consumer { -public: - ~PushConsumer() override = default; - - virtual void iterateProcessQueue(const std::function& cb) = 0; - - virtual MessageModel messageModel() const = 0; - - virtual void ack(const MQMessageExt& msg, const std::function& callback) = 0; - - virtual void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) = 0; - - virtual const Executor& customExecutor() const = 0; - - virtual uint32_t consumeBatchSize() const = 0; - - virtual int32_t maxDeliveryAttempts() const = 0; - - virtual void updateOffset(const MQMessageQueue& message_queue, int64_t offset) = 0; - - virtual void nack(const MQMessageExt& message, const std::function& callback) = 0; - - virtual bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) = 0; - - virtual MessageListener* messageListener() = 0; - - virtual void setOffsetStore(std::unique_ptr offset_store) = 0; -}; - -using PushConsumerSharedPtr = std::shared_ptr; -using PushConsumerWeakPtr = std::weak_ptr; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 399b87ed6..58ba4e530 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -21,22 +21,18 @@ #include #include -#include "absl/strings/string_view.h" - -#include "ClientConfigImpl.h" +#include "ClientConfig.h" #include "ClientImpl.h" #include "ClientManagerImpl.h" #include "ConsumeMessageService.h" -#include "FilterExpression.h" #include "ProcessQueue.h" -#include "PushConsumer.h" #include "Scheduler.h" #include "TopicAssignmentInfo.h" #include "TopicPublishInfo.h" #include "UtilAll.h" -#include "apache/rocketmq/v1/service.pb.h" -#include "rocketmq/DefaultMQPushConsumer.h" -#include "rocketmq/OffsetStore.h" +#include "absl/strings/string_view.h" +#include "rocketmq/Executor.h" +#include "rocketmq/FilterExpression.h" #include "rocketmq/State.h" ROCKETMQ_NAMESPACE_BEGIN @@ -45,39 +41,41 @@ class ConsumeMessageService; class ConsumeFifoMessageService; class ConsumeStandardMessageService; -class PushConsumerImpl : virtual public ClientImpl, - virtual public PushConsumer, - public std::enable_shared_from_this { +class PushConsumerImpl : virtual public ClientImpl, public std::enable_shared_from_this { public: explicit PushConsumerImpl(absl::string_view group_name); ~PushConsumerImpl() override; - void prepareHeartbeatData(HeartbeatRequest& request) override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + void buildClientSettings(rmq::Settings& settings) override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + + void prepareHeartbeatData(HeartbeatRequest& request) override; + + void topicsOfInterest(std::vector& topics) override LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); void start() override; void shutdown() override; - void subscribe(const std::string& topic, const std::string& expression, + void subscribe(const std::string& topic, + const std::string& expression, ExpressionType expression_type = ExpressionType::TAG) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); - absl::optional getFilterExpression(const std::string& topic) const override + absl::optional getFilterExpression(const std::string& topic) const LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); - void setConsumeFromWhere(ConsumeFromWhere consume_from_where); - - void registerMessageListener(MessageListener* message_listener); + void registerMessageListener(MessageListener message_listener); void scanAssignments() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); static bool selectBroker(const TopicRouteDataPtr& route, std::string& broker_host); - void wrapQueryAssignmentRequest(const std::string& topic, const std::string& consumer_group, - const std::string& client_id, const std::string& strategy_name, + void wrapQueryAssignmentRequest(const std::string& topic, + const std::string& consumer_group, + const std::string& strategy_name, QueryAssignmentRequest& request); /** @@ -92,50 +90,38 @@ class PushConsumerImpl : virtual public ClientImpl, void queryAssignment(const std::string& topic, const std::function& cb); - void syncProcessQueue(const std::string& topic, const TopicAssignmentPtr& topic_assignment, + void syncProcessQueue(const std::string& topic, + const TopicAssignmentPtr& topic_assignment, const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); - ProcessQueueSharedPtr getOrCreateProcessQueue(const MQMessageQueue& message_queue, - const FilterExpression& filter_expression) + std::shared_ptr getOrCreateProcessQueue(const rmq::MessageQueue& message_queue, + const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); - bool receiveMessage(const MQMessageQueue& message_queue, const FilterExpression& filter_expression) override + bool receiveMessage(const rmq::MessageQueue& message_queue, const FilterExpression& filter_expression) LOCKS_EXCLUDED(process_queue_table_mtx_); uint32_t consumeThreadPoolSize() const; void consumeThreadPoolSize(int thread_pool_size); - int32_t maxDeliveryAttempts() const override { + int32_t maxDeliveryAttempts() const { return max_delivery_attempts_; } - uint32_t consumeBatchSize() const override; - - void consumeBatchSize(uint32_t consume_batch_size); - - int32_t receiveBatchSize() const override { + int32_t receiveBatchSize() const { return receive_batch_size_; } - std::shared_ptr getConsumeMessageService() override; + std::shared_ptr getConsumeMessageService(); - void ack(const MQMessageExt& msg, const std::function& callback) override; + void ack(const Message& msg, const std::function& callback); - /** - * Negative acknowledge the given message; Refer to - * https://en.wikipedia.org/wiki/Acknowledgement_(data_networks) for - * background info. - * - * Current implementation is to change invisible time of the given message. - * - * @param message Message to negate on the broker side. - */ - void nack(const MQMessageExt& message, const std::function& callback) override; + void nack(const Message& message, const std::function& callback); - void forwardToDeadLetterQueue(const MQMessageExt& message, const std::function& cb) override; + void forwardToDeadLetterQueue(const Message& message, const std::function& cb); - void wrapAckMessageRequest(const MQMessageExt& msg, AckMessageRequest& request); + void wrapAckMessageRequest(const Message& msg, AckMessageRequest& request); // only for test std::size_t getProcessQueueTableSize() LOCKS_EXCLUDED(process_queue_table_mtx_); @@ -144,36 +130,18 @@ class PushConsumerImpl : virtual public ClientImpl, custom_executor_ = executor; } - const Executor& customExecutor() const override { + const Executor& customExecutor() const { return custom_executor_; } void setThrottle(const std::string& topic, uint32_t threshold); - MessageModel messageModel() const override { - return message_model_; - } - - void setMessageModel(MessageModel message_model) { - message_model_ = message_model; - } - - void setOffsetStore(std::unique_ptr offset_store) override { - offset_store_ = std::move(offset_store); - } - - void updateOffset(const MQMessageQueue& message_queue, int64_t offset) override { - if (offset_store_) { - offset_store_->updateOffset(message_queue, offset); - } - } - /** * Max number of messages that may be cached per queue before applying * back-pressure. * @return */ - uint32_t maxCachedMessageQuantity() const override { + uint32_t maxCachedMessageQuantity() const { return MixAll::DEFAULT_CACHED_MESSAGE_COUNT; } @@ -182,28 +150,32 @@ class PushConsumerImpl : virtual public ClientImpl, * back-pressure. * @return */ - uint64_t maxCachedMessageMemory() const override { + uint64_t maxCachedMessageMemory() const { return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; } - void iterateProcessQueue(const std::function& callback) override; + void iterateProcessQueue(const std::function)>& callback); - MessageListener* messageListener() override { + MessageListener& messageListener() { return message_listener_; } + const std::string& groupName() const { + return client_config_.subscriber.group.name(); + } + protected: std::shared_ptr self() override { return shared_from_this(); } - ClientResourceBundle resourceBundle() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_) override; - void notifyClientTermination() override; + void onVerifyMessage(MessageConstSharedPtr, std::function) override; + private: - absl::flat_hash_map - topic_filter_expression_table_ GUARDED_BY(topic_filter_expression_table_mtx_); + absl::flat_hash_map topic_filter_expression_table_ + GUARDED_BY(topic_filter_expression_table_mtx_); mutable absl::Mutex topic_filter_expression_table_mtx_; /** @@ -211,7 +183,7 @@ class PushConsumerImpl : virtual public ClientImpl, */ uint32_t consume_thread_pool_size_{MixAll::DEFAULT_CONSUME_THREAD_POOL_SIZE}; - MessageListener* message_listener_{nullptr}; + MessageListener message_listener_; std::shared_ptr consume_message_service_; uint32_t consume_batch_size_{MixAll::DEFAULT_CONSUME_MESSAGE_BATCH_SIZE}; @@ -221,24 +193,25 @@ class PushConsumerImpl : virtual public ClientImpl, std::uintptr_t scan_assignment_handle_{0}; static const char* SCAN_ASSIGNMENT_TASK_NAME; - absl::flat_hash_map process_queue_table_ GUARDED_BY(process_queue_table_mtx_); + /** + * simple-queue-name ==> process-queue + */ + absl::flat_hash_map> process_queue_table_ + GUARDED_BY(process_queue_table_mtx_); absl::Mutex process_queue_table_mtx_; - ConsumeFromWhere consume_from_where_{ConsumeFromWhere::CONSUME_FROM_LAST_OFFSET}; Executor custom_executor_; - absl::flat_hash_map - throttle_table_ GUARDED_BY(throttle_table_mtx_); + absl::flat_hash_map throttle_table_ + GUARDED_BY(throttle_table_mtx_); absl::Mutex throttle_table_mtx_; int32_t max_delivery_attempts_{MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS}; - MessageModel message_model_{MessageModel::CLUSTERING}; - - mutable std::unique_ptr offset_store_; - void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); + std::chrono::milliseconds invisibleDuration(std::size_t attempt); + friend class ConsumeMessageService; friend class ConsumeFifoMessageService; friend class ConsumeStandardMessageService; diff --git a/src/main/cpp/rocketmq/include/SendCallbacks.h b/src/main/cpp/rocketmq/include/SendCallbacks.h deleted file mode 100644 index d7cc68608..000000000 --- a/src/main/cpp/rocketmq/include/SendCallbacks.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/synchronization/mutex.h" -#include "apache/rocketmq/v1/service.grpc.pb.h" -#include "opencensus/trace/span.h" - -#include "TransactionImpl.h" -#include "rocketmq/AsyncCallback.h" -#include "rocketmq/ErrorCode.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -using SendMessageRequest = apache::rocketmq::v1::SendMessageRequest; - -class OnewaySendCallback : public SendCallback { -public: - void onSuccess(SendResult& send_result) noexcept override; - - void onFailure(const std::error_code& ec) noexcept override; -}; - -OnewaySendCallback* onewaySendCallback(); - -class AwaitSendCallback : public SendCallback { -public: - void onSuccess(SendResult& send_result) noexcept override; - - void onFailure(const std::error_code& ec) noexcept override; - - void await(); - - explicit operator bool() const { - return !ec_.operator bool(); - } - - const SendResult& sendResult() const { - return send_result_; - } - - const std::error_code& errorCode() const { - return ec_; - } - -private: - absl::Mutex mtx_; - absl::CondVar cv_; - bool completed_{false}; - SendResult send_result_; - std::error_code ec_; -}; - -class ProducerImpl; - -class RetrySendCallback : public SendCallback { -public: - RetrySendCallback(std::weak_ptr producer, MQMessage message, int max_attempt_times, - SendCallback* callback, std::vector candidates) - : producer_(std::move(producer)), message_(std::move(message)), max_attempt_times_(max_attempt_times), - callback_(callback), candidates_(std::move(candidates)), span_(opencensus::trace::Span::BlankSpan()) { - } - - void onSuccess(SendResult& send_result) noexcept override; - - void onFailure(const std::error_code& ec) noexcept override; - - MQMessage& message() { - return message_; - } - - int attemptTime() const { - return attempt_times_; - } - - const MQMessageQueue& messageQueue() const { - int index = attempt_times_ % candidates_.size(); - return candidates_[index]; - } - - opencensus::trace::Span& span() { - return span_; - } - -private: - std::weak_ptr producer_; - MQMessage message_; - int attempt_times_{0}; - int max_attempt_times_; - SendCallback* callback_{nullptr}; - - /** - * @brief Once the first publish attempt failed, the following routable - * message queues are employed. - * - */ - std::vector candidates_; - - /** - * @brief The on-going span. Should be terminated in the callback functions. - */ - opencensus::trace::Span span_; -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SendContext.h b/src/main/cpp/rocketmq/include/SendContext.h new file mode 100644 index 000000000..bba83c8fe --- /dev/null +++ b/src/main/cpp/rocketmq/include/SendContext.h @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/synchronization/mutex.h" +#include "opencensus/trace/span.h" + +#include "Protocol.h" +#include "TransactionImpl.h" +#include "rocketmq/ErrorCode.h" +#include "rocketmq/Message.h" +#include "rocketmq/SendCallback.h" +#include "rocketmq/SendReceipt.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProducerImpl; + +class SendContext : public std::enable_shared_from_this { +public: + SendContext(std::weak_ptr producer, + MessageConstPtr message, + SendCallback callback, + std::vector candidates) + : producer_(std::move(producer)), + message_(std::move(message)), + callback_(std::move(callback)), + candidates_(std::move(candidates)), + span_(opencensus::trace::Span::BlankSpan()) { + } + + void onSuccess(const SendReceipt& send_receipt) noexcept; + + void onFailure(const std::error_code& ec) noexcept; + + const rmq::MessageQueue& messageQueue() const { + int index = attempt_times_ % candidates_.size(); + return candidates_[index]; + } + + std::weak_ptr producer_; + MessageConstPtr message_; + std::size_t attempt_times_{0}; + SendCallback callback_; + + /** + * @brief Once the first publish attempt failed, the following routable + * message queues are employed. + * + */ + std::vector candidates_; + + /** + * @brief The on-going span. Should be terminated in the callback functions. + */ + opencensus::trace::Span span_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/SimpleConsumerImpl.h b/src/main/cpp/rocketmq/include/SimpleConsumerImpl.h new file mode 100644 index 000000000..5a5a1b9a6 --- /dev/null +++ b/src/main/cpp/rocketmq/include/SimpleConsumerImpl.h @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "ClientImpl.h" +#include "rocketmq/FilterExpression.h" +#include "rocketmq/SimpleConsumer.h" + +using namespace std::chrono; +ROCKETMQ_NAMESPACE_BEGIN + +class SimpleConsumerImpl : public ClientImpl, public std::enable_shared_from_this { +public: + SimpleConsumerImpl(std::string group); + + ~SimpleConsumerImpl() override; + + void prepareHeartbeatData(rmq::HeartbeatRequest& request) override; + + void buildClientSettings(rmq::Settings& settings) override; + + std::shared_ptr self() override { + return shared_from_this(); + } + + void start() override; + + void shutdown() override; + + void subscribe(std::string topic, FilterExpression expression) LOCKS_EXCLUDED(subscriptions_mtx_); + + void unsubscribe(const std::string& topic) LOCKS_EXCLUDED(subscriptions_mtx_); + + void receive(std::size_t limit, std::chrono::milliseconds invisible_duration, ReceiveCallback callback); + + void ack(const Message& message, std::error_code& ec); + + void ackAsync(const Message& message, AckCallback callback); + + void changeInvisibleDuration(const Message& message, + std::chrono::milliseconds duration, + ChangeInvisibleDurationCallback callback); + +protected: + void topicsOfInterest(std::vector& topics) override; + +private: + absl::flat_hash_map subscriptions_ GUARDED_BY(subscriptions_mtx_); + absl::Mutex subscriptions_mtx_; + + absl::flat_hash_map> topic_assignments_ GUARDED_BY(topic_assignments_mtx_); + absl::Mutex topic_assignments_mtx_; + + std::vector assignments_; + absl::Mutex assignments_mtx_ ACQUIRED_AFTER(topic_assignments_mtx_); + + std::size_t refresh_assignment_task_{0}; + + static thread_local std::size_t assignment_index_; + + void refreshAssignments0() LOCKS_EXCLUDED(topic_assignments_mtx_, subscriptions_mtx_); + + void refreshAssignments() LOCKS_EXCLUDED(subscriptions_mtx_); + + void refreshAssignment(const std::string& topic, std::function cb); + + void updateAssignments(const std::string& topic, const std::vector& assignments) + LOCKS_EXCLUDED(assignments_mtx_, topic_assignments_mtx_); + + void wrapAckRequest(const Message& message, AckMessageRequest& request); + + void removeAssignmentsByTopic(const std::string& topic) LOCKS_EXCLUDED(topic_assignments_mtx_, assignments_mtx_); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/client/include/TopicPublishInfo.h b/src/main/cpp/rocketmq/include/TopicPublishInfo.h similarity index 56% rename from src/main/cpp/client/include/TopicPublishInfo.h rename to src/main/cpp/rocketmq/include/TopicPublishInfo.h index b1d22ddca..b4c37b501 100644 --- a/src/main/cpp/client/include/TopicPublishInfo.h +++ b/src/main/cpp/rocketmq/include/TopicPublishInfo.h @@ -16,49 +16,42 @@ */ #pragma once +#include #include +#include "TopicRouteData.h" #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" - -#include "TopicRouteData.h" -#include "rocketmq/MQMessageQueue.h" +#include "absl/types/optional.h" ROCKETMQ_NAMESPACE_BEGIN +class ProducerImpl; + class TopicPublishInfo { public: - TopicPublishInfo(absl::string_view topic, TopicRouteDataPtr topic_route_data); + TopicPublishInfo(std::weak_ptr producer, absl::string_view topic, TopicRouteDataPtr topic_route_data); - /** - * @param message_queue Reference to target message queue. - * @return true if manage to select one; false otherwise. - */ - bool selectOneMessageQueue(MQMessageQueue& message_queue) LOCKS_EXCLUDED(partition_list_mtx_); - - bool selectOneActiveMessageQueue(absl::flat_hash_set& isolated, MQMessageQueue& message_queue) - LOCKS_EXCLUDED(partition_list_mtx_); - - bool takeMessageQueues(absl::flat_hash_set& isolated, std::vector& candidates, - uint32_t count) LOCKS_EXCLUDED(partition_list_mtx_); + bool selectMessageQueues(absl::optional message_group, std::vector& result) + LOCKS_EXCLUDED(queue_list_mtx_); void topicRouteData(TopicRouteDataPtr topic_route_data); /** - * Expose partition list in perspective of message queue list. + * Expose queue list in perspective of message queue list. * * @return */ - std::vector getMessageQueueList() LOCKS_EXCLUDED(partition_list_mtx_); + std::vector getMessageQueueList() LOCKS_EXCLUDED(queue_list_mtx_); private: - std::vector partition_list_ GUARDED_BY(partition_list_mtx_); - absl::Mutex partition_list_mtx_; // protects message_queue_list_ - - std::string topic_; - TopicRouteDataPtr topic_route_data_; + std::vector queue_list_ GUARDED_BY(queue_list_mtx_); + absl::Mutex queue_list_mtx_; // protects message_queue_list_ + std::weak_ptr producer_; + std::string topic_; + TopicRouteDataPtr topic_route_data_; void updatePublishInfo(); diff --git a/src/main/cpp/rocketmq/include/TransactionImpl.h b/src/main/cpp/rocketmq/include/TransactionImpl.h index 844c82796..ce633ed9e 100644 --- a/src/main/cpp/rocketmq/include/TransactionImpl.h +++ b/src/main/cpp/rocketmq/include/TransactionImpl.h @@ -19,7 +19,7 @@ #include #include -#include "rocketmq/MQMessage.h" +#include "rocketmq/Message.h" #include "rocketmq/Transaction.h" ROCKETMQ_NAMESPACE_BEGIN @@ -28,10 +28,8 @@ class ProducerImpl; class TransactionImpl : public Transaction { public: - TransactionImpl(MQMessage message, std::string transaction_id, std::string endpoint, std::string trace_context, - const std::shared_ptr& producer) - : message_(std::move(message)), transaction_id_(std::move(transaction_id)), endpoint_(std::move(endpoint)), - trace_context_(std::move(trace_context)), producer_(producer) { + TransactionImpl(std::string topic, std::string message_id, std::string trace_context, const std::weak_ptr& producer) + : topic_(std::move(topic)), message_id_(std::move(message_id)), trace_context_(std::move(trace_context)), producer_(producer) { } ~TransactionImpl() override = default; @@ -40,12 +38,35 @@ class TransactionImpl : public Transaction { bool rollback() override; - std::string messageId() const override; + const std::string& messageId() const override; - std::string transactionId() const override; + const std::string& transactionId() const override; + + void transactionId(std::string transaction_id) { + transaction_id_ = std::move(transaction_id); + } + + const std::string& traceContext() const override { + return trace_context_; + } + + void traceContext(std::string trace_context) { + trace_context_ = std::move(trace_context); + } + + const std::string& endpoint() const override { + return endpoint_; + } + + void endpoint(std::string endpoint) { endpoint_ = std::move(endpoint); } + + const std::string& topic() const override { + return topic_; + } private: - MQMessage message_; + std::string topic_; + std::string message_id_; std::string transaction_id_; std::string endpoint_; std::string trace_context_; diff --git a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h index ee9251937..9811a8ce1 100644 --- a/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h +++ b/src/main/cpp/rocketmq/mocks/include/ConsumeMessageServiceMock.h @@ -30,9 +30,7 @@ class ConsumeMessageServiceMock : public ConsumeMessageService { MOCK_METHOD(void, shutdown, (), (override)); - MOCK_METHOD(void, submitConsumeTask, (const ProcessQueueWeakPtr&), (override)); - - MOCK_METHOD(MessageListenerType, messageListenerType, (), (override)); + MOCK_METHOD(void, submitConsumeTask, (const std::weak_ptr&), (override)); MOCK_METHOD(void, signalDispatcher, (), (override)); diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index e4508baa6..0a9a3611e 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -25,7 +25,7 @@ ROCKETMQ_NAMESPACE_BEGIN class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMock { public: - MOCK_METHOD(void, iterateProcessQueue, (const std::function&), (override)); + MOCK_METHOD(void, iterateProcessQueue, (const std::function)>&), (override)); MOCK_METHOD(MessageModel, messageModel, (), (const override)); @@ -41,8 +41,6 @@ class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMoc MOCK_METHOD(void, updateOffset, (const MQMessageQueue&, int64_t), (override)); - MOCK_METHOD(void, nack, (const MQMessageExt&, const std::function&), (override)); - MOCK_METHOD(bool, receiveMessage, (const MQMessageQueue&, const FilterExpression&), (override)); MOCK_METHOD(MessageListener*, messageListener, (), (override)); diff --git a/src/test/cpp/ut/logger/BUILD.bazel b/src/main/cpp/rocketmq/tests/BUILD.bazel similarity index 74% rename from src/test/cpp/ut/logger/BUILD.bazel rename to src/main/cpp/rocketmq/tests/BUILD.bazel index 7d51c3c12..83f627163 100644 --- a/src/test/cpp/ut/logger/BUILD.bazel +++ b/src/main/cpp/rocketmq/tests/BUILD.bazel @@ -16,14 +16,23 @@ # load("@rules_cc//cc:defs.bzl", "cc_test") +base_deps = [ + "//src/main/cpp/rocketmq:rocketmq_library", + "@com_google_googletest//:gtest_main", +] + cc_test( - name = "logger_test", + name = "time_test", srcs = [ - "LoggerTest.cpp", + "TimeTest.cpp", ], - deps = [ - "//api:rocketmq_interface", - "//src/main/cpp/log:log_library", - "@com_google_googletest//:gtest_main", + deps = base_deps, +) + +cc_test( + name = "send_context_test", + srcs = [ + "SendContextTest.cpp", ], -) \ No newline at end of file + deps = base_deps, +) diff --git a/src/main/cpp/rocketmq/tests/SendContextTest.cpp b/src/main/cpp/rocketmq/tests/SendContextTest.cpp new file mode 100644 index 000000000..3c29c2cd0 --- /dev/null +++ b/src/main/cpp/rocketmq/tests/SendContextTest.cpp @@ -0,0 +1,17 @@ +#include + +#include "SendContext.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(SendContextTest, testBasics) { + auto message = Message::newBuilder().withBody("body").withTopic("topic").withGroup("group0").withTag("TagA").build(); + auto callback = [](const std::error_code&, const SendReceipt&) {}; + std::weak_ptr producer; + std::vector message_queues; + auto send_context = std::make_shared(producer, std::move(message), callback, message_queues); + send_context.reset(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/tests/TimeTest.cpp b/src/main/cpp/rocketmq/tests/TimeTest.cpp new file mode 100644 index 000000000..898aef02c --- /dev/null +++ b/src/main/cpp/rocketmq/tests/TimeTest.cpp @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "absl/time/time.h" +#include "gtest/gtest.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +TEST(TimeTest, testTimeHelpers) { + auto duration = absl::Seconds(1) + absl::Nanoseconds(100); + auto seconds = absl::ToInt64Seconds(duration); + ASSERT_EQ(1, seconds); + ASSERT_EQ(100, absl::ToInt64Nanoseconds(duration - absl::Seconds(seconds))); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/BUILD.bazel b/src/main/cpp/stats/BUILD.bazel similarity index 71% rename from src/test/cpp/ut/scheduler/BUILD.bazel rename to src/main/cpp/stats/BUILD.bazel index 23615c556..f31a08135 100644 --- a/src/test/cpp/ut/scheduler/BUILD.bazel +++ b/src/main/cpp/stats/BUILD.bazel @@ -14,17 +14,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "scheduler_test", - srcs = [ - "SchedulerTest.cpp", - ], +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "stats", + hdrs = glob(["include/*.h"]), + srcs = glob(["*.cpp"]), + strip_include_prefix = "//src/main/cpp/stats/include", deps = [ "//api:rocketmq_interface", - "//src/main/cpp/base:base_library", - "//src/main/cpp/concurrent:countdown_latch_library", - "//src/main/cpp/scheduler:scheduler_library", - "@com_google_googletest//:gtest_main", + "@io_opencensus_cpp//opencensus/stats", ], + visibility = ["//visibility:public"], ) \ No newline at end of file diff --git a/src/main/cpp/stats/PublishStats.cpp b/src/main/cpp/stats/PublishStats.cpp new file mode 100644 index 000000000..9356f980d --- /dev/null +++ b/src/main/cpp/stats/PublishStats.cpp @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "PublishStats.h" + +ROCKETMQ_NAMESPACE_BEGIN + +PublishStats::PublishStats() + : success_(opencensus::stats::MeasureInt64::Register("publish_success", "Number of message published", "1")), + failure_(opencensus::stats::MeasureInt64::Register("pubish_failure", "Number of publish failures", "1")), + latency_(opencensus::stats::MeasureInt64::Register("publish_latency", "Publish latency in milliseconds", "ms")) { + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_send_success_total") + .set_description("Number of messages published") + .set_measure("publish_success") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(topicTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_send_failure_total") + .set_description("Number of publish failures") + .set_measure("pubish_failure") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(topicTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_send_cost_time") + .set_description("Publish latency") + .set_measure("publish_latency") + .set_aggregation(opencensus::stats::Aggregation::Distribution( + opencensus::stats::BucketBoundaries::Explicit({1, 10, 100, 1000}))) + .add_column(topicTag()) + .RegisterForExport(); +} + +opencensus::tags::TagKey& PublishStats::topicTag() { + static opencensus::tags::TagKey topic_tag = opencensus::tags::TagKey::Register("topic"); + return topic_tag; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientTest.cpp b/src/main/cpp/stats/include/PublishStats.h similarity index 62% rename from src/test/cpp/ut/client/ClientTest.cpp rename to src/main/cpp/stats/include/PublishStats.h index 3d8148f61..46305d0bf 100644 --- a/src/test/cpp/ut/client/ClientTest.cpp +++ b/src/main/cpp/stats/include/PublishStats.h @@ -14,33 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "ClientMock.h" +#pragma once + +#include + +#include "opencensus/stats/stats.h" #include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include ROCKETMQ_NAMESPACE_BEGIN -class ClientTest : public testing::Test { +class PublishStats { public: - void SetUp() override { - client_ = std::make_shared>(); - ON_CALL(*client_, active).WillByDefault(testing::Invoke([]() { - std::cout << "active() is invoked" << std::endl; - return true; - })); + PublishStats(); + + opencensus::stats::MeasureInt64& success() { + return success_; } - void TearDown() override { + opencensus::stats::MeasureInt64& failure() { + return failure_; } -protected: - std::shared_ptr> client_; -}; + opencensus::stats::MeasureInt64& latency() { + return latency_; + } + + static opencensus::tags::TagKey& topicTag(); -TEST_F(ClientTest, testActive) { - EXPECT_TRUE(client_->active()); -} +private: + opencensus::stats::MeasureInt64 success_; + opencensus::stats::MeasureInt64 failure_; + opencensus::stats::MeasureInt64 latency_; +}; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/api/BUILD.bazel b/src/main/cpp/stats/tests/BUILD.bazel similarity index 90% rename from src/test/cpp/ut/api/BUILD.bazel rename to src/main/cpp/stats/tests/BUILD.bazel index 78f575ff8..d1a67285c 100644 --- a/src/test/cpp/ut/api/BUILD.bazel +++ b/src/main/cpp/stats/tests/BUILD.bazel @@ -17,12 +17,12 @@ load("@rules_cc//cc:defs.bzl", "cc_test") cc_test( - name = "error_code_test", + name = "publish_stats_test", srcs = [ - "ErrorCodeTest.cpp", + "PublishStatsTest.cpp", ], deps = [ - "//src/main/cpp/base:base_library", + "//src/main/cpp/stats", "@com_google_googletest//:gtest_main", ], ) \ No newline at end of file diff --git a/src/main/cpp/stats/tests/PublishStatsTest.cpp b/src/main/cpp/stats/tests/PublishStatsTest.cpp new file mode 100644 index 000000000..609e63424 --- /dev/null +++ b/src/main/cpp/stats/tests/PublishStatsTest.cpp @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "PublishStats.h" +#include "gtest/gtest.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Handler : public opencensus::stats::StatsExporter::Handler { +public: + void ExportViewData( + const std::vector>& data) override { + for (const auto& datum : data) { + const auto& view_data = datum.second; + switch (view_data.type()) { + case opencensus::stats::ViewData::Type::kInt64: { + exportDatum(datum.first, view_data.start_time(), view_data.end_time(), view_data.int_data()); + break; + } + case opencensus::stats::ViewData::Type::kDouble: { + exportDatum(datum.first, view_data.start_time(), view_data.end_time(), view_data.double_data()); + break; + } + case opencensus::stats::ViewData::Type::kDistribution: { + exportDatum(datum.first, view_data.start_time(), view_data.end_time(), view_data.distribution_data()); + break; + } + } + } + } + + template + void exportDatum(const opencensus::stats::ViewDescriptor& descriptor, + absl::Time start_time, + absl::Time end_time, + const opencensus::stats::ViewData::DataMap& data) { + if (data.empty()) { + std::cout << "No data for " << descriptor.name() << std::endl; + return; + } + + for (const auto& row : data) { + for (std::size_t column = 0; column < descriptor.columns().size(); column++) { + std::cout << descriptor.name() << "[" << descriptor.columns()[column].name() << "=" << row.first[column] << "]" + << DataToString(row.second) << std::endl; + } + } + } + + // Functions to format data for different aggregation types. + std::string DataToString(double data) { + return absl::StrCat(": ", data, "\n"); + } + std::string DataToString(int64_t data) { + return absl::StrCat(": ", data, "\n"); + } + std::string DataToString(const opencensus::stats::Distribution& data) { + std::string output = "\n"; + std::vector lines = absl::StrSplit(data.DebugString(), '\n'); + // Add indent. + for (const auto& line : lines) { + absl::StrAppend(&output, " ", line, "\n"); + } + return output; + } +}; + +TEST(StatsTest, testBasics) { + std::string t1("T1"); + std::string t2("T2"); + PublishStats metrics; + opencensus::stats::StatsExporter::RegisterPushHandler(absl::make_unique()); + opencensus::stats::Record({{metrics.success(), 1}}, {{PublishStats::topicTag(), t1}}); + opencensus::stats::Record({{metrics.success(), 100}}, {{PublishStats::topicTag(), t2}}); + opencensus::stats::Record({{metrics.latency(), 100}}, {{PublishStats::topicTag(), t1}}); + + std::this_thread::sleep_for(std::chrono::seconds(10)); +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/tracing/BUILD.bazel b/src/main/cpp/trace/BUILD.bazel similarity index 92% rename from src/main/cpp/tracing/BUILD.bazel rename to src/main/cpp/trace/BUILD.bazel index 0fdb6b095..b44fe4c0e 100644 --- a/src/main/cpp/tracing/BUILD.bazel +++ b/src/main/cpp/trace/BUILD.bazel @@ -16,10 +16,10 @@ # load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( - name = "tracing_utility", + name = "trace", hdrs = glob(["include/*.h"]), srcs = glob(["*.cpp"]), - strip_include_prefix = "//src/main/cpp/tracing/include", + strip_include_prefix = "//src/main/cpp/trace/include", deps = [ "//api:rocketmq_interface", "//src/main/cpp/client:client_library", diff --git a/src/main/cpp/tracing/TracingUtility.cpp b/src/main/cpp/trace/TracingUtility.cpp similarity index 71% rename from src/main/cpp/tracing/TracingUtility.cpp rename to src/main/cpp/trace/TracingUtility.cpp index d5b7be0d2..4cd7a8dd8 100644 --- a/src/main/cpp/tracing/TracingUtility.cpp +++ b/src/main/cpp/trace/TracingUtility.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ #include "TracingUtility.h" +#include "MessageExt.h" #include "MixAll.h" #include "absl/strings/str_join.h" #include "rocketmq/CredentialsProvider.h" @@ -22,27 +23,30 @@ ROCKETMQ_NAMESPACE_BEGIN -void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, ClientConfig& client_config, +void TracingUtility::addUniversalSpanAttributes(const Message& message, const ClientConfig& client_config, opencensus::trace::Span& span) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.getMsgId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES, message.getBody().length()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.getTags()); - const std::vector& keys = message.getKeys(); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_ID, message.id()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PAYLOAD_SIZE_BYTES, message.body().length()); + if (message.tag().has_value()) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_TAG, message.tag().value()); + } + + const std::vector& keys = message.keys(); if (!keys.empty()) { span.AddAnnotation(MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, {{MixAll::SPAN_ANNOTATION_MESSAGE_KEYS, absl::StrJoin(keys.begin(), keys.end(), MixAll::MESSAGE_KEY_SEPARATOR)}}); } - switch (message.messageType()) { - case MessageType::FIFO: + switch (typeOf(message)) { + case rmq::MessageType::FIFO: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_FIFO_MESSAGE); break; - case MessageType::DELAY: + case rmq::MessageType::DELAY: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_DELAY_MESSAGE); break; - case MessageType::TRANSACTION: + case rmq::MessageType::TRANSACTION: span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_MESSAGE_TYPE, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_TRANSACTION_MESSAGE); break; @@ -51,28 +55,33 @@ void TracingUtility::addUniversalSpanAttributes(const MQMessage& message, Client MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_NORMAL_MESSAGE); break; } - std::chrono::system_clock::time_point timestamp = message.deliveryTimestamp(); - auto duration = absl::FromChrono(timestamp.time_since_epoch()); - int64_t timestamp_millis = absl::ToInt64Milliseconds(duration); - if (timestamp_millis > 0) { - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, timestamp_millis); + + if (message.deliveryTimestamp().has_value()) { + std::chrono::system_clock::time_point timestamp = message.deliveryTimestamp().value(); + auto duration = absl::FromChrono(timestamp.time_since_epoch()); + int64_t timestamp_millis = absl::ToInt64Milliseconds(duration); + if (timestamp_millis > 0) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, timestamp_millis); + } } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_SYSTEM, MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_MESSAGING_SYSTEM); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.getTopic()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION, message.topic()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_DESTINATION_KIND, MixAll::SPAN_ATTRIBUTE_VALUE_DESTINATION_KIND); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_PROTOCOL_VERSION, MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROTOCOL_VERSION); // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_URL, "abc") - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID, client_config.clientId()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, client_config.getGroupName()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resource_namespace); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_ID, client_config.client_id); + + // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_CLIENT_GROUP, client_config.getGroupName()); span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ACCESS_KEY, - client_config.credentialsProvider()->getCredentials().accessKey()); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resourceNamespace()); + client_config.credentials_provider->getCredentials().accessKey()); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_NAMESPACE, client_config.resource_namespace); } ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/include/TracingUtility.h b/src/main/cpp/trace/include/TracingUtility.h similarity index 82% rename from src/main/cpp/tracing/include/TracingUtility.h rename to src/main/cpp/trace/include/TracingUtility.h index f97478068..8dbe48830 100644 --- a/src/main/cpp/tracing/include/TracingUtility.h +++ b/src/main/cpp/trace/include/TracingUtility.h @@ -16,16 +16,17 @@ */ #pragma once +#include "opencensus/trace/propagation/trace_context.h" #include "opencensus/trace/span.h" #include "ClientConfig.h" -#include "rocketmq/MQMessage.h" +#include "rocketmq/Message.h" ROCKETMQ_NAMESPACE_BEGIN class TracingUtility { public: - static void addUniversalSpanAttributes(const MQMessage& message, ClientConfig&, opencensus::trace::Span& span); + static void addUniversalSpanAttributes(const Message& message, const ClientConfig&, opencensus::trace::Span& span); }; ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/tracing/exporters/BUILD.bazel b/src/main/cpp/tracing/exporters/BUILD.bazel deleted file mode 100644 index 0fee74515..000000000 --- a/src/main/cpp/tracing/exporters/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_library") -cc_library( - name = "otlp_exporter", - hdrs = glob(["include/*.h"]), - srcs = glob(["*.cpp"]), - strip_include_prefix = "//src/main/cpp/tracing/exporters/include", - deps = [ - "//api:rocketmq_interface", - "//src/main/cpp/client:client_library", - "@com_google_absl//absl/base", - "@com_google_absl//absl/memory", - "@io_opencensus_cpp//opencensus/trace", - "@com_github_opentelemetry_proto//:trace_service_grpc_cc", - "@com_github_grpc_grpc//:grpc++", - "@com_github_fmtlib_fmt//:fmtlib" - ], - visibility = ["//visibility:public"], -) \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/OtlpExporter.cpp b/src/main/cpp/tracing/exporters/OtlpExporter.cpp deleted file mode 100644 index 1d24de4a1..000000000 --- a/src/main/cpp/tracing/exporters/OtlpExporter.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "OtlpExporter.h" -#include "ClientConfigImpl.h" -#include "InvocationContext.h" -#include "MixAll.h" -#include "Signature.h" -#include "UtilAll.h" -#include "absl/strings/str_split.h" -#include "fmt/format.h" -#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" -#include "opentelemetry/proto/common/v1/common.pb.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -namespace trace = opentelemetry::proto::trace::v1; -namespace common = opentelemetry::proto::common::v1; - -opencensus::trace::Sampler& Samplers::always() { - static opencensus::trace::AlwaysSampler sampler; - return sampler; -} - -void OtlpExporter::start() { - std::shared_ptr self = shared_from_this(); - auto handler = absl::make_unique(self); - handler->start(); - opencensus::trace::exporter::SpanExporter::RegisterHandler(std::move(handler)); -} - -void ExportClient::asyncExport(const collector_trace::ExportTraceServiceRequest& request, - InvocationContext* invocation_context) { - auto completion_queue = completion_queue_.lock(); - if (!completion_queue) { - // Server should have shutdown - return; - } - - invocation_context->response_reader = - stub_->PrepareAsyncExport(&invocation_context->context, request, completion_queue.get()); - invocation_context->response_reader->StartCall(); - invocation_context->response_reader->Finish(&invocation_context->response, &invocation_context->status, - invocation_context); -} - -const int OtlpExporterHandler::SPAN_ID_SIZE = 8; -const int OtlpExporterHandler::TRACE_ID_SIZE = 16; - -OtlpExporterHandler::OtlpExporterHandler(std::weak_ptr exporter) - : exporter_(std::move(exporter)), completion_queue_(std::make_shared()) { - auto exp = exporter_.lock(); -} - -void OtlpExporterHandler::start() { - poll_thread_ = std::thread(std::bind(&OtlpExporterHandler::poll, this)); - { - absl::MutexLock lk(&start_mtx_); - start_cv_.Wait(&start_mtx_); - } -} - -void OtlpExporterHandler::shutdown() { - stopped_.store(true, std::memory_order_relaxed); - if (poll_thread_.joinable()) { - poll_thread_.join(); - } -} - -void OtlpExporterHandler::syncExportClients() { - auto exp = exporter_.lock(); - if (!exp) { - return; - } - - std::vector&& hosts = exp->hosts(); - { - absl::MutexLock lk(&clients_map_mtx_); - for (auto i = clients_map_.begin(); i != clients_map_.end();) { - if (std::none_of(hosts.cbegin(), hosts.cend(), [&](const std::string& host) { return i->first == host; })) { - clients_map_.erase(i++); - } else { - i++; - } - } - - auto client_manager = exp->clientManager().lock(); - for (const auto& host : hosts) { - if (!clients_map_.contains(host)) { - if (client_manager) { - auto channel = client_manager->createChannel(host); - auto export_client = absl::make_unique(completion_queue_, channel); - clients_map_.emplace(host, std::move(export_client)); - } - } - } - } -} - -void OtlpExporterHandler::poll() { - { - // Notify main thread that the poller thread has started - absl::MutexLock lk(&start_mtx_); - start_cv_.SignalAll(); - } - - while (!stopped_.load(std::memory_order_relaxed)) { - bool ok = false; - void* opaque_invocation_context; - while (completion_queue_->Next(&opaque_invocation_context, &ok)) { - auto invocation_context = static_cast(opaque_invocation_context); - if (!ok) { - // the call is dead - SPDLOG_WARN("CompletionQueue#Next assigned ok false, indicating the call is dead"); - } - invocation_context->onCompletion(ok); - } - SPDLOG_INFO("CompletionQueue is fully drained and shut down"); - } - SPDLOG_INFO("poll completed and quit"); -} - -void OtlpExporterHandler::Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) { - auto exp = exporter_.lock(); - if (!exp) { - return; - } - - switch (exp->traceMode()) { - case TraceMode::Off: - return; - case TraceMode::Develop: { - { - for (const auto& span : spans) { - SPDLOG_INFO("trace span {} --> {}: {}", absl::FormatTime(span.start_time()), - absl::FormatTime(span.end_time()), span.name().data()); - for (const auto& event : span.annotations().events()) { - for (const auto& attr : event.event().attributes()) { - switch (attr.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.int_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - SPDLOG_INFO("Annotation {} attribute: {} --> {}", event.event().description().data(), attr.first, - attr.second.bool_value()); - break; - } - } - } - SPDLOG_INFO("Attributes size={}", span.attributes().size()); - for (const auto& attribute : span.attributes()) { - switch (attribute.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.string_value()); - break; - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.bool_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - SPDLOG_INFO("Span attribute: {} --> {}", attribute.first, attribute.second.int_value()); - break; - } - } - } - } - return; - } - case TraceMode::Grpc: - break; - } - - syncExportClients(); - absl::MutexLock lk(&clients_map_mtx_); - if (clients_map_.empty()) { - SPDLOG_WARN("No exporter client is available"); - return; - } - - uint32_t client_index = round_robin_++ % clients_map_.size(); - - auto iterator = clients_map_.begin(); - for (uint32_t i = 0; i < client_index; i++) { - iterator++; - } - - auto& exporter_client = iterator->second; - - collector_trace::ExportTraceServiceRequest request; - - auto resource = new trace::ResourceSpans(); - auto instrument_library_span = new trace::InstrumentationLibrarySpans(); - - uint8_t span_id_buf[SPAN_ID_SIZE]; - uint8_t trace_id_buf[TRACE_ID_SIZE]; - for (const auto& span : spans) { - auto item = new trace::Span(); - - span.context().trace_id().CopyTo(trace_id_buf); - item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); - - span.context().span_id().CopyTo(span_id_buf); - item->set_span_id(&span_id_buf, SPAN_ID_SIZE); - - span.parent_span_id().CopyTo(span_id_buf); - item->set_parent_span_id(&span_id_buf, SPAN_ID_SIZE); - - item->set_name(span.name().data()); - - item->set_start_time_unix_nano(absl::ToUnixNanos(span.start_time())); - item->set_end_time_unix_nano(absl::ToUnixNanos(span.end_time())); - - item->set_kind(trace::Span_SpanKind::Span_SpanKind_SPAN_KIND_CLIENT); - - // OpenCensus has annotations or message_events, which maps to events in OpenTelemetry. - if (!span.message_events().events().empty()) { - for (const auto& event : span.message_events().events()) { - auto ev = new trace::Span::Event(); - ev->set_time_unix_nano(absl::ToUnixNanos(event.timestamp())); - } - item->set_dropped_events_count(span.message_events().dropped_events_count()); - } - - if (!span.annotations().events().empty()) { - for (const auto& annotation : span.annotations().events()) { - // Specialized annotation to adjust span start-time. - // OpenCensus does not expose function to modify span start time. As as result, we need to apply this system - // annotation duration transforming opencensus span to OpenTelemetry span. - if (annotation.event().description() == MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION) { - for (const auto& attr : annotation.event().attributes()) { - if (attr.first == MixAll::SPAN_ANNOTATION_ATTR_START_TIME) { - assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kInt); - item->set_start_time_unix_nano(attr.second.int_value() * 1e6); - } - } - continue; - } - if (annotation.event().description() == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { - for (const auto& attr : annotation.event().attributes()) { - if (attr.first == MixAll::SPAN_ANNOTATION_MESSAGE_KEYS) { - assert(attr.second.type() == opencensus::trace::AttributeValueRef::Type::kString); - std::string message_keys = attr.second.string_value(); - std::vector key_list = absl::StrSplit(message_keys, MixAll::MESSAGE_KEY_SEPARATOR); - auto key_kv = new common::KeyValue(); - key_kv->set_key(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_KEYS); - auto key_value = new common::AnyValue(); - auto key_array_value = new common::ArrayValue(); - for (const auto& key : key_list) { - auto value = new common::AnyValue(); - value->set_string_value(key); - key_array_value->mutable_values()->AddAllocated(value); - } - key_value->set_allocated_array_value(key_array_value); - key_kv->set_allocated_value(key_value); - item->mutable_attributes()->AddAllocated(key_kv); - } - } - continue; - } - auto ev = new trace::Span::Event(); - ev->set_time_unix_nano(absl::ToUnixNanos(annotation.timestamp())); - auto attrs = annotation.event().attributes(); - for (const auto& attr : attrs) { - auto kv = new common::KeyValue(); - kv->set_key(attr.first); - auto value = new common::AnyValue(); - switch (attr.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attr.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attr.second.int_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attr.second.bool_value()); - break; - } - ev->mutable_attributes()->AddAllocated(kv); - } - item->mutable_events()->AddAllocated(ev); - } - item->set_dropped_events_count(span.annotations().dropped_events_count()); - } - - for (const auto& link : span.links()) { - auto span_link = new trace::Span::Link(); - - link.trace_id().CopyTo(trace_id_buf); - item->set_trace_id(&trace_id_buf, TRACE_ID_SIZE); - - link.span_id().CopyTo(span_id_buf); - item->set_trace_id(&span_id_buf, SPAN_ID_SIZE); - - for (const auto& attribute : link.attributes()) { - auto kv = new common::KeyValue(); - kv->set_key(attribute.first); - auto value = new common::AnyValue(); - switch (attribute.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attribute.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attribute.second.bool_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attribute.second.int_value()); - break; - } - kv->set_allocated_value(value); - span_link->mutable_attributes()->AddAllocated(kv); - } - - item->mutable_links()->AddAllocated(span_link); - } - item->set_dropped_links_count(span.num_attributes_dropped()); - - for (const auto& attribute : span.attributes()) { - auto kv = new common::KeyValue(); - kv->set_key(attribute.first); - auto value = new common::AnyValue(); - switch (attribute.second.type()) { - case opencensus::trace::AttributeValueRef::Type::kString: - value->set_string_value(attribute.second.string_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kBool: - value->set_bool_value(attribute.second.bool_value()); - break; - case opencensus::trace::AttributeValueRef::Type::kInt: - value->set_int_value(attribute.second.int_value()); - break; - } - kv->set_allocated_value(value); - item->mutable_attributes()->AddAllocated(kv); - } - - if (span.status().ok()) { - item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_OK); - } else { - item->mutable_status()->set_code(trace::Status_StatusCode::Status_StatusCode_STATUS_CODE_ERROR); - item->mutable_status()->set_message(span.status().error_message()); - } - - instrument_library_span->mutable_instrumentation_library()->mutable_name()->assign(MixAll::OTLP_NAME_VALUE); - instrument_library_span->mutable_instrumentation_library()->mutable_version()->assign( - ClientConfigImpl::CLIENT_VERSION); - instrument_library_span->mutable_spans()->AddAllocated(item); - } - resource->mutable_instrumentation_library_spans()->AddAllocated(instrument_library_span); - - auto telemetry_sdk_language_kv = new common::KeyValue(); - telemetry_sdk_language_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_TELEMETRY_SDK_LANGUAGE); - auto telemetry_sdk_language_value = new common::AnyValue(); - telemetry_sdk_language_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_TELEMETRY_SDK_LANGUAGE); - telemetry_sdk_language_kv->set_allocated_value(telemetry_sdk_language_value); - resource->mutable_resource()->mutable_attributes()->AddAllocated(telemetry_sdk_language_kv); - - auto host_name_kv = new common::KeyValue(); - host_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_HOST_NAME); - auto host_name_value = new common::AnyValue(); - host_name_value->set_string_value(UtilAll::hostname()); - host_name_kv->set_allocated_value(host_name_value); - resource->mutable_resource()->mutable_attributes()->AddAllocated(host_name_kv); - - auto service_name_kv = new common::KeyValue(); - service_name_kv->set_key(MixAll::TRACE_RESOURCE_ATTRIBUTE_KEY_SERVICE_NAME); - auto service_name_value = new common::AnyValue(); - service_name_value->set_string_value(MixAll::TRACE_RESOURCE_ATTRIBUTE_VALUE_SERVICE_NAME); - service_name_kv->set_allocated_value(service_name_value); - resource->mutable_resource()->mutable_attributes()->AddAllocated(service_name_kv); - - request.mutable_resource_spans()->AddAllocated(resource); - - auto invocation_context = new InvocationContext(); - invocation_context->remote_address = iterator->first; - auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3); - invocation_context->context.set_deadline(deadline); - - absl::flat_hash_map metadata; - Signature::sign(exp->clientConfig(), metadata); - for (const auto& entry : metadata) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - auto callback = [](const InvocationContext* invocation_context) { - if (invocation_context->status.ok()) { - SPDLOG_DEBUG("Export tracing spans OK, target={}", invocation_context->remote_address); - } else { - SPDLOG_WARN("Failed to export tracing spans to {}, gRPC code:{}, gRPC error message: {}", - invocation_context->remote_address, invocation_context->status.error_code(), - invocation_context->status.error_message()); - } - }; - invocation_context->callback = callback; - - exporter_client->asyncExport(request, invocation_context); -} - -thread_local std::uint32_t OtlpExporterHandler::round_robin_ = 0; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/tracing/exporters/include/OtlpExporter.h b/src/main/cpp/tracing/exporters/include/OtlpExporter.h deleted file mode 100644 index f947a3886..000000000 --- a/src/main/cpp/tracing/exporters/include/OtlpExporter.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "opencensus/trace/exporter/span_data.h" -#include "opencensus/trace/exporter/span_exporter.h" -#include "opencensus/trace/sampler.h" -#include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h" - -#include "ClientConfig.h" -#include "ClientManager.h" -#include "InvocationContext.h" -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -namespace collector = opentelemetry::proto::collector; -namespace collector_trace = collector::trace::v1; - -enum class TraceMode : std::uint8_t -{ - Off = 0, - Develop = 1, - Grpc = 2 -}; - -class Samplers { -public: - static opencensus::trace::Sampler& always(); -}; - -class OtlpExporter : public std::enable_shared_from_this { -public: - OtlpExporter(std::weak_ptr client_manager, ClientConfig* client_config) - : client_manager_(std::move(client_manager)), client_config_(client_config) { - if (client_config_->isTracingEnabled()) { - mode_ = TraceMode::Grpc; - } - } - - void updateHosts(std::vector hosts) LOCKS_EXCLUDED(hosts_mtx_) { - if (hosts.empty()) { - return; - } - absl::MutexLock lk(&hosts_mtx_); - hosts_ = std::move(hosts); - } - - void start(); - - void shutdown(); - - std::vector hosts() LOCKS_EXCLUDED(hosts_mtx_) { - absl::MutexLock lk(&hosts_mtx_); - return hosts_; - } - - std::weak_ptr& clientManager() { - return client_manager_; - } - - ClientConfig* clientConfig() { - return client_config_; - } - - void traceMode(TraceMode mode) { - mode_ = mode; - } - - TraceMode traceMode() const { - return mode_; - } - -private: - std::weak_ptr client_manager_; - ClientConfig* client_config_; - - std::vector hosts_; - absl::Mutex hosts_mtx_; - - TraceMode mode_{TraceMode::Off}; -}; - -class ExportClient { -public: - ExportClient(std::shared_ptr completion_queue, std::shared_ptr channel) - : completion_queue_(std::move(completion_queue)), stub_(collector_trace::TraceService::NewStub(channel)) { - } - - void asyncExport(const collector_trace::ExportTraceServiceRequest& request, - InvocationContext* invocation_context); - -private: - std::weak_ptr completion_queue_; - std::unique_ptr stub_; -}; - -class OtlpExporterHandler : public ::opencensus::trace::exporter::SpanExporter::Handler { -public: - static const int SPAN_ID_SIZE; - static const int TRACE_ID_SIZE; - - OtlpExporterHandler(std::weak_ptr exporter); - - void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) override; - - void start(); - - void shutdown(); - -private: - std::weak_ptr exporter_; - std::shared_ptr completion_queue_; - std::thread poll_thread_; - absl::Mutex start_mtx_; - absl::CondVar start_cv_; - - absl::flat_hash_map> clients_map_ GUARDED_BY(clients_map_mtx_); - absl::Mutex clients_map_mtx_; - - thread_local static std::uint32_t round_robin_; - - std::atomic_bool stopped_{false}; - - void poll(); - - void syncExportClients() LOCKS_EXCLUDED(clients_map_mtx_); -}; - -ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/benchmark/BUILD.bazel b/src/test/cpp/benchmark/BUILD.bazel deleted file mode 100644 index c0cabf8f3..000000000 --- a/src/test/cpp/benchmark/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") - -cc_test( - name = "benchmark_test", - srcs = [ - "BenchmarkTest.cpp", - ], - deps = [ - "//external:benchmark", - ], -) \ No newline at end of file diff --git a/src/test/cpp/benchmark/BenchmarkTest.cpp b/src/test/cpp/benchmark/BenchmarkTest.cpp deleted file mode 100644 index 421541d2b..000000000 --- a/src/test/cpp/benchmark/BenchmarkTest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "benchmark/benchmark.h" - -static void BM_StringCreation(benchmark::State& state) { - for (auto _ : state) - std::string empty_string; -} -// Register the function as a benchmark -BENCHMARK(BM_StringCreation); - -// Define another benchmark -static void BM_StringCopy(benchmark::State& state) { - std::string x = "hello"; - for (auto _ : state) - std::string copy(x); -} -BENCHMARK(BM_StringCopy); - -BENCHMARK_MAIN(); \ No newline at end of file diff --git a/src/test/cpp/it/BUILD.bazel b/src/test/cpp/it/BUILD.bazel deleted file mode 100644 index b4cabee09..000000000 --- a/src/test/cpp/it/BUILD.bazel +++ /dev/null @@ -1,51 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "it_top_addressing", - srcs = [ - "TopAddressingTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "it_rpc_client", - srcs = [ - "RpcClientTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "it_topic_publish_info", - srcs = [ - "TopicPublishInfoTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/it/README.md b/src/test/cpp/it/README.md deleted file mode 100644 index d9bac84d3..000000000 --- a/src/test/cpp/it/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## Introduction - -Execution of integration test relies on presence of daily environment, which provides dependent services. Dependent -services are discovered through configured DNS records in hosts file. \ No newline at end of file diff --git a/src/test/cpp/it/RpcClientTest.cpp b/src/test/cpp/it/RpcClientTest.cpp deleted file mode 100644 index fe3920420..000000000 --- a/src/test/cpp/it/RpcClientTest.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "absl/container/flat_hash_set.h" -#include "apache/rocketmq/v1/service.pb.h" -#include "google/rpc/code.pb.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" - -#include "ClientConfigImpl.h" -#include "InvocationContext.h" -#include "LogInterceptorFactory.h" -#include "MixAll.h" -#include "RpcClientImpl.h" -#include "Signature.h" -#include "TlsHelper.h" -#include "UniqueIdGenerator.h" -#include "UtilAll.h" -#include "rocketmq/CredentialsProvider.h" -#include "rocketmq/Logger.h" - -using namespace testing; - -ROCKETMQ_NAMESPACE_BEGIN - -class RpcClientTest : public ::testing::Test { -protected: - RpcClientTest() : completion_queue_(std::make_shared()) { - - server_authorization_check_config_ = std::make_shared( - std::make_shared()); - std::vector pem_list; - grpc::experimental::IdentityKeyCertPair pair{}; - pair.private_key = TlsHelper::client_private_key; - pair.certificate_chain = TlsHelper::client_certificate_chain; - pem_list.emplace_back(pair); - certificate_provider_ = - std::make_shared(TlsHelper::CA, pem_list); - tls_channel_credential_options_.set_certificate_provider(certificate_provider_); - tls_channel_credential_options_.set_server_verification_option(GRPC_TLS_SKIP_ALL_SERVER_VERIFICATION); - tls_channel_credential_options_.set_server_authorization_check_config(server_authorization_check_config_); - tls_channel_credential_options_.watch_root_certs(); - tls_channel_credential_options_.watch_identity_key_cert_pairs(); - channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_options_); - credentials_provider_ = std::make_shared(); - client_config_.tenantId(tenant_id_); - client_config_.setCredentialsProvider(credentials_provider_); - } - - void SetUp() override { - getLogger().setLevel(Level::Debug); - client_config_.setCredentialsProvider(std::make_shared()); - client_config_.resourceNamespace(resource_namespace_); - client_config_.region(region_id_); - client_config_.tenantId(tenant_id_); - - Signature::sign(&client_config_, metadata_); - - std::vector> interceptor_factories; - interceptor_factories.emplace_back(absl::make_unique()); - name_server_channel_ = grpc::experimental::CreateCustomChannelWithInterceptors( - name_server_target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); - } - - void TearDown() override { - } - - ~RpcClientTest() override { - completion_queue_->Shutdown(); - } - - bool brokerEndpoint(const std::string& topic, std::string& endpoint) { - QueryRouteRequest route_request; - route_request.mutable_topic()->set_name(topic.data()); - QueryRouteResponse route_response; - RpcClientImpl name_server_client(completion_queue_, name_server_channel_); - - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - - auto callback = [&](const InvocationContext* invocation_context) { - ASSERT_TRUE(invocation_context->status.ok()); - std::cout << "Route Response:" << route_response.DebugString() << std::endl; - absl::MutexLock lk(&mtx); - route_response = invocation_context->response; - completed = true; - cv.SignalAll(); - }; - - auto invocation_context = new InvocationContext(); - invocation_context->callback = callback; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); - - for (const auto& item : metadata_) { - invocation_context->context.AddMetadata(item.first, item.second); - } - - name_server_client.asyncQueryRoute(route_request, invocation_context); - - absl::flat_hash_set broker_addresses; - for (auto& partition : route_response.partitions()) { - auto& broker = partition.broker(); - if (MixAll::MASTER_BROKER_ID == broker.id()) { - for (auto& item : broker.endpoints().addresses()) { - std::string connect_string(fmt::format("{}:{}", item.host(), item.port())); - if (!broker_addresses.contains(connect_string)) { - broker_addresses.insert(connect_string); - } - } - } else { - for (const auto& item : broker.endpoints().addresses()) { - SPDLOG_WARN("Unexpected endpoint[{}:{}] with brokerId={}", item.host(), item.port(), broker.id()); - } - } - } - if (broker_addresses.empty()) { - return false; - } - - endpoint = *broker_addresses.begin(); - return true; - } - - void fillSendMessageRequest(SendMessageRequest& send_message_request) { - send_message_request.mutable_message()->mutable_topic()->set_resource_namespace(resource_namespace_); - send_message_request.mutable_message()->mutable_topic()->set_name(topic_); - std::unordered_map props; - props["key"] = "value"; - props["Jack"] = "Bauer"; - send_message_request.mutable_message()->mutable_user_attribute()->insert(props.begin(), props.end()); - auto system_attribute = send_message_request.mutable_message()->mutable_system_attribute(); - system_attribute->set_message_id(message_id_); - system_attribute->set_message_type(rmq::MessageType::NORMAL); - system_attribute->set_body_encoding(rmq::Encoding::IDENTITY); - system_attribute->set_born_host(UtilAll::hostname()); - system_attribute->set_tag("TagA"); - send_message_request.mutable_message()->set_body("Example data"); - } - - RpcClientSharedPtr brokerRpcClient() { - std::string broker_endpoint; - bool success = brokerEndpoint(topic_, broker_endpoint); - if (!success) { - return nullptr; - } - SPDLOG_INFO("Target broker address: {}", broker_endpoint); - - std::vector> interceptor_factories; - interceptor_factories.emplace_back(absl::make_unique()); - auto broker_channel = grpc::experimental::CreateCustomChannelWithInterceptors( - broker_endpoint, channel_credential_, channel_arguments_, std::move(interceptor_factories)); - auto client = std::make_shared(completion_queue_, broker_channel); - return client; - } - - void fillHeartbeatRequest(HeartbeatRequest& heartbeat_request) { - heartbeat_request.set_client_id("client_id_0"); - auto consumer_data = heartbeat_request.mutable_consumer_data(); - consumer_data->mutable_group()->set_resource_namespace(resource_namespace_); - consumer_data->mutable_group()->set_name(topic_); - - auto subscription_entry = new rmq::SubscriptionEntry; - subscription_entry->mutable_topic()->set_name(topic_); - subscription_entry->mutable_topic()->set_resource_namespace(resource_namespace_); - subscription_entry->mutable_expression()->set_type(rmq::FilterType::TAG); - subscription_entry->mutable_expression()->set_expression("*"); - consumer_data->mutable_subscriptions()->AddAllocated(subscription_entry); - heartbeat_request.set_fifo_flag(false); - } - - std::string name_server_target_{"47.98.116.189:80"}; - std::shared_ptr completion_queue_; - std::shared_ptr name_server_channel_; - std::string topic_{"cpp_sdk_standard"}; - std::string group_{"GID_cpp_sdk_standard"}; - std::string resource_namespace_{"MQ_INST_1080056302921134_BXuIbML7"}; - std::string tenant_id_{"sample-tenant"}; - std::string region_id_{"cn-hangzhou"}; - std::string service_name_{"MQ"}; - absl::flat_hash_map metadata_; - ClientConfigImpl client_config_{group_}; - CredentialsProviderPtr credentials_provider_; - std::shared_ptr certificate_provider_; - grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_options_; - std::shared_ptr server_authorization_check_config_; - std::shared_ptr channel_credential_; - grpc::ChannelArguments channel_arguments_; - std::string message_id_{UniqueIdGenerator::instance().next()}; -}; - -TEST_F(RpcClientTest, testRouteInfo) { - RpcClientImpl client(completion_queue_, name_server_channel_); - QueryRouteRequest request; - request.mutable_topic()->set_name(topic_); - request.mutable_topic()->set_resource_namespace(resource_namespace_); - auto invocation_context = new InvocationContext(); - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - auto callback = [&](const InvocationContext* invocation_context) { - if (!invocation_context->status.ok()) { - SPDLOG_ERROR("Status not OK"); - } - ASSERT_TRUE(invocation_context->status.ok()); - EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); - EXPECT_FALSE(invocation_context->response.partitions().empty()); - { - absl::MutexLock lk(&mtx); - cv.SignalAll(); - } - }; - - invocation_context->callback = callback; - invocation_context->context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(3)); - for (const auto& item : metadata_) { - invocation_context->context.AddMetadata(item.first, item.second); - } - client.asyncQueryRoute(request, invocation_context); - - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -TEST_F(RpcClientTest, DISABLED_testSendMessageAsync) { - SendMessageRequest request; - fillSendMessageRequest(request); - auto client = brokerRpcClient(); - auto context = new InvocationContext(); - - for (const auto& entry : metadata_) { - context->context.AddMetadata(entry.first, entry.second); - } - - context->callback = [](const InvocationContext* invocation_context) { - if ((!invocation_context->status.ok())) { - std::cout << "error code: " << invocation_context->status.error_code() - << ", error message: " << invocation_context->status.error_message() << std::endl; - } - ASSERT_TRUE(invocation_context->status.ok()); - }; - client->asyncSend(request, context); - std::thread th([&]() { - InvocationContext* ctx; - bool ok = false; - completion_queue_->Next(reinterpret_cast(&ctx), &ok); - if (ok) { - ctx->onCompletion(ok); - } - }); - - if (th.joinable()) { - th.join(); - } -} - -TEST_F(RpcClientTest, DISABLED_testHeartbeat) { - auto client = brokerRpcClient(); - HeartbeatRequest heartbeat_request; - HeartbeatResponse response; - fillHeartbeatRequest(heartbeat_request); - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto invocation_context = new InvocationContext(); - - for (const auto& entry : metadata_) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - - invocation_context->callback = [&](const InvocationContext* invocation_context) { - ASSERT_TRUE(invocation_context->status.ok()); - EXPECT_TRUE(google::rpc::Code::OK == invocation_context->response.common().status().code()); - { - completed = true; - absl::MutexLock lk(&mtx); - cv.SignalAll(); - } - }; - - client->asyncHeartbeat(heartbeat_request, invocation_context); - - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -TEST_F(RpcClientTest, DISABLED_testQueryAssignment) { - auto client = brokerRpcClient(); - QueryAssignmentRequest request; - request.mutable_topic()->set_name(topic_); - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_resource_namespace(resource_namespace_); - request.mutable_group()->set_name(group_); - QueryAssignmentResponse response; - - auto invocation_context = new InvocationContext(); - - for (const auto& entry : metadata_) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - auto callback = [&](const InvocationContext* invocation_context) { - ASSERT_TRUE(invocation_context->status.ok()); - ASSERT_FALSE(invocation_context->response.assignments().empty()); - completed = true; - { - absl::MutexLock lk(&mtx); - cv.SignalAll(); - } - }; - invocation_context->callback = callback; - client->asyncQueryAssignment(request, invocation_context); - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -TEST_F(RpcClientTest, DISABLED_testHealthCheck) { - auto client = brokerRpcClient(); - HealthCheckRequest request; - const std::string& client_host = UtilAll::hostname(); - request.set_client_host(client_host); - HealthCheckResponse response; - auto invocation_context = new InvocationContext(); - - for (const auto& entry : metadata_) { - invocation_context->context.AddMetadata(entry.first, entry.second); - } - - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - invocation_context->callback = [&](const InvocationContext* invocation_context) { - { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - } - EXPECT_TRUE(invocation_context->status.ok()); - EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); - }; - client->asyncHealthCheck(request, invocation_context); - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopAddressingTest.cpp b/src/test/cpp/it/TopAddressingTest.cpp deleted file mode 100644 index 42c7a13e8..000000000 --- a/src/test/cpp/it/TopAddressingTest.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "absl/synchronization/mutex.h" -#include "grpc/grpc.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" - -#include "RateLimiter.h" -#include "TopAddressing.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class TopAddressingTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - spdlog::set_level(spdlog::level::debug); - } - - void TearDown() override { - grpc_shutdown(); - } - - void SetEnv(const char* key, const char* value) { - int overwrite = 1; -#ifdef _WIN32 - std::string env; - env.append(key); - env.push_back('='); - env.append(value); - _putenv(env.c_str()); -#else - setenv(key, value, overwrite); -#endif - } -}; - -TEST_F(TopAddressingTest, testFetchNameServerAddresses) { - std::vector list; - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - - bool success = false; - auto callback = [&](bool ok, const std::vector& name_server_list) { - success = ok; - list.insert(list.end(), name_server_list.begin(), name_server_list.end()); - { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - } - }; - TopAddressing top_addressing; - top_addressing.fetchNameServerAddresses(callback); - - while (!completed) { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithTimeout(&mtx, absl::Seconds(3)); - } - } - - ASSERT_TRUE(success); - EXPECT_FALSE(list.empty()); -} - -TEST_F(TopAddressingTest, testFetchNameServerAddresses_env) { - SetEnv(HostInfo::ENV_LABEL_UNIT, "CENTER_UNIT.center"); - SetEnv(HostInfo::ENV_LABEL_STAGE, "DAILY"); - std::vector list; - - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - - bool success = false; - auto callback = [&](bool ok, const std::vector& name_server_list) { - success = ok; - list.insert(list.end(), name_server_list.begin(), name_server_list.end()); - { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - } - }; - TopAddressing top_addressing; - top_addressing.fetchNameServerAddresses(callback); - - while (!completed) { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithTimeout(&mtx, absl::Seconds(3)); - } - } - - ASSERT_TRUE(success); - EXPECT_FALSE(list.empty()); -} - -TEST_F(TopAddressingTest, testPerf) { - TopAddressing top_addressing; - RateLimiter<10> rate_limiter(100); - - std::atomic_bool stopped(false); - std::atomic_long qps(0); - auto callback = [&](bool ok, const std::vector& name_sever_list) { - if (ok) { - qps++; - } else { - SPDLOG_WARN("Yuck, Bad HTTP response"); - } - }; - - auto benchmark = [&]() { - while (!stopped) { - // rate_limiter.acquire(); - SPDLOG_DEBUG("Submit a fetch request"); - top_addressing.fetchNameServerAddresses(callback); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - }; - - auto stats = [&]() { - while (!stopped) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - long value = qps.load(std::memory_order_relaxed); - qps.fetch_sub(value, std::memory_order_relaxed); - SPDLOG_INFO("QPS: {}", value); - } - }; - - std::thread benchmark_thread(benchmark); - std::thread stats_thread(stats); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - stopped.store(true); - - if (stats_thread.joinable()) { - stats_thread.join(); - } - - if (benchmark_thread.joinable()) { - benchmark_thread.join(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/it/TopicPublishInfoTest.cpp b/src/test/cpp/it/TopicPublishInfoTest.cpp deleted file mode 100644 index 1edfea028..000000000 --- a/src/test/cpp/it/TopicPublishInfoTest.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TopicPublishInfo.h" -#include "ClientConfigImpl.h" -#include "LogInterceptorFactory.h" -#include "RpcClientImpl.h" -#include "Signature.h" -#include "TlsHelper.h" -#include "grpcpp/security/tls_credentials_options.h" -#include "rocketmq/MQMessageQueue.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class TopicPublishInfoTest : public ::testing::Test { -protected: - TopicPublishInfoTest() : completion_queue_(std::make_shared()) { - server_authorization_check_config_ = std::make_shared( - std::make_shared()); - tls_channel_credential_option_.set_server_verification_option(GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); - - std::vector pem_list; - grpc::experimental::IdentityKeyCertPair pair{}; - pair.private_key = TlsHelper::client_private_key; - pair.certificate_chain = TlsHelper::client_certificate_chain; - pem_list.emplace_back(pair); - certificate_provider_ = - std::make_shared(TlsHelper::CA, pem_list); - tls_channel_credential_option_.set_certificate_provider(certificate_provider_); - tls_channel_credential_option_.set_server_authorization_check_config(server_authorization_check_config_); - tls_channel_credential_option_.watch_root_certs(); - tls_channel_credential_option_.watch_identity_key_cert_pairs(); - channel_credential_ = grpc::experimental::TlsCredentials(tls_channel_credential_option_); - credentials_provider_ = std::make_shared(); - } - - void SetUp() override { - std::vector name_server_list; - - if (!name_server_list.empty()) { - target_ = *name_server_list.begin(); - } - std::vector> interceptor_factories; - client_config_.tenantId(tenant_id_); - client_config_.setCredentialsProvider(credentials_provider_); - interceptor_factories.emplace_back(absl::make_unique()); - auto channel = grpc::experimental::CreateCustomChannelWithInterceptors( - target_, channel_credential_, channel_arguments_, std::move(interceptor_factories)); - client_ = std::make_shared(completion_queue_, channel); - - client_config_.resourceNamespace(resource_namespace_); - client_config_.setCredentialsProvider(std::make_shared()); - client_config_.setIoTimeout(absl::Seconds(3)); - - Signature::sign(&client_config_, metadata_); - } - - void TearDown() override { - completion_queue_->Shutdown(); - } - - std::string topic_{"yc001"}; - std::string group_{"yc001"}; - std::string resource_namespace_{"MQ_INST_1973281269661160_BXmPlOA6"}; - std::string tenant_id_{"sample-tenant"}; - std::string region_id_{"cn-hangzhou"}; - std::string service_name_{"MQ"}; - std::string target_{"dns:grpc.dev:9876"}; - ClientConfigImpl client_config_{group_}; - absl::flat_hash_map metadata_; - std::shared_ptr completion_queue_; - std::shared_ptr client_; - CredentialsProviderPtr credentials_provider_; - std::shared_ptr certificate_provider_; - grpc::experimental::TlsChannelCredentialsOptions tls_channel_credential_option_; - std::shared_ptr server_authorization_check_config_; - std::shared_ptr channel_credential_; - grpc::ChannelArguments channel_arguments_; -}; - -TEST_F(TopicPublishInfoTest, testTopicPublishInfo) { - rmq::QueryRouteResponse response; - rmq::QueryRouteRequest request; - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_topic()->set_name(topic_); - auto invocation_context = new InvocationContext(); - invocation_context->context.set_deadline(std::chrono::system_clock::now() + - absl::ToChronoMilliseconds(client_config_.getIoTimeout())); - for (const auto& item : metadata_) { - invocation_context->context.AddMetadata(item.first, item.second); - } - - auto callback = [this](const InvocationContext* invocation_context) { - if (!invocation_context->status.ok()) { - std::cout << "code: " << invocation_context->status.error_code() - << ", message: " << invocation_context->status.error_message() << std::endl; - } else { - std::cout << "Response debug string:" << invocation_context->response.DebugString() << std::endl; - } - ASSERT_TRUE(invocation_context->status.ok()); - - std::vector partitions; - for (const auto& item : invocation_context->response.partitions()) { - Topic topic(resource_namespace_, topic_); - Permission permission; - switch (item.permission()) { - case rmq::Permission::READ: - permission = Permission::READ; - break; - case rmq::Permission::WRITE: - permission = Permission::WRITE; - break; - case rmq::Permission::READ_WRITE: - permission = Permission::READ_WRITE; - break; - default: - permission = Permission::NONE; - break; - } - - AddressScheme scheme; - switch (item.broker().endpoints().scheme()) { - case rmq::AddressScheme::IPv4: - scheme = AddressScheme::IPv4; - break; - case rmq::AddressScheme::IPv6: - scheme = AddressScheme::IPv6; - break; - case rmq::AddressScheme::DOMAIN_NAME: - scheme = AddressScheme::DOMAIN_NAME; - break; - default: - scheme = AddressScheme::IPv4; - } - - std::vector
addresses; - for (const auto& host_port : item.broker().endpoints().addresses()) { - Address address(host_port.host(), host_port.port()); - addresses.emplace_back(address); - } - ServiceAddress service_address(scheme, addresses); - - Broker broker(item.broker().name(), item.broker().id(), service_address); - Partition partition(topic, item.id(), permission, broker); - partitions.emplace_back(partition); - } - auto topic_route_data = - std::make_shared(partitions, invocation_context->response.common().DebugString()); - rocketmq::TopicPublishInfo topic_publish_info(topic_, topic_route_data); - rocketmq::MQMessageQueue message_queue; - EXPECT_TRUE(topic_publish_info.selectOneMessageQueue(message_queue)); - - EXPECT_STREQ(topic_.c_str(), message_queue.getTopic().c_str()); - }; - - invocation_context->callback = callback; - - client_->asyncQueryRoute(request, invocation_context); -} - -ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/BUILD b/src/test/cpp/ut/BUILD deleted file mode 100644 index 9bf771b53..000000000 --- a/src/test/cpp/ut/BUILD +++ /dev/null @@ -1 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/BUILD.bazel b/src/test/cpp/ut/BUILD.bazel deleted file mode 100644 index 2695ae24a..000000000 --- a/src/test/cpp/ut/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test", "cc_binary") \ No newline at end of file diff --git a/src/test/cpp/ut/README.md b/src/test/cpp/ut/README.md deleted file mode 100644 index f16f14eea..000000000 --- a/src/test/cpp/ut/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Introduction -This directory is supposed to contain all unit tests for this project. File hierarchy of unit test files should resemble -those of main sources. Aka, Unit test file for src/main/cpp/base/UtilAll.cpp should be src/test/cpp/base/UtilAllTest.cpp \ No newline at end of file diff --git a/src/test/cpp/ut/admin/AdminServerTest.cpp b/src/test/cpp/ut/admin/AdminServerTest.cpp deleted file mode 100644 index eb988cc64..000000000 --- a/src/test/cpp/ut/admin/AdminServerTest.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "AdminServerImpl.h" -#include "apache/rocketmq/v1/admin.grpc.pb.h" - -#include "rocketmq/RocketMQ.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "spdlog/spdlog.h" - -#include - -namespace rmq = apache::rocketmq::v1; - -ROCKETMQ_NAMESPACE_BEGIN -namespace admin { - -TEST(AdminServerTest, testSetUp) { - - auto logger = spdlog::basic_logger_mt("rocketmq_logger", "logs/test.log"); - logger->set_level(spdlog::level::debug); - spdlog::set_default_logger(logger); - - AdminServer* admin_server = new AdminServerImpl; - admin_server->start(); - - std::string address("127.0.0.1:"); - address.append(std::to_string(admin_server->port())); - auto channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials()); - - auto stub = rmq::Admin::NewStub(channel); - - rmq::ChangeLogLevelRequest request; - request.set_level(rmq::ChangeLogLevelRequest_Level_INFO); - rmq::ChangeLogLevelResponse response; - - grpc::ClientContext context; - context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(10)); - - auto status = stub->ChangeLogLevel(&context, request, &response); - - EXPECT_TRUE(status.ok()); - EXPECT_STREQ("OK", response.remark().c_str()); - EXPECT_EQ(spdlog::level::info, logger->level()); - - admin_server->stop(); - - delete admin_server; -} - -} // namespace admin -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/admin/BUILD.bazel b/src/test/cpp/ut/admin/BUILD.bazel deleted file mode 100644 index 8fe13fcb6..000000000 --- a/src/test/cpp/ut/admin/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") - -cc_test( - name = "admin_server_test", - srcs = [ - "AdminServerTest.cpp", - ], - deps = [ - "//src/main/cpp/admin:admin_server_library", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/base/BUILD.bazel b/src/test/cpp/ut/base/BUILD.bazel deleted file mode 100644 index 092ef7860..000000000 --- a/src/test/cpp/ut/base/BUILD.bazel +++ /dev/null @@ -1,153 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "histogram_test", - srcs = [ - "HistogramTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "mix_all_test", - srcs = [ - "MixAllTest.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "rate_limiter_test", - srcs = [ - "RateLimiterTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "util_all_test", - srcs = [ - "UtilAllTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - "@zlib//:zlib", - "@asio//:asio", - ], -) - -cc_test( - name = "http_client_test", - srcs = [ - "HttpClientTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "unique_id_generator_test", - srcs = [ - "UniqueIdGeneratorTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "host_info_test", - srcs = [ - "HostInfoTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "message_test", - srcs = [ - "MQMessageTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "message_ext_test", - srcs = [ - "MQMessageExtTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "top_addressing_test", - srcs = [ - "TopAddressingTest.cpp", - ], - deps = [ - "//src/main/cpp/base:base_library", - "//src/main/cpp/base/mocks:base_mocks", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "invocation_context_test", - srcs = [ - "InvocationContextTest.cpp", - ], - deps = [ - "//src/main/cpp/client:client_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "thread_pool_test", - srcs = [ - "ThreadPoolTest.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "//src/main/cpp/base:base_library", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/base/HistogramTest.cpp b/src/test/cpp/ut/base/HistogramTest.cpp deleted file mode 100644 index 388a2c43f..000000000 --- a/src/test/cpp/ut/base/HistogramTest.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "Histogram.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class HistogramTest : public testing::Test { -public: - HistogramTest() : histogram_("Test", 3) { - } - - void SetUp() override { - histogram_.labels().emplace_back("Foo: "); - histogram_.labels().emplace_back("Bar: "); - histogram_.labels().emplace_back("Baz: "); - } - -protected: - Histogram histogram_; -}; - -TEST_F(HistogramTest, testHistogram_no_count) { - - std::string result; - histogram_.reportAndReset(result); - - std::string expect("Test:Foo: 0, Bar: 0, Baz: 0"); - EXPECT_STREQ(result.c_str(), expect.c_str()); -} - -TEST_F(HistogramTest, testHistogram) { - histogram_.countIn(0); - histogram_.countIn(-1); - histogram_.countIn(4); - histogram_.countIn(2); - - std::string result; - histogram_.reportAndReset(result); - - std::string expect("Test:Foo: 1, Bar: 0, Baz: 2"); - EXPECT_STREQ(result.c_str(), expect.c_str()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HostInfoTest.cpp b/src/test/cpp/ut/base/HostInfoTest.cpp deleted file mode 100644 index 3b75f0d38..000000000 --- a/src/test/cpp/ut/base/HostInfoTest.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "HostInfo.h" -#include "fmt/format.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class HostInfoTest : public testing::Test { -public: - void SetUp() override { - } - - void TearDown() override { - } - -protected: - std::string site_{"site"}; - std::string unit_{"unit"}; - std::string app_{"app"}; - std::string stage_{"stage"}; - - void SetEnv(const char* key, const char* value) { - int overwrite = 1; -#ifdef _WIN32 - std::string env; - env.append(key); - env.push_back('='); - env.append(value); - _putenv(env.c_str()); -#else - setenv(key, value, overwrite); -#endif - } -}; - -TEST_F(HostInfoTest, testQueryString) { - SetEnv(HostInfo::ENV_LABEL_SITE, site_.c_str()); - SetEnv(HostInfo::ENV_LABEL_UNIT, unit_.c_str()); - SetEnv(HostInfo::ENV_LABEL_APP, app_.c_str()); - SetEnv(HostInfo::ENV_LABEL_STAGE, stage_.c_str()); - - HostInfo host_info; - std::string query_string = host_info.queryString(); - std::string query_string_template("labels=site:{},unit:{},app:{},stage:{}"); - EXPECT_EQ(query_string, fmt::format(query_string_template, site_, unit_, app_, stage_)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/HttpClientTest.cpp b/src/test/cpp/ut/base/HttpClientTest.cpp deleted file mode 100644 index 5ea118516..000000000 --- a/src/test/cpp/ut/base/HttpClientTest.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "gtest/gtest.h" - -#include "HttpClientImpl.h" -#include "LoggerImpl.h" -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class HttpClientTest : public testing::Test { -public: - void SetUp() override { - SPDLOG_DEBUG("GHttpClient::SetUp() starts"); - http_client.start(); - SPDLOG_DEBUG("GHttpClient::SetUp() completed"); - } - - void TearDown() override { - SPDLOG_DEBUG("GHttpClientTest::TearDown() starts"); - http_client.shutdown(); - SPDLOG_DEBUG("GHttpClientTest::TearDown() completed"); - } - -protected: - HttpClientImpl http_client; -}; - -TEST_F(HttpClientTest, testBasics) { -} - -TEST_F(HttpClientTest, testGet) { - auto cb = [](int code, const std::multimap& headers, const std::string& body) { - SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); - }; - - http_client.get(HttpProtocol::HTTP, "www.baidu.com", 80, "/", cb); -} - -TEST_F(HttpClientTest, DISABLED_testJMEnv) { - auto cb = [](int code, const std::multimap& headers, const std::string& body) { - SPDLOG_INFO("Response received. Status-code: {}, Body: {}", code, body); - }; - - http_client.get(HttpProtocol::HTTP, "jmenv.tbsite.net", 8080, "/rocketmq/nsaddr", cb); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/InvocationContextTest.cpp b/src/test/cpp/ut/base/InvocationContextTest.cpp deleted file mode 100644 index 91212e5ef..000000000 --- a/src/test/cpp/ut/base/InvocationContextTest.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "InvocationContext.h" -#include "RpcClient.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(InvocationContextTest, testOnCompletion_OK) { - bool cb_invoked = false; - auto callback = [&](const InvocationContext* invocation_context) { - cb_invoked = true; - throw 1; - }; - - auto invocation_context = new InvocationContext(); - invocation_context->status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "Mock deadline exceeded"); - invocation_context->callback = callback; - invocation_context->onCompletion(true); - EXPECT_TRUE(cb_invoked); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageExtTest.cpp b/src/test/cpp/ut/base/MQMessageExtTest.cpp deleted file mode 100644 index 894cfe50f..000000000 --- a/src/test/cpp/ut/base/MQMessageExtTest.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/MQMessageExt.h" -#include "MessageAccessor.h" -#include "rocketmq/MQMessage.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class MQMessageExtTest : public testing::Test { -public: - void SetUp() override { - MessageAccessor::setMessageId(message_, msg_id_); - } - - void TearDown() override { - } - -protected: - std::string msg_id_{"msg-0"}; - std::string topic_{"test"}; - MQMessageExt message_; -}; - -TEST_F(MQMessageExtTest, testGetQueueId) { - EXPECT_EQ(message_.getQueueId(), 0); -} - -TEST_F(MQMessageExtTest, testBornTimestamp) { - auto born_timestamp = message_.bornTimestamp(); - EXPECT_TRUE(std::chrono::system_clock::now() - born_timestamp < std::chrono::seconds(1)); -} - -TEST_F(MQMessageExtTest, testGetDeliveryAttempt) { - EXPECT_EQ(message_.getDeliveryAttempt(), 0); -} - -TEST_F(MQMessageExtTest, testGetBornTimestamp) { - int64_t born_timestamp = message_.getBornTimestamp(); - uint64_t last_second = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - std::chrono::seconds(1)) - .count(); - EXPECT_TRUE(static_cast(born_timestamp) > last_second); -} - -TEST_F(MQMessageExtTest, testEqual) { - MQMessageExt other; - other.setTopic("test2"); - MessageAccessor::setMessageId(other, msg_id_); - EXPECT_TRUE(message_ == other); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MQMessageTest.cpp b/src/test/cpp/ut/base/MQMessageTest.cpp deleted file mode 100644 index c42ef2219..000000000 --- a/src/test/cpp/ut/base/MQMessageTest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/MQMessage.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class MQMessageTest : public testing::Test { -public: - void SetUp() override { - message.setTopic(topic_); - message.setKey(key_); - message.setBody(body_data_, strlen(body_data_)); - message.setDelayTimeLevel(delay_level_); - } - - void TearDown() override { - } - -protected: - std::string topic_{"Test"}; - std::string key_{"key0"}; - const char* body_data_{"body content"}; - int delay_level_{1}; - MQMessage message; -}; - -TEST_F(MQMessageTest, testAssignment) { - MQMessage msg; - msg = message; - EXPECT_EQ(msg.getTopic(), topic_); - EXPECT_EQ(msg.getDelayTimeLevel(), delay_level_); - EXPECT_EQ(*msg.getKeys().begin(), key_); - EXPECT_EQ(msg.getBody(), body_data_); -} - -TEST_F(MQMessageTest, testProperty) { - std::string key{"k"}; - std::string value{"value"}; - message.setProperty(key, value); - auto prop_value = message.getProperty(key); - EXPECT_EQ(value, prop_value); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/MixAllTest.cpp b/src/test/cpp/ut/base/MixAllTest.cpp deleted file mode 100644 index 89b3312b5..000000000 --- a/src/test/cpp/ut/base/MixAllTest.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MixAll.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN -class MixAllTest : public testing::Test { -public: - static std::string toUpperCase(const std::string& s) { - std::string result; - for (const char& c : s) { - if ('a' <= c && 'z' >= c) { - result.push_back(static_cast('A' + (c - 'a'))); - } else { - result.push_back(c); - } - } - return result; - } -}; - -TEST_F(MixAllTest, testValidate_empty_topic) { - MQMessage message; - ASSERT_FALSE(MixAll::validate(message)); -} - -TEST_F(MixAllTest, testValidate_normal_topic) { - MQMessage message("T_abc-123", "sample_body"); - ASSERT_TRUE(MixAll::validate(message)); -} - -TEST_F(MixAllTest, testValidate_topic_too_long) { - - std::string topic("T"); - for (int i = 0;; ++i) { - topic.append(std::to_string(i)); - if (topic.length() > 64) { - break; - } - } - MQMessage message(topic, "sample_body"); - ASSERT_FALSE(MixAll::validate(message)); -} - -TEST_F(MixAllTest, testValidate_body_too_large) { - std::string topic("TestTopic"); - std::string body; - - body.reserve(MixAll::MAX_MESSAGE_BODY_SIZE + 1); - for (uint32_t i = 0; i <= MixAll::MAX_MESSAGE_BODY_SIZE; ++i) { - body.append("a"); - } - ASSERT_FALSE(MixAll::validate(MQMessage(topic, body))); -} - -TEST_F(MixAllTest, testRandom) { - uint32_t left = 1; - uint32_t right = 100; - uint32_t random_number = MixAll::random(left, right); - EXPECT_TRUE(random_number >= left && random_number <= right); -} - -TEST_F(MixAllTest, testHex) { - const char* data = "abc"; - std::string hex = MixAll::hex(data, strlen(data)); - std::vector bin; - EXPECT_TRUE(MixAll::hexToBinary(hex, bin)); - EXPECT_EQ(hex, MixAll::hex(bin.data(), bin.size())); -} - -TEST_F(MixAllTest, testMD5) { - std::string data("abc"); - std::string digest; - bool success = MixAll::md5(data, digest); - std::string expect("900150983CD24FB0D6963F7D28E17F72"); - EXPECT_TRUE(success); - EXPECT_EQ(digest, expect); -} - -TEST_F(MixAllTest, testSHA1) { - std::string data("abc"); - std::string digest; - bool ok = MixAll::sha1(data, digest); - EXPECT_TRUE(ok); - std::string expect("a9993e364706816aba3e25717850c26c9cd0d89d"); - EXPECT_EQ(digest, toUpperCase(expect)); -} - -TEST_F(MixAllTest, testCrc32) { - std::string data("abc"); - std::string digest; - bool success = MixAll::crc32(data, digest); - EXPECT_TRUE(success); - std::cout << digest << std::endl; -} - -TEST_F(MixAllTest, testIsIpv4) { - const char* ip = "8.8.8.8"; - const char* host = "www.taobao.com"; - EXPECT_TRUE(MixAll::isIPv4(ip)); - EXPECT_FALSE(MixAll::isIPv4(host)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/RateLimiterTest.cpp b/src/test/cpp/ut/base/RateLimiterTest.cpp deleted file mode 100644 index ca6c047f3..000000000 --- a/src/test/cpp/ut/base/RateLimiterTest.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "RateLimiter.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN -class RateLimiterTest : public ::testing::Test { -public: - void TearDown() override { - observer.stop(); - } - -protected: - RateLimiterObserver observer; -}; - -TEST_F(RateLimiterTest, basicTest) { - std::shared_ptr> limiter(new RateLimiter<10>(1000)); - observer.subscribe(limiter); - - std::atomic_bool stopped(false); - std::atomic_long acquired(0); - - std::thread timer([&] { - std::this_thread::sleep_for(std::chrono::seconds(10)); - stopped.store(true); - }); - - std::thread report([&] { - while (!stopped) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - long qps = acquired.load(); - while (!acquired.compare_exchange_weak(qps, 0)) { - qps = acquired.load(); - } - std::cout << "QPS: " << qps << ", available=" << limiter->available() << std::endl; - } - }); - - std::thread t([&] { - while (!stopped.load()) { - limiter->acquire(); - acquired++; - } - }); - - timer.join(); - t.join(); - report.join(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/ThreadPoolTest.cpp b/src/test/cpp/ut/base/ThreadPoolTest.cpp deleted file mode 100644 index 9cbde6cfa..000000000 --- a/src/test/cpp/ut/base/ThreadPoolTest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ThreadPoolImpl.h" -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class ThreadPoolTest : public testing::Test { -public: - ThreadPoolTest() = default; - - void SetUp() override { - pool_ = absl::make_unique(2); - pool_->start(); - completed = false; - } - - void TearDown() override { - pool_->shutdown(); - } - -protected: - std::unique_ptr pool_; - absl::Mutex mtx; - absl::CondVar cv; - bool completed{false}; -}; - -TEST_F(ThreadPoolTest, testBasics) { - - auto task = [this](int cnt) { - for (int i = 0; i < cnt; i++) { - std::cout << std::this_thread::get_id() << ": It works" << std::endl; - } - { - absl::MutexLock lk(&mtx); - if (!completed) { - completed = true; - cv.SignalAll(); - } - } - }; - - for (int i = 0; i < 3; i++) { - pool_->submit(std::bind(task, 3)); - } - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.Wait(&mtx); - } - } -} - -ROCKETMQ_NAMESPACE_END diff --git a/src/test/cpp/ut/base/TopAddressingTest.cpp b/src/test/cpp/ut/base/TopAddressingTest.cpp deleted file mode 100644 index cfb6e7879..000000000 --- a/src/test/cpp/ut/base/TopAddressingTest.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "absl/synchronization/mutex.h" -#include "grpc/grpc.h" -#include "gtest/gtest.h" - -#include "HttpClientMock.h" -#include "TopAddressing.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class TopAddressingTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - top_addressing_ = absl::make_unique(); - auto http_client_ = absl::make_unique>(); - auto mock_get = - [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) { - std::multimap headers; - std::string body("10.0.0.1:9876"); - cb(200, headers, body); - }; - ON_CALL(*http_client_, get).WillByDefault(testing::Invoke(mock_get)); - top_addressing_->injectHttpClient(std::move(http_client_)); - } - - void TearDown() override { - grpc_shutdown(); - } - -protected: - std::unique_ptr top_addressing_; -}; - -TEST_F(TopAddressingTest, testFetchNameServerAddresses) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto callback = [&](bool ok, const std::vector& name_server_list) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - top_addressing_->fetchNameServerAddresses(callback); - { - absl::MutexLock lk(&mtx); - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - EXPECT_TRUE(completed); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp b/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp deleted file mode 100644 index d78e1347b..000000000 --- a/src/test/cpp/ut/base/UniqueIdGeneratorTest.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "UniqueIdGenerator.h" -#include "absl/container/flat_hash_set.h" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" - -#include -ROCKETMQ_NAMESPACE_BEGIN - -TEST(UniqueIdGeneratorTest, testOutputSampleId) { - std::cout << "A sample unique ID: " << UniqueIdGenerator::instance().next() << std::endl; -} - -TEST(UniqueIdGeneratorTest, testNext) { - absl::flat_hash_set id_set; - uint32_t total = 500000; - uint32_t count = 0; - while (total--) { - std::string id = UniqueIdGenerator::instance().next(); - if (id_set.contains(id)) { - SPDLOG_WARN("Yuck, found an duplicated ID: {}", id); - } else { - id_set.insert(id); - } - ++count; - } - EXPECT_EQ(count, id_set.size()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/base/UtilAllTest.cpp b/src/test/cpp/ut/base/UtilAllTest.cpp deleted file mode 100644 index 9ef8c83c7..000000000 --- a/src/test/cpp/ut/base/UtilAllTest.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "UtilAll.h" -#include "MixAll.h" -#include "absl/strings/str_split.h" -#include "asio.hpp" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class UtilAllTest : public testing::Test { -public: - void SetUp() override { - } - - void TearDown() override { - } -}; - -TEST_F(UtilAllTest, testCompress) { - std::string src("How are you doing?"); - std::string dst; - bool success = UtilAll::compress(src, dst); - EXPECT_TRUE(success); - EXPECT_FALSE(dst.empty()); -} - -TEST_F(UtilAllTest, testUncompress) { - std::string raw("What is your favorite color?"); - std::string compressed; - EXPECT_TRUE(UtilAll::compress(raw, compressed)); - std::string uncompressed; - EXPECT_TRUE(UtilAll::uncompress(compressed, uncompressed)); - EXPECT_EQ(raw.length(), uncompressed.length()); - EXPECT_EQ(raw, uncompressed); -} - -TEST_F(UtilAllTest, benchmarkTest) { - std::string raw; - uint32_t len = 1024 * 1024; - raw.reserve(len); - for (uint32_t i = 0; i < len; i++) { - raw.push_back(i % 128); - } - - std::string compressed; - auto now = std::chrono::steady_clock::now(); - UtilAll::compress(raw, compressed); - auto elapsed = std::chrono::steady_clock::now() - now; - EXPECT_TRUE(elapsed < std::chrono::milliseconds(100)); - EXPECT_TRUE(len / compressed.length() >= 5); -} - -TEST_F(UtilAllTest, split) { - std::string ip("8.8.8.8"); - std::vector segments = absl::StrSplit(ip, '.'); - std::vector expected = {"8", "8", "8", "8"}; - EXPECT_EQ(expected, segments); -} - -TEST_F(UtilAllTest, macAddrss) { - std::vector mac; - bool success = UtilAll::macAddress(mac); - ASSERT_TRUE(success); - std::cout << MixAll::hex(mac.data(), mac.size()) << std::endl; -} - -TEST_F(UtilAllTest, testAsioGetHostName) { - auto&& host_name = asio::ip::host_name(); - std::cout << host_name << std::endl; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/BUILD.bazel b/src/test/cpp/ut/client/BUILD.bazel deleted file mode 100644 index b0757cb35..000000000 --- a/src/test/cpp/ut/client/BUILD.bazel +++ /dev/null @@ -1,86 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "tls_helper_test", - srcs = [ - "TlsHelperTest.cpp", - ], - deps = [ - "//src/main/cpp/admin:admin_server_library", - "//src/main/cpp/client:client_library", - "@com_google_googletest//:gtest_main", - "@boringssl//:ssl", - ], -) - -cc_test( - name = "rpc_client_test", - srcs = [ - "RpcClientTest.cpp", - ], - deps = [ - "//src/main/cpp/client:client_library", - "//src/main/cpp/client/mocks:client_mocks", - "@com_google_googletest//:gtest_main", - "@boringssl//:ssl", - ], -) - -cc_test( - name = "client_test", - srcs = [ - "ClientTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "client_manager_test", - srcs = [ - "ClientManagerTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "client_manager_factory_test", - srcs = [ - "ClientManagerFactoryTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "topic_assignment_info_test", - srcs = [ - "TopicAssignmentInfoTest.cpp", - ], - deps = [ - "//src/main/cpp/client:client_library", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp b/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp deleted file mode 100644 index e48200e41..000000000 --- a/src/test/cpp/ut/client/ClientManagerFactoryTest.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClientManagerFactory.h" -#include "ClientConfigMock.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ClientManagerFactoryTest : public testing::Test { -public: - void SetUp() override { - } - - void TearDown() override { - } - -protected: - testing::NiceMock client_config_; - std::string resource_namespace_{"mq://test"}; -}; - -TEST_F(ClientManagerFactoryTest, testGetClientManager) { - EXPECT_CALL(client_config_, resourceNamespace) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::ReturnRef(resource_namespace_)); - ClientManagerPtr client_manager = ClientManagerFactory::getInstance().getClientManager(client_config_); - EXPECT_TRUE(client_manager); - client_manager->start(); - - client_manager->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/ClientManagerTest.cpp b/src/test/cpp/ut/client/ClientManagerTest.cpp deleted file mode 100644 index 3ab764802..000000000 --- a/src/test/cpp/ut/client/ClientManagerTest.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClientManagerImpl.h" -#include "ReceiveMessageCallbackMock.h" -#include "RpcClientMock.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "gtest/gtest.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class ClientManagerTest : public testing::Test { -public: - void SetUp() override { - client_manager_ = std::make_shared(resource_namespace_); - client_manager_->start(); - rpc_client_ = std::make_shared>(); - ON_CALL(*rpc_client_, ok).WillByDefault(testing::Return(true)); - client_manager_->addRpcClient(target_host_, rpc_client_); - receive_message_callback_ = std::make_shared>(); - metadata_.insert({"foo", "bar"}); - metadata_.insert({"name", "Donald.J.Trump"}); - } - - void TearDown() override { - client_manager_->shutdown(); - } - -protected: - std::string resource_namespace_{"mq://test"}; - std::string topic_{"TestTopic"}; - std::string target_host_{"ipv4:10.0.0.0:10911"}; - std::shared_ptr client_manager_; - std::shared_ptr> rpc_client_; - std::shared_ptr> receive_message_callback_; - absl::Duration io_timeout_{absl::Seconds(3)}; - Metadata metadata_; - std::string message_body_{"Message body"}; - std::string tag_{"TagA"}; - std::string key_{"key-0"}; -}; - -TEST_F(ClientManagerTest, testBasic) { - // Ensure that start/shutdown works well. -} - -TEST_F(ClientManagerTest, testResolveRoute) { - auto rpc_cb = [](const QueryRouteRequest& request, InvocationContext* invocation_context) { - auto partition = new rmq::Partition(); - partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); - partition->mutable_topic()->set_name(request.topic().name()); - partition->mutable_broker()->set_name("broker-0"); - partition->mutable_broker()->set_id(0); - auto address = new rmq::Address(); - address->set_host("10.0.0.1"); - address->set_port(10911); - partition->mutable_broker()->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); - partition->mutable_broker()->mutable_endpoints()->mutable_addresses()->AddAllocated(address); - invocation_context->response.mutable_partitions()->AddAllocated(partition); - - invocation_context->onCompletion(true); - }; - EXPECT_CALL(*rpc_client_, asyncQueryRoute).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(rpc_cb)); - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - QueryRouteRequest request; - request.mutable_topic()->set_resource_namespace(resource_namespace_); - request.mutable_topic()->set_name(topic_); - auto callback = [&](const std::error_code& ec, const TopicRouteDataPtr&) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - client_manager_->resolveRoute(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - { - absl::MutexLock lk(&mtx); - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - EXPECT_TRUE(completed); -} - -TEST_F(ClientManagerTest, testQueryAssignment) { - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_query_assignment = [&](const QueryAssignmentRequest& request, - InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncQueryAssignment) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_query_assignment)); - QueryAssignmentRequest request; - bool callback_invoked = false; - auto callback = [&](const std::error_code& ec, const QueryAssignmentResponse& response) { callback_invoked = true; }; - - client_manager_->queryAssignment(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -TEST_F(ClientManagerTest, testReceiveMessage) { - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_async_receive = [&](const ReceiveMessageRequest& request, - InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncReceive) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_async_receive)); - ReceiveMessageRequest request; - - EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); - - client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), - receive_message_callback_); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); -} - -TEST_F(ClientManagerTest, testReceiveMessage_Failure) { - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_async_receive = [&](const ReceiveMessageRequest& request, - InvocationContext* invocation_context) { - invocation_context->status = grpc::Status::CANCELLED; - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncReceive) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_async_receive)); - ReceiveMessageRequest request; - - EXPECT_CALL(*receive_message_callback_, onCompletion).Times(testing::AtLeast(1)); - - client_manager_->receiveMessage(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), - receive_message_callback_); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); -} - -TEST_F(ClientManagerTest, testAck) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_ack = [&](const AckMessageRequest& request, InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncAck).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_ack)); - AckMessageRequest request; - bool callback_invoked = false; - auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; - - client_manager_->ack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -TEST_F(ClientManagerTest, testNack) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_nack = [&](const NackMessageRequest& request, InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncNack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_nack)); - NackMessageRequest request; - bool callback_invoked = false; - auto callback = [&](const std::error_code& ec) { callback_invoked = true; }; - - client_manager_->nack(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -TEST_F(ClientManagerTest, testForwardMessageToDeadLetterQueue) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_forward = [&](const ForwardMessageToDeadLetterQueueRequest& request, - InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncForwardMessageToDeadLetterQueue) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_forward)); - ForwardMessageToDeadLetterQueueRequest request; - bool callback_invoked = false; - auto callback = [&](bool ok) { callback_invoked = true; }; - - client_manager_->forwardMessageToDeadLetterQueue(target_host_, metadata_, request, - absl::ToChronoMilliseconds(io_timeout_), callback); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -TEST_F(ClientManagerTest, testMultiplexingCall) { -} - -TEST_F(ClientManagerTest, testEndTransaction) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_end_transaction = [&](const EndTransactionRequest& request, - InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncEndTransaction) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_end_transaction)); - EndTransactionRequest request; - bool callback_invoked = false; - auto callback = [&](const std::error_code& ec, const EndTransactionResponse& response) { callback_invoked = true; }; - - client_manager_->endTransaction(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -TEST_F(ClientManagerTest, testHealthCheck) { - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto mock_health_check = [&](const HealthCheckRequest& request, - InvocationContext* invocation_context) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - invocation_context->onCompletion(true); - }; - - EXPECT_CALL(*rpc_client_, asyncHealthCheck) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_health_check)); - HealthCheckRequest request; - bool callback_invoked = false; - auto callback = [&](const std::error_code& ec, const InvocationContext* invocation_context) { - callback_invoked = true; - }; - - client_manager_->healthCheck(target_host_, metadata_, request, absl::ToChronoMilliseconds(io_timeout_), callback); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - EXPECT_TRUE(callback_invoked); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/RpcClientTest.cpp b/src/test/cpp/ut/client/RpcClientTest.cpp deleted file mode 100644 index f57477978..000000000 --- a/src/test/cpp/ut/client/RpcClientTest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "RpcClientMock.h" - -#include "google/rpc/code.pb.h" -#include "grpcpp/impl/grpc_library.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -namespace ut { - -class RpcClientTest : public testing::Test { - -public: - void SetUp() override { - grpc::internal::GrpcLibraryInitializer initializer; - } - - static void mockQueryRouteInfo(const QueryRouteRequest& request, - InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); - for (int i = 0; i < 3; ++i) { - auto partition = new rmq::Partition; - partition->mutable_topic()->set_name(request.topic().name()); - partition->mutable_broker()->set_name(fmt::format("broker-{}", i)); - partition->mutable_broker()->set_id(0); - auto endpoint = partition->mutable_broker()->mutable_endpoints(); - auto address = new rmq::Address; - address->set_host(fmt::format("10.0.0.{}", i)); - address->set_port(10911); - endpoint->mutable_addresses()->AddAllocated(address); - invocation_context->response.mutable_partitions()->AddAllocated(partition); - } - - invocation_context->onCompletion(true); - } -}; - -TEST_F(RpcClientTest, testMockedGetRouteInfo) { - RpcClientMock rpc_client_mock; - ON_CALL(rpc_client_mock, asyncQueryRoute(testing::_, testing::_)).WillByDefault(testing::Invoke(mockQueryRouteInfo)); - std::string topic = "sample_topic"; - QueryRouteRequest request; - request.mutable_topic()->set_name(topic); - absl::flat_hash_map metadata; - auto invocation_context = new InvocationContext(); - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto callback = [&](const InvocationContext* invocation_context) { - EXPECT_TRUE(invocation_context->status.ok()); - EXPECT_EQ(google::rpc::Code::OK, invocation_context->response.common().status().code()); - EXPECT_EQ(3, invocation_context->response.partitions().size()); - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - invocation_context->callback = callback; - rpc_client_mock.asyncQueryRoute(request, invocation_context); - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -} // namespace ut -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TlsHelperTest.cpp b/src/test/cpp/ut/client/TlsHelperTest.cpp deleted file mode 100644 index 89745de50..000000000 --- a/src/test/cpp/ut/client/TlsHelperTest.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TlsHelper.h" -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(TlsHelperTest, testSign) { - const char* data = "some random data for test purpose only"; - const char* access_secret = "arbitrary-access-key"; - const std::string& signature = TlsHelper::sign(access_secret, data); - const char* expect = "567868dc8e81f1e8095f88958edff1e07db4290e"; - EXPECT_STRCASEEQ(expect, signature.c_str()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp b/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp deleted file mode 100644 index 003fab703..000000000 --- a/src/test/cpp/ut/client/TopicAssignmentInfoTest.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TopicAssignmentInfo.h" -#include "rocketmq/ConsumeType.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class QueryAssignmentInfoTest : public testing::Test { -protected: - std::string resource_namespace_{"mq://test"}; - std::string topic_{"TopicTest"}; - std::string broker_name_{"broker-a"}; - int broker_id_ = 0; - int total_ = 16; -}; - -TEST_F(QueryAssignmentInfoTest, testCtor) { - QueryAssignmentResponse response; - for (int i = 0; i < total_; i++) { - auto assignment = new rmq::Assignment; - assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - assignment->mutable_partition()->mutable_topic()->set_name(topic_); - assignment->mutable_partition()->set_id(i); - assignment->mutable_partition()->set_permission(rmq::Permission::READ); - auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name_); - broker->set_id(broker_id_); - broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); - - auto address = new rmq::Address; - address->set_host("10.0.0.1"); - address->set_port(10911); - broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); - response.mutable_assignments()->AddAllocated(assignment); - } - TopicAssignment assignment(response); - EXPECT_EQ(total_, assignment.assignmentList().size()); - const auto& item = *assignment.assignmentList().begin(); - EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); - EXPECT_EQ(item.messageQueue().getTopic(), topic_); - EXPECT_TRUE(item.messageQueue().getQueueId() < 16); -} - -TEST_F(QueryAssignmentInfoTest, testCtor2) { - QueryAssignmentResponse response; - for (int i = 0; i < total_; i++) { - auto assignment = new rmq::Assignment; - assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - assignment->mutable_partition()->mutable_topic()->set_name(topic_); - assignment->mutable_partition()->set_id(i); - assignment->mutable_partition()->set_permission(rmq::Permission::READ_WRITE); - auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name_); - broker->set_id(broker_id_); - broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); - - auto address = new rmq::Address; - address->set_host("10.0.0.1"); - address->set_port(10911); - broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); - response.mutable_assignments()->AddAllocated(assignment); - } - TopicAssignment assignment(response); - EXPECT_EQ(total_, assignment.assignmentList().size()); - const auto& item = *assignment.assignmentList().begin(); - EXPECT_EQ(item.messageQueue().getBrokerName(), broker_name_); - EXPECT_EQ(item.messageQueue().getTopic(), topic_); - EXPECT_TRUE(item.messageQueue().getQueueId() < 16); -} - -TEST_F(QueryAssignmentInfoTest, testCtor3) { - QueryAssignmentResponse response; - for (int i = 0; i < total_; i++) { - auto assignment = new rmq::Assignment; - assignment->mutable_partition()->mutable_topic()->set_resource_namespace(resource_namespace_); - assignment->mutable_partition()->mutable_topic()->set_name(topic_); - assignment->mutable_partition()->set_id(i); - assignment->mutable_partition()->set_permission(rmq::Permission::NONE); - auto broker = assignment->mutable_partition()->mutable_broker(); - broker->set_name(broker_name_); - broker->set_id(broker_id_); - broker->mutable_endpoints()->set_scheme(rmq::AddressScheme::IPv4); - - auto address = new rmq::Address; - address->set_host("10.0.0.1"); - address->set_port(10911); - broker->mutable_endpoints()->mutable_addresses()->AddAllocated(address); - response.mutable_assignments()->AddAllocated(assignment); - } - TopicAssignment assignment(response); - EXPECT_TRUE(assignment.assignmentList().empty()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/client/TracingUtilityTest.cpp b/src/test/cpp/ut/client/TracingUtilityTest.cpp deleted file mode 100644 index 42b6cf0e5..000000000 --- a/src/test/cpp/ut/client/TracingUtilityTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TracingUtility.h" -#include "gtest/gtest.h" -#include - -using namespace opentelemetry::trace; - -ROCKETMQ_NAMESPACE_BEGIN - -template -static std::string hex(const T& id_item) { - char buf[T::kSize * 2]; - id_item.ToLowerBase16(buf); - return std::string(buf, sizeof(buf)); -} - -TEST(TracingUtilityTest, testInject) { - constexpr uint8_t buf_span[] = {1, 2, 3, 4, 5, 6, 7, 8}; - constexpr uint8_t buf_trace[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - SpanContext span_context{TraceId{buf_trace}, SpanId{buf_span}, TraceFlags{true}, true}; - EXPECT_EQ(TracingUtility::injectSpanContextToTraceParent(span_context), - "00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); -} - -TEST(TracingUtilityTest, testExtract) { - SpanContext span_context = - TracingUtility::extractContextFromTraceParent("00-0102030405060708090a0b0c0d0e0f10-0102030405060708-01"); - - EXPECT_EQ(hex(span_context.trace_id()), "0102030405060708090a0b0c0d0e0f10"); - EXPECT_EQ(hex(span_context.span_id()), "0102030405060708"); - EXPECT_TRUE(span_context.IsSampled()); - EXPECT_TRUE(span_context.IsRemote()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/BUILD.bazel b/src/test/cpp/ut/concurrent/BUILD.bazel deleted file mode 100644 index 00e4ad82d..000000000 --- a/src/test/cpp/ut/concurrent/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") -cc_test( - name = "countdown_latch_test", - srcs = [ - "CountdownLatchTest.cpp", - ], - deps = [ - "//src/main/cpp/concurrent:countdown_latch_library", - "@com_github_grpc_grpc//:grpc++", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp b/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp deleted file mode 100644 index 3204a6055..000000000 --- a/src/test/cpp/ut/concurrent/CountdownLatchTest.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "CountdownLatch.h" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(CountdownLatchTest, testCountdown) { - CountdownLatch countdown_latch(0); - - countdown_latch.increaseCount(); - - auto start = std::chrono::system_clock::now(); - - std::thread t([&] { - std::this_thread::sleep_for(std::chrono::seconds(3)); - spdlog::info("Counting down now..."); - countdown_latch.countdown(); - }); - - spdlog::info("Start to await"); - countdown_latch.await(); - spdlog::info("Await over"); - auto duration = std::chrono::system_clock::now() - start; - EXPECT_TRUE(std::chrono::duration_cast(duration).count() >= 3); - - if (t.joinable()) { - t.join(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/logger/LoggerTest.cpp b/src/test/cpp/ut/logger/LoggerTest.cpp deleted file mode 100644 index 372ed177f..000000000 --- a/src/test/cpp/ut/logger/LoggerTest.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/Logger.h" -#include "LoggerImpl.h" -#include "rocketmq/RocketMQ.h" -#include "spdlog/spdlog.h" -#include "gtest/gtest.h" -ROCKETMQ_NAMESPACE_BEGIN - -class LoggerTest : public testing::Test { -protected: - std::size_t log_file_size_{1024 * 4}; -}; - -TEST_F(LoggerTest, testLogger_Trace) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Trace); - logger.setConsoleLevel(Level::Trace); - logger.init(); - SPDLOG_ERROR("=========Trace==========="); - SPDLOG_TRACE("Should show up!"); - SPDLOG_DEBUG("Should show up!"); - SPDLOG_INFO("Should show up!"); - SPDLOG_WARN("Should show up!"); - SPDLOG_ERROR("Should show up!"); -} - -TEST_F(LoggerTest, testLogger_Debug) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Debug); - logger.init(); - SPDLOG_ERROR("=========Debug==========="); - SPDLOG_TRACE("Should NOT get logged"); - SPDLOG_DEBUG("Should show up"); - SPDLOG_INFO("Should show up!!"); - SPDLOG_WARN("Should show up!"); - SPDLOG_ERROR("Should show up!"); -} - -TEST_F(LoggerTest, testLogger_Info) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Info); - logger.init(); - SPDLOG_ERROR("=========Info==========="); - SPDLOG_TRACE("Should NOT get logged"); - SPDLOG_DEBUG("Should NOT get logged"); - SPDLOG_INFO("Should show up!"); - SPDLOG_WARN("Should show up!"); - SPDLOG_ERROR("Should show up!"); -} - -TEST_F(LoggerTest, testLogger_Warn) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Warn); - logger.init(); - SPDLOG_ERROR("=========Warn==========="); - SPDLOG_TRACE("Should NOT get logged"); - SPDLOG_DEBUG("Should NOT get logged"); - SPDLOG_INFO("Should NOT get logged"); - SPDLOG_WARN("Should show up!"); - SPDLOG_ERROR("Should show up!"); -} - -TEST_F(LoggerTest, testLogger_Error) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Error); - logger.init(); - SPDLOG_ERROR("=========Error==========="); - SPDLOG_TRACE("Should NOT get logged"); - SPDLOG_DEBUG("Should NOT get logged"); - SPDLOG_INFO("Should NOT get logged"); - SPDLOG_WARN("Should NOT get logged"); - SPDLOG_ERROR("Should show up!"); -} - -TEST_F(LoggerTest, testFileRolling) { - Logger& logger = getLogger(); - logger.setFileSize(log_file_size_); - logger.setLevel(Level::Trace); - logger.init(); - for (size_t i = 0; i < 32; i++) { - SPDLOG_TRACE("Should show up!"); - SPDLOG_DEBUG("Should show up!"); - SPDLOG_INFO("Should show up!"); - SPDLOG_WARN("Should show up!"); - SPDLOG_ERROR("Should show up!"); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BUILD.bazel b/src/test/cpp/ut/remoting/BUILD.bazel deleted file mode 100644 index 68201979e..000000000 --- a/src/test/cpp/ut/remoting/BUILD.bazel +++ /dev/null @@ -1,73 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") - -cc_test( - name = "query_route_request_header_test", - srcs = [ - "QueryRouteRequestHeaderTest.cpp", - ], - deps = [ - "//src/main/cpp/remoting", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "remoting_command_test", - srcs = [ - "RemotingCommandTest.cpp", - ], - deps = [ - "//src/main/cpp/remoting", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "broker_data_test", - srcs = [ - "BrokerDataTest.cpp", - ], - deps = [ - "//src/main/cpp/remoting", - "@com_google_googletest//:gtest_main", - ] -) - - -cc_test( - name = "queue_data_test", - srcs = [ - "QueueDataTest.cpp", - ], - deps = [ - "//src/main/cpp/remoting", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "topic_route_data_test", - srcs = [ - "TopicRouteDataTest.cpp", - ], - deps = [ - "//src/main/cpp/remoting", - "@com_google_googletest//:gtest_main", - ] -) \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/BrokerDataTest.cpp b/src/test/cpp/ut/remoting/BrokerDataTest.cpp deleted file mode 100644 index 96249ebb3..000000000 --- a/src/test/cpp/ut/remoting/BrokerDataTest.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "BrokerData.h" - -#include - -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" - -#include "google/protobuf/struct.pb.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(BrokerDataTest, testDecode) { - std::string json = - R"({"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false})"; - google::protobuf::Struct root; - - auto status = google::protobuf::util::JsonStringToMessage(json, &root); - ASSERT_TRUE(status.ok()); - - BrokerData&& broker_data = BrokerData::decode(root); - EXPECT_EQ("b1", broker_data.broker_name_); - EXPECT_EQ("cluster", broker_data.cluster_); - EXPECT_EQ(2, broker_data.broker_addresses_.size()); - EXPECT_EQ("abc", broker_data.broker_addresses_.at(1)); - EXPECT_EQ("def", broker_data.broker_addresses_.at(2)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp b/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp deleted file mode 100644 index d887ba6fa..000000000 --- a/src/test/cpp/ut/remoting/QueryRouteRequestHeaderTest.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "QueryRouteRequestHeader.h" - -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(QueryRouteRequestHeaderTest, testEncode) { - google::protobuf::Value root; - QueryRouteRequestHeader header; - header.topic("abc"); - header.encode(root); - - std::string json; - auto status = google::protobuf::util::MessageToJsonString(root, &json); - EXPECT_TRUE(status.ok()); - EXPECT_FALSE(json.empty()); - std::cout << json << std::endl; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/QueueDataTest.cpp b/src/test/cpp/ut/remoting/QueueDataTest.cpp deleted file mode 100644 index 81c55ffe3..000000000 --- a/src/test/cpp/ut/remoting/QueueDataTest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gtest/gtest.h" - -#include "QueueData.h" -#include "rocketmq/RocketMQ.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(QueueDataTest, testDecode) { - std::string json = R"({"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8})"; - google::protobuf::Struct root; - auto status = google::protobuf::util::JsonStringToMessage(json, &root); - EXPECT_TRUE(status.ok()); - - QueueData queue_data = QueueData::decode(root); - EXPECT_EQ("broker1", queue_data.broker_name_); - EXPECT_EQ(6, queue_data.perm_); - EXPECT_EQ(4, queue_data.read_queue_number_); - EXPECT_EQ(8, queue_data.write_queue_number_); - EXPECT_EQ(3, queue_data.topic_system_flag_); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp b/src/test/cpp/ut/remoting/RemotingCommandTest.cpp deleted file mode 100644 index 68f887286..000000000 --- a/src/test/cpp/ut/remoting/RemotingCommandTest.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "RemotingCommand.h" - -#include -#include -#include - -#include "QueryRouteRequestHeader.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class RemotingCommandTest : public testing::Test { -public: -}; - -TEST_F(RemotingCommandTest, testNextRequestId) { - - std::unordered_set request_ids; - - for (int i = 0; i < 50; i++) { - std::int32_t id = RemotingCommand::nextRequestId(); - EXPECT_TRUE(request_ids.find(id) == request_ids.end()); - request_ids.insert(id); - } -} - -TEST_F(RemotingCommandTest, testCreateRequest) { - auto header = new QueryRouteRequestHeader; - header->topic("abc"); - - auto command = RemotingCommand::createRequest(RequestCode::QueryRoute, header); - google::protobuf::Value root; - command.encodeHeader(root); - - const auto& fields = root.struct_value().fields(); - EXPECT_TRUE(fields.contains("extFields")); - EXPECT_TRUE(fields.contains("code")); - EXPECT_EQ(static_cast(RequestCode::QueryRoute), fields.at("code").number_value()); - - std::string json; - auto status = google::protobuf::util::MessageToJsonString(root, &json); - EXPECT_TRUE(status.ok()); - std::cout << json << std::endl; -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp b/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp deleted file mode 100644 index d2718b47b..000000000 --- a/src/test/cpp/ut/remoting/TopicRouteDataTest.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "TopicRouteData.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(TopicRouteDataTest, testDecode) { - std::string json = - R"({"brokerDatas":[{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false},{"brokerAddrs":{"1":"abc","2":"def"},"brokerName":"b1","cluster":"cluster","enableActingMaster":false}],"filterServerTable":{},"queueDatas":[{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8},{"brokerName":"broker1","perm":6,"readQueueNums":4,"topicSynFlag":3,"writeQueueNums":8}]})"; - google::protobuf::Struct root; - auto status = google::protobuf::util::JsonStringToMessage(json, &root); - EXPECT_TRUE(status.ok()); - - auto topic_route_data = TopicRouteData::decode(root); - - EXPECT_FALSE(topic_route_data.broker_data_.empty()); - EXPECT_FALSE(topic_route_data.queue_data_.empty()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp b/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp deleted file mode 100644 index e8e437474..000000000 --- a/src/test/cpp/ut/rocketmq/AwaitPullCallbackTest.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "AwaitPullCallback.h" -#include "MessageAccessor.h" -#include "rocketmq/ErrorCode.h" -#include "rocketmq/MQClientException.h" -#include "rocketmq/MQMessageExt.h" -#include "rocketmq/PullResult.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(AwaitPullCallbackTest, testOnSuccess) { - std::vector messages; - PullResult pull_result(0, 32, 32, messages); - AwaitPullCallback callback(pull_result); - EXPECT_FALSE(callback.isCompleted()); - - std::string topic{"Test"}; - int total = 32; - int min = 32; - int max = 128; - int next = 64; - auto task = [&]() { - std::vector msgs; - for (int i = 0; i < total; i++) { - MQMessageExt msg; - msg.setTopic(topic); - MessageAccessor::setQueueOffset(msg, i); - msgs.emplace_back(msg); - } - PullResult result(32, 128, 64, msgs); - callback.onSuccess(result); - }; - - std::thread t(task); - - callback.await(); - EXPECT_TRUE(callback.isCompleted()); - EXPECT_EQ(pull_result.messages().size(), total); - EXPECT_EQ(pull_result.min(), min); - EXPECT_EQ(pull_result.max(), max); - EXPECT_EQ(pull_result.next(), next); - - if (t.joinable()) { - t.join(); - } -} - -TEST(AwaitPullCallbackTest, testOnFailure) { - std::vector messages; - PullResult pull_result(0, 32, 32, messages); - AwaitPullCallback callback(pull_result); - std::string topic{"Test"}; - auto task = [&]() { - std::error_code ec = ErrorCode::NotImplemented; - callback.onFailure(ec); - }; - - std::thread t(task); - - callback.await(); - EXPECT_TRUE(callback.isCompleted()); - EXPECT_TRUE(callback.hasFailure()); - if (t.joinable()) { - t.join(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel deleted file mode 100644 index 205cee162..000000000 --- a/src/test/cpp/ut/rocketmq/BUILD.bazel +++ /dev/null @@ -1,264 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test", "cc_library") - -cc_test( - name = "message_group_queue_selector_test", - srcs = [ - "MessageGroupQueueSelectorTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "executor_test", - srcs = [ - "ExecutorTest.cpp", - ], - deps = [ - "//api:rocketmq_interface", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "message_test", - srcs = [ - "MessageTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "process_queue_test", - srcs = [ - "ProcessQueueTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_library( - name = "client_interface", - hdrs = [ - "include/MQClientTest.h", - ], - srcs = [], - strip_include_prefix = "//src/test/cpp/ut/rocketmq/include", - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "default_mq_producer_test", - srcs = [ - "DefaultMQProducerTest.cpp", - ], - deps = [ - ":client_interface", - "//src/main/cpp/admin:admin_server_library", - "//src/main/cpp/rocketmq:rocketmq_library", - ], -) - -cc_test( - name = "ut_credentials_provider", - srcs = [ - "CredentialsProviderTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "ut_send_callbacks", - srcs = [ - "SendCallbacksTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "sts_credentials_provider_test", - srcs = [ - "StsCredentialsProviderImplTest.cpp", - ], - deps = [ - "//src/main/cpp/base/mocks:base_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "filter_expression_test", - srcs = [ - "FilterExpressionTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "consume_message_service_test", - srcs = [ - "ConsumeStandardMessageServiceTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", - "//src/main/cpp/base/mocks:base_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "consumer_test", - srcs = [ - "ConsumerTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "await_pull_callback_test", - srcs = [ - "AwaitPullCallbackTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "push_consumer_impl_test", - srcs = [ - "PushConsumerImplTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "producer_impl_test", - srcs = [ - "ProducerImplTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "pull_consumer_impl_test", - srcs = [ - "PullConsumerImplTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ] -) - -cc_test( - name = "client_impl_test", - srcs = [ - "ClientImplTest.cpp", - ], - deps = [ - "//src/main/cpp/base/mocks:base_mocks", - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/rocketmq:rocketmq_library", - "//src/main/cpp/rocketmq/mocks:rocketmq_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "static_name_server_resolver_test", - srcs = [ - "StaticNameServerResolverTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - - -cc_test( - name = "dynamic_name_server_resolver_test", - srcs = [ - "DynamicNameServerResolverTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "//src/main/cpp/base/mocks:base_mocks", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "default_mq_push_consumer_test", - srcs = [ - "DefaultMQPushConsumerTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "naming_scheme_test", - srcs = [ - "NamingSchemeTest.cpp", - ], - deps = [ - "//src/main/cpp/rocketmq:rocketmq_library", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp b/src/test/cpp/ut/rocketmq/ClientImplTest.cpp deleted file mode 100644 index f93fbec0c..000000000 --- a/src/test/cpp/ut/rocketmq/ClientImplTest.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "ClientImpl.h" -#include "ClientManagerFactory.h" -#include "ClientManagerMock.h" -#include "DynamicNameServerResolver.h" -#include "HttpClientMock.h" -#include "NameServerResolverMock.h" -#include "Scheduler.h" -#include "SchedulerImpl.h" -#include "TopAddressing.h" -#include "rocketmq/RocketMQ.h" - -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class TestClientImpl : public ClientImpl, public std::enable_shared_from_this { -public: - TestClientImpl(std::string group) : ClientImpl(std::move(group)) { - } - - std::shared_ptr self() override { - return shared_from_this(); - } - - void prepareHeartbeatData(HeartbeatRequest& request) override { - } -}; - -class ClientImplTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - scheduler_ = std::make_shared(); - - http_client_ = absl::make_unique>(); - name_server_resolver_ = std::make_shared(endpoint_, std::chrono::seconds(1)); - client_manager_ = std::make_shared>(); - ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); - - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); - ON_CALL(*client_manager_, start).WillByDefault([&]() { scheduler_->start(); }); - ON_CALL(*client_manager_, shutdown).WillByDefault([&]() { scheduler_->shutdown(); }); - - client_ = std::make_shared(group_); - client_->withNameServerResolver(name_server_resolver_); - } - - void TearDown() override { - grpc_shutdown(); - } - -protected: - std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; - std::string resource_namespace_{"mq://test"}; - std::string group_{"Group-0"}; - std::shared_ptr> client_manager_; - SchedulerSharedPtr scheduler_; - std::shared_ptr client_; - std::shared_ptr name_server_resolver_; - std::unique_ptr> http_client_; -}; - -TEST_F(ClientImplTest, testBasic) { - std::string once{"10.0.0.1:9876"}; - std::string then{"10.0.0.1:9876;10.0.0.2:9876"}; - std::multimap header; - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - int http_status = 200; - auto once_cb = - [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) { - cb(http_status, header, once); - }; - auto then_cb = - [&](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) { - cb(http_status, header, then); - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - - EXPECT_CALL(*http_client_, get).WillOnce(testing::Invoke(once_cb)).WillRepeatedly(testing::Invoke(then_cb)); - name_server_resolver_->injectHttpClient(std::move(http_client_)); - - client_->resourceNamespace(resource_namespace_); - client_->start(); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - - ASSERT_TRUE(completed); - - // Now that the derivative class has closed its own resources, state of ClientImpl should be STOPPING. - client_->state(State::STOPPING); - client_->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp b/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp deleted file mode 100644 index b43267e9c..000000000 --- a/src/test/cpp/ut/rocketmq/ConsumeStandardMessageServiceTest.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ConsumeStandardMessageService.h" -#include "MessageListenerMock.h" -#include "ProcessQueueMock.h" -#include "PushConsumerMock.h" -#include "grpc/grpc.h" -#include "rocketmq/CredentialsProvider.h" -#include "rocketmq/MQMessageExt.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class ConsumeStandardMessageServiceTest : public testing::Test { -public: - ConsumeStandardMessageServiceTest() - : credentials_provider_(std::make_shared("access_key", "access_secret")) { - } - - void SetUp() override { - grpc_init(); - consumer_ = std::make_shared>(); - ON_CALL(*consumer_, consumeBatchSize).WillByDefault(testing::Return(consume_batch_size_)); - ON_CALL(*consumer_, messageModel).WillByDefault(testing::Return(MessageModel::CLUSTERING)); - std::weak_ptr consumer = std::dynamic_pointer_cast(consumer_); - consume_standard_message_service_ = - std::make_shared(consumer, thread_count_, &message_listener_); - process_queue_ = std::make_shared>(); - ON_CALL(*process_queue_, topic).WillByDefault(testing::Return(topic_)); - auto mock_take = [this](uint32_t batch_size, std::vector& messages) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - message.setBody(body_); - messages.emplace_back(message); - return false; - }; - ON_CALL(*process_queue_, take).WillByDefault(testing::Invoke(mock_take)); - ON_CALL(*process_queue_, getConsumer).WillByDefault(testing::Return(consumer)); - ON_CALL(*consumer_, customExecutor).WillByDefault(testing::ReturnRef(executor_)); - ON_CALL(*consumer_, credentialsProvider).WillByDefault(testing::Return(credentials_provider_)); - ON_CALL(*consumer_, resourceNamespace).WillByDefault(testing::ReturnRef(resource_namespace_)); - ON_CALL(*consumer_, getGroupName).WillByDefault(testing::ReturnRef(group_name_)); - } - - void TearDown() override { - grpc_shutdown(); - } - -protected: - int thread_count_{2}; - std::string topic_{"TestTopic"}; - std::string tag_{"TagA"}; - std::string body_{"Body Content"}; - std::string resource_namespace_{"mq://test"}; - std::string group_name_{"CID_Test"}; - uint32_t consume_batch_size_; - std::shared_ptr> consumer_; - std::shared_ptr consume_standard_message_service_; - testing::NiceMock message_listener_; - std::shared_ptr> process_queue_; - std::shared_ptr credentials_provider_; - Executor executor_; -}; - -TEST_F(ConsumeStandardMessageServiceTest, testStartAndShutdown) { - consume_standard_message_service_->start(); - consume_standard_message_service_->shutdown(); -} - -TEST_F(ConsumeStandardMessageServiceTest, testConsume) { - ASSERT_FALSE(executor_); - consume_standard_message_service_->start(); - - auto callback = [this](const std::function& cb) { cb(process_queue_); }; - - ON_CALL(*consumer_, iterateProcessQueue).WillByDefault(testing::Invoke(callback)); - - bool completed = false; - bool success = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto listener_cb = [&](const std::vector& messages) { - absl::MutexLock lk(&mtx); - completed = true; - success = !messages.empty(); - cv.SignalAll(); - return ConsumeMessageResult::SUCCESS; - }; - - ON_CALL(message_listener_, consumeMessage).WillByDefault(testing::Invoke(listener_cb)); - EXPECT_CALL(*process_queue_, release).Times(testing::AtLeast(1)); - EXPECT_CALL(*consumer_, ack).Times(testing::AtLeast(1)); - - absl::MutexLock lk(&mtx); - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - - consume_standard_message_service_->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp b/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp deleted file mode 100644 index 4f8bb9d81..000000000 --- a/src/test/cpp/ut/rocketmq/CredentialsProviderTest.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/CredentialsProvider.h" -#include "MixAll.h" -#include "ghc/filesystem.hpp" -#include "google/protobuf/struct.pb.h" -#include "google/protobuf/util/json_util.h" -#include "gtest/gtest.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class CredentialsProviderTest : public testing::Test {}; - -TEST_F(CredentialsProviderTest, testStaticCredentialsProvider) { - std::string access_key("abc"); - std::string access_secret("def"); - StaticCredentialsProvider credentials_provider(access_key, access_secret); - Credentials&& credentials = credentials_provider.getCredentials(); - ASSERT_EQ(credentials.accessKey(), access_key); - ASSERT_EQ(credentials.accessSecret(), access_secret); -} - -TEST_F(CredentialsProviderTest, testEnvironmentVariable) { - const char* access_key = "abc"; - const char* access_secret = "def"; - -#ifdef _WIN32 - std::string env_access_key; - env_access_key.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY); - env_access_key.push_back('='); - env_access_key.append(access_key); - - _putenv(env_access_key.c_str()); - - std::string env_access_secret; - env_access_secret.append(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET); - env_access_secret.push_back('='); - env_access_secret.append(access_secret); - _putenv(env_access_secret.c_str()); -#else - setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_KEY, access_key, 1); - setenv(EnvironmentVariablesCredentialsProvider::ENVIRONMENT_ACCESS_SECRET, access_secret, 1); -#endif - - EnvironmentVariablesCredentialsProvider provider; - const Credentials& credentials = provider.getCredentials(); - EXPECT_STREQ(access_key, credentials.accessKey().c_str()); - EXPECT_STREQ(access_secret, credentials.accessSecret().c_str()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp b/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp deleted file mode 100644 index 47a17ae06..000000000 --- a/src/test/cpp/ut/rocketmq/DefaultMQProducerTest.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/DefaultMQProducer.h" - -#include -#include -#include -#include - -#include "MQClientTest.h" -#include "ProducerImpl.h" -#include "rocketmq/CredentialsProvider.h" -#include "rocketmq/MQSelector.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class DefaultMQProducerUnitTest : public MQClientTest { -public: - DefaultMQProducerUnitTest() : MQClientTest() { - credentials_provider_ = std::make_shared(access_key_, access_secret_); - } - - void SetUp() override { - MQClientTest::SetUp(); - // More set-up - rpc_client_broker_ = std::make_shared>(); - - ON_CALL(*rpc_client_broker_, needHeartbeat()).WillByDefault(testing::Return(true)); - - ON_CALL(*rpc_client_broker_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); - - ON_CALL(*rpc_client_broker_, asyncHealthCheck) - .WillByDefault(testing::Invoke( - [this](const HealthCheckRequest& request, InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->onCompletion(true); - })); - - ON_CALL(*rpc_client_broker_, asyncHeartbeat(testing::_, testing::_)) - .WillByDefault(testing::Invoke( - [this](const HeartbeatRequest& request, InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->onCompletion(true); - })); - - ON_CALL(*rpc_client_broker_, asyncSend) - .WillByDefault(testing::Invoke( - [this](const SendMessageRequest& request, InvocationContext* invocation_context) { - invocation_context->response.mutable_common()->mutable_status()->set_code(ok_); - invocation_context->response.set_message_id(message_id_); - invocation_context->onCompletion(true); - })); - - const char* address_format = "ipv4:10.0.0.{}:10911"; - for (int i = 0; i < partition_num_ / avg_partition_per_host_; ++i) { - std::string&& address = fmt::format(address_format, i); - client_instance_->addRpcClient(address, rpc_client_broker_); - } - } - - void TearDown() override { - rpc_client_broker_.reset(); - MQClientTest::TearDown(); - } - -protected: - absl::flat_hash_map metadata_; - std::shared_ptr> rpc_client_broker_; - std::string message_id_{"msg_id_0"}; - std::string access_key_{"access_key"}; - std::string access_secret_{"access_secret"}; - std::shared_ptr credentials_provider_; - std::string body_{"Test message body"}; -}; - -TEST_F(DefaultMQProducerUnitTest, testBasicSetUp) { - QueryRouteRequest request; - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - auto invocation_context = new InvocationContext(); - auto callback = [&](const InvocationContext* invocation_context) { - ASSERT_TRUE(invocation_context->status.ok()); - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - invocation_context->callback = callback; - rpc_client_ns_->asyncQueryRoute(request, invocation_context); - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } - - completed = false; - SendMessageRequest send_message_request; - auto send_message_invocation_context = new InvocationContext(); - auto send_callback = [&](const InvocationContext* invocation_context) { - ASSERT_TRUE(invocation_context->response.message_id() == message_id_); - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - send_message_invocation_context->callback = send_callback; - - rpc_client_broker_->asyncSend(send_message_request, send_message_invocation_context); - - while (!completed) { - absl::MutexLock lk(&mtx); - cv.Wait(&mtx); - } -} - -class UnitTestSendCallback : public SendCallback { -public: - UnitTestSendCallback(absl::Mutex& mtx, absl::CondVar& cv, std::string& msg_id, bool& completed) - : mtx_(mtx), cv_(cv), msg_id_(msg_id), completed_(completed) { - } - - void onSuccess(SendResult& send_result) noexcept override { - absl::MutexLock lk(&mtx_); - msg_id_ = send_result.getMsgId(); - completed_ = true; - cv_.SignalAll(); - } - - void onFailure(const std::error_code& e) noexcept override { - absl::MutexLock lk(&mtx_); - completed_ = true; - cv_.SignalAll(); - } - -private: - absl::Mutex& mtx_; - absl::CondVar& cv_; - std::string& msg_id_; - bool& completed_; -}; - -TEST_F(DefaultMQProducerUnitTest, testAsyncSendMessage) { - auto producer = std::make_shared(group_name_); - producer->resourceNamespace(resource_namespace_); - producer->withNameServerResolver(name_server_resolver_); - producer->setCredentialsProvider(credentials_provider_); - producer->start(); - - MQMessage message; - message.setTopic(topic_); - message.setBody(body_); - - absl::Mutex mtx; - absl::CondVar cv; - bool completed = false; - std::string msg_id; - auto send_callback = new UnitTestSendCallback(mtx, cv, msg_id, completed); - producer->send(message, send_callback); - if (!completed) { - absl::MutexLock lk(&mtx); - cv.WaitWithTimeout(&mtx, absl::Seconds(10)); - } - ASSERT_EQ(msg_id, message_id_); - producer->shutdown(); - delete send_callback; -} - -TEST_F(DefaultMQProducerUnitTest, testSendMessage) { - auto producer = std::make_shared(group_name_); - producer->resourceNamespace(resource_namespace_); - producer->withNameServerResolver(name_server_resolver_); - producer->setCredentialsProvider(credentials_provider_); - producer->start(); - - MQMessage message; - message.setTopic(topic_); - message.setBody(body_); - - std::error_code ec; - SendResult send_result = producer->send(message, ec); - EXPECT_FALSE(ec); - ASSERT_EQ(send_result.getMsgId(), message_id_); - producer->shutdown(); -} - -TEST_F(DefaultMQProducerUnitTest, testEndpointIsolation) { - auto producer = std::make_shared(group_name_); - producer->resourceNamespace(resource_namespace_); - producer->withNameServerResolver(name_server_resolver_); - producer->setCredentialsProvider(credentials_provider_); - producer->start(); - - const char* isolated_endpoint = "ipv4:10.0.0.0:10911"; - producer->isolateEndpoint(isolated_endpoint); - absl::SleepFor(absl::Seconds(10)); - ASSERT_FALSE(producer->isEndpointIsolated(isolated_endpoint)); - producer->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp deleted file mode 100644 index a693fd6aa..000000000 --- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "DynamicNameServerResolver.h" - -#include -#include -#include -#include - -#include "absl/memory/memory.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_replace.h" -#include "absl/strings/str_split.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "HttpClientMock.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class DynamicNameServerResolverTest : public testing::Test { -public: - DynamicNameServerResolverTest() - : resolver_(std::make_shared(endpoint_, std::chrono::seconds(1))) { - } - - void SetUp() override { - auto http_client = absl::make_unique>(); - - auto callback = - [this](HttpProtocol, const std::string&, std::uint16_t, const std::string&, - const std::function&, const std::string&)>& cb) { - int code = 200; - std::multimap headers; - cb(code, headers, name_server_list_); - }; - - ON_CALL(*http_client, get).WillByDefault(testing::Invoke(callback)); - - resolver_->injectHttpClient(std::move(http_client)); - - resolver_->start(); - } - - void TearDown() override { - resolver_->shutdown(); - } - -protected: - std::string endpoint_{"http://jmenv.tbsite.net:8080/rocketmq/nsaddr"}; - std::string name_server_list_{"10.0.0.0:9876;10.0.0.1:9876"}; - std::shared_ptr resolver_; -}; - -TEST_F(DynamicNameServerResolverTest, testResolve) { - auto name_server_list = resolver_->resolve(); - std::string result = absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); - ASSERT_EQ(name_server_list, "ipv4:" + result); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp b/src/test/cpp/ut/rocketmq/ExecutorTest.cpp deleted file mode 100644 index e637b415d..000000000 --- a/src/test/cpp/ut/rocketmq/ExecutorTest.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/RocketMQ.h" -#include "rocketmq/State.h" -#include "gtest/gtest.h" -#include -#include -#include -#include -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN -class ExecutorImpl { -public: - explicit ExecutorImpl(unsigned int core) : state_(State::CREATED), core_(core) { - if (core_ <= 0) { - core_ = std::thread::hardware_concurrency(); - } - } - - virtual ~ExecutorImpl() { - switch (state_.load(std::memory_order_relaxed)) { - case CREATED: - case STOPPING: - case STOPPED: - break; - - case STARTING: - case STARTED: - state_.store(State::STOPPED); - for (auto& worker : workers_) { - if (worker.joinable()) { - worker.join(); - } - } - break; - } - } - - void submit(const std::function& task) { - if (State::STOPPED == state_.load(std::memory_order_relaxed)) { - return; - } - - { - std::unique_lock lock(task_mtx_); - tasks_.push_back(task); - } - cv_.notify_one(); - } - - void start() { - State expected = State::CREATED; - if (state_.compare_exchange_strong(expected, State::STARTING)) { - for (unsigned int i = 0; i < core_; i++) { - workers_.emplace_back(std::bind(&ExecutorImpl::loop, this)); - } - state_.store(State::STARTED); - } - } - - void stop() { - state_.store(State::STOPPED); - for (auto& worker : workers_) { - if (worker.joinable()) { - worker.join(); - } - } - } - -private: - void loop() { - while (state_.load(std::memory_order_relaxed) != State::STOPPED) { - std::function func; - { - std::unique_lock lk(task_mtx_); - if (!tasks_.empty()) { - func = tasks_.back(); - } - } - - if (func) { - func(); - } else { - std::unique_lock lk(task_mtx_); - cv_.wait_for(lk, std::chrono::seconds(3), - [&]() { return state_.load(std::memory_order_relaxed) == State::STOPPED || !tasks_.empty(); }); - } - } - - std::stringstream ss; - ss << "ThreadId=" << std::this_thread::get_id() << " quit."; - printf("%s\n", ss.str().c_str()); - } - - std::atomic state_; - std::vector> tasks_; - std::mutex task_mtx_; - std::condition_variable cv_; - unsigned int core_; - std::vector workers_; -}; - -class ExecutorTest : public ::testing::Test { -public: - void SetUp() override { - executor_.start(); - } - - void TearDown() override { - executor_.stop(); - } - -protected: - ExecutorImpl executor_{0}; -}; - -TEST_F(ExecutorTest, testBasic) { -} - -std::mutex submit_mtx_; -std::condition_variable submit_cv_; -std::atomic_bool submit_status(false); - -void f(int n) { - std::this_thread::sleep_for(std::chrono::milliseconds(n)); - submit_status.store(true, std::memory_order_relaxed); - submit_cv_.notify_all(); -} - -TEST_F(ExecutorTest, testSubmit) { - auto task = std::bind(f, 100); - executor_.submit(task); - { - std::unique_lock lock(submit_mtx_); - submit_cv_.wait(lock, [] { return submit_status.load(std::memory_order_relaxed); }); - } - EXPECT_EQ(true, submit_status.load()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp b/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp deleted file mode 100644 index 1f51c70c9..000000000 --- a/src/test/cpp/ut/rocketmq/MessageGroupQueueSelectorTest.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "MessageGroupQueueSelector.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQMessageQueue.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -TEST(MessageGroupQueueSelectorTest, testSelect) { - std::string message_group("sample"); - std::size_t hash_code = std::hash{}(message_group); - MessageGroupQueueSelector selector(message_group); - std::size_t len = 8; - std::vector mqs; - mqs.resize(len); - for (std::size_t i = 0; i < len; i++) { - MQMessageQueue queue("topic", "broker-a", static_cast(i)); - mqs.emplace_back(queue); - } - - MQMessage message; - MQMessageQueue selected = selector.select(mqs, message, nullptr); - EXPECT_EQ(selected, mqs[hash_code % len]); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/MessageTest.cpp b/src/test/cpp/ut/rocketmq/MessageTest.cpp deleted file mode 100644 index 77267d6ad..000000000 --- a/src/test/cpp/ut/rocketmq/MessageTest.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "rocketmq/MQMessageExt.h" -#include -#include -#include - -TEST(MessageTest, testExpire) { - auto expire_time_point = std::chrono::steady_clock::now() + std::chrono::milliseconds(300); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT_TRUE(std::chrono::steady_clock::now() < expire_time_point); -} \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp deleted file mode 100644 index 336f702b2..000000000 --- a/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NamingScheme.h" -#include "rocketmq/RocketMQ.h" - -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class NamingSchemeTest : public testing::Test { -public: - void SetUp() override { - } - - void TearDown() override { - } - -protected: - NamingScheme naming_scheme_; -}; - -TEST_F(NamingSchemeTest, testBuildAddress) { - std::string address = "www.baidu.com:80"; - std::string result = naming_scheme_.buildAddress({address}); - ASSERT_EQ("dns:www.baidu.com:80", result); - - address = "8.8.8.8:1234"; - result = naming_scheme_.buildAddress({address}); - ASSERT_EQ("ipv4:8.8.8.8:1234", result); - - result = naming_scheme_.buildAddress({address, "4.4.4.4:1234"}); - ASSERT_EQ("ipv4:8.8.8.8:1234,4.4.4.4:1234", result); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp b/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp deleted file mode 100644 index bfa7d5177..000000000 --- a/src/test/cpp/ut/rocketmq/ProcessQueueTest.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" - -#include "Assignment.h" -#include "ClientManagerMock.h" -#include "ConsumeMessageServiceMock.h" -#include "ConsumeMessageType.h" -#include "InvocationContext.h" -#include "MessageAccessor.h" -#include "ProcessQueueImpl.h" -#include "PushConsumerMock.h" -#include "ReceiveMessageCallbackMock.h" -#include "ReceiveMessageResult.h" -#include "RpcClientMock.h" -#include "rocketmq/CredentialsProvider.h" -#include "rocketmq/MQMessageExt.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ProcessQueueTest : public testing::Test { -public: - void SetUp() override { - rpc_client_ = std::make_shared>(); - message_queue_.serviceAddress(service_address_); - message_queue_.setTopic(topic_); - message_queue_.setBrokerName(broker_name_); - message_queue_.setQueueId(queue_id_); - client_manager_ = std::make_shared>(); - credentials_provider_ = std::make_shared(access_key_, access_secret_); - consumer_ = std::make_shared>(); - auto consumer = std::dynamic_pointer_cast(consumer_); - - consume_message_service_ = std::make_shared>(); - ON_CALL(*consume_message_service_, messageListenerType) - .WillByDefault(testing::Return(MessageListenerType::STANDARD)); - - ON_CALL(*consumer_, getConsumeMessageService).WillByDefault(testing::Return(consume_message_service_)); - - process_queue_ = std::make_shared(message_queue_, filter_expression_, consumer, client_manager_); - receive_message_callback_ = std::make_shared>(); - process_queue_->callback(receive_message_callback_); - } - - void TearDown() override { - } - -protected: - std::string tenant_id_{"tenant-0"}; - std::string access_key_{"ak"}; - std::string access_secret_{"secret"}; - std::shared_ptr credentials_provider_; - std::string group_name_{"TestGroup"}; - std::string client_id_{"Client-0"}; - std::string broker_name_{"broker-a"}; - std::string region_{"cn-hangzhou"}; - std::string service_name_{"MQ"}; - int queue_id_{0}; - std::string topic_{"TestTopic"}; - std::string service_address_{"ipv4:10.0.0.1:10911"}; - std::string tag_{"TagA"}; - FilterExpression filter_expression_{tag_}; - MQMessageQueue message_queue_; - std::shared_ptr> rpc_client_; - std::shared_ptr> client_manager_; - std::shared_ptr> consumer_; - std::shared_ptr> consume_message_service_; - std::shared_ptr process_queue_; - std::shared_ptr> receive_message_callback_; - std::string resource_namespace_{"mq://test"}; - std::string message_body_{"Sample body"}; - - uint32_t threshold_quantity_{32}; - uint64_t threshold_memory_{4096}; - uint32_t consume_batch_size_{8}; -}; - -TEST_F(ProcessQueueTest, testBind) { - EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); - EXPECT_FALSE(process_queue_->bindFifoConsumeTask()); - EXPECT_TRUE(process_queue_->unbindFifoConsumeTask()); - EXPECT_FALSE(process_queue_->unbindFifoConsumeTask()); - EXPECT_TRUE(process_queue_->bindFifoConsumeTask()); -} - -TEST_F(ProcessQueueTest, testExpired) { - EXPECT_FALSE(process_queue_->expired()); - process_queue_->idle_since_ -= MixAll::PROCESS_QUEUE_EXPIRATION_THRESHOLD_; - EXPECT_TRUE(process_queue_->expired()); -} - -TEST_F(ProcessQueueTest, testShouldThrottle) { - EXPECT_CALL(*consumer_, maxCachedMessageQuantity) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_quantity_)); - EXPECT_CALL(*consumer_, maxCachedMessageMemory) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_memory_)); - EXPECT_FALSE(process_queue_->shouldThrottle()); -} - -TEST_F(ProcessQueueTest, testShouldThrottle_ByQuantity) { - std::vector messages; - for (uint32_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::to_string(i)); - messages.emplace_back(message); - } - - process_queue_->cacheMessages(messages); - - EXPECT_CALL(*consumer_, maxCachedMessageQuantity) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_quantity_)); - EXPECT_CALL(*consumer_, maxCachedMessageMemory) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_memory_)); - EXPECT_TRUE(process_queue_->shouldThrottle()); -} - -TEST_F(ProcessQueueTest, testShouldThrottle_ByMemory) { - std::vector messages; - size_t body_length = 1024 * 4; - for (uint32_t i = 0; i < threshold_quantity_ / 2; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::string(body_length, 'c')); - messages.emplace_back(message); - } - - process_queue_->cacheMessages(messages); - - EXPECT_CALL(*consumer_, maxCachedMessageQuantity) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_quantity_)); - EXPECT_CALL(*consumer_, maxCachedMessageMemory) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(threshold_memory_)); - EXPECT_TRUE(process_queue_->shouldThrottle()); -} - -TEST_F(ProcessQueueTest, testHasPendingMessages) { - EXPECT_FALSE(process_queue_->hasPendingMessages()); -} - -TEST_F(ProcessQueueTest, testHasPendingMessages2) { - std::vector messages; - size_t body_length = 1024; - for (size_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::string(body_length, 'c')); - messages.emplace_back(message); - } - process_queue_->cacheMessages(messages); - EXPECT_TRUE(process_queue_->hasPendingMessages()); -} - -TEST_F(ProcessQueueTest, testTake) { - std::vector messages; - EXPECT_FALSE(process_queue_->take(consume_batch_size_, messages)); - EXPECT_TRUE(messages.empty()); -} - -TEST_F(ProcessQueueTest, testTake2) { - - { - std::vector messages; - size_t body_length = 1024; - for (size_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::string(body_length, 'c')); - messages.emplace_back(message); - } - process_queue_->cacheMessages(messages); - EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); - } - - std::vector msgs; - EXPECT_TRUE(process_queue_->take(consume_batch_size_, msgs)); - EXPECT_FALSE(msgs.empty()); - EXPECT_EQ(tag_, msgs.begin()->getTags()); - EXPECT_EQ(topic_, msgs.begin()->getTopic()); - EXPECT_EQ(threshold_quantity_ - consume_batch_size_, process_queue_->cachedMessagesSize()); -} - -TEST_F(ProcessQueueTest, testRelease) { - EXPECT_CALL(*consumer_, messageModel) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); - - int64_t offset; - EXPECT_FALSE(process_queue_->committedOffset(offset)); - - size_t body_length = 1024; - { - std::vector messages; - for (size_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::string(body_length, 'c')); - messages.emplace_back(message); - } - process_queue_->cacheMessages(messages); - EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); - } - - std::vector msgs; - process_queue_->take(1, msgs); - - EXPECT_TRUE(process_queue_->committedOffset(offset)); - EXPECT_EQ(0, offset); - - process_queue_->release(body_length, 0); - - EXPECT_TRUE(process_queue_->committedOffset(offset)); - EXPECT_EQ(1, offset); -} - -TEST_F(ProcessQueueTest, testOffset) { - EXPECT_CALL(*consumer_, messageModel) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Return(MessageModel::BROADCASTING)); - - int64_t offset; - EXPECT_FALSE(process_queue_->committedOffset(offset)); - - size_t body_length = 1024; - { - std::vector messages; - for (size_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - MessageAccessor::setQueueId(message, 0); - MessageAccessor::setQueueOffset(message, i); - message.setBody(std::string(body_length, 'c')); - messages.emplace_back(message); - } - process_queue_->cacheMessages(messages); - EXPECT_EQ(threshold_quantity_, process_queue_->cachedMessagesSize()); - } - - std::vector msgs; - process_queue_->take(threshold_quantity_, msgs); - - EXPECT_TRUE(process_queue_->committedOffset(offset)); - EXPECT_EQ(0, offset); - - for (size_t i = 0; i < threshold_quantity_; i++) { - process_queue_->release(body_length, i); - } - - EXPECT_TRUE(process_queue_->committedOffset(offset)); - EXPECT_EQ(threshold_quantity_, offset); -} - -TEST_F(ProcessQueueTest, testReceiveMessage_POP) { - EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); - EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); - EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); - EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); - EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); - EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); - EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); - EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - - auto optional = absl::make_optional(filter_expression_); - - EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); - EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); - EXPECT_CALL(*consumer_, messageModel).WillRepeatedly(testing::Return(MessageModel::CLUSTERING)); - - auto receive_message_mock = [this](const std::string& target, const Metadata& metadata, - const ReceiveMessageRequest& request, std::chrono::milliseconds timeout, - const std::shared_ptr& cb) { - std::error_code ec; - ReceiveMessageResult result; - for (size_t i = 0; i < threshold_quantity_; i++) { - MQMessageExt message; - message.setTopic(topic_); - message.setTags(tag_); - message.setBody(message_body_); - MessageAccessor::setQueueId(message, queue_id_); - MessageAccessor::setQueueOffset(message, i); - result.messages.emplace_back(message); - } - cb->onCompletion(ec, result); - }; - - EXPECT_CALL(*client_manager_, receiveMessage) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(receive_message_mock)); - process_queue_->receiveMessage(); -} - -TEST_F(ProcessQueueTest, testReceiveMessage_Pull) { - EXPECT_CALL(*consumer_, tenantId).WillRepeatedly(testing::ReturnRef(tenant_id_)); - EXPECT_CALL(*consumer_, resourceNamespace).WillRepeatedly(testing::ReturnRef(resource_namespace_)); - EXPECT_CALL(*consumer_, credentialsProvider).WillRepeatedly(testing::Return(credentials_provider_)); - EXPECT_CALL(*consumer_, region).WillRepeatedly(testing::ReturnRef(region_)); - EXPECT_CALL(*consumer_, serviceName).WillRepeatedly(testing::ReturnRef(service_name_)); - EXPECT_CALL(*consumer_, clientId).WillRepeatedly(testing::Return(client_id_)); - EXPECT_CALL(*consumer_, getGroupName).WillRepeatedly(testing::ReturnRef(group_name_)); - EXPECT_CALL(*consumer_, getLongPollingTimeout).WillRepeatedly(testing::Return(absl::Seconds(3))); - - auto optional = absl::make_optional(filter_expression_); - - EXPECT_CALL(*consumer_, getFilterExpression).WillRepeatedly(testing::Return(optional)); - EXPECT_CALL(*consumer_, receiveBatchSize).WillRepeatedly(testing::Return(threshold_quantity_)); - - std::error_code ec; - ReceiveMessageResult result; - - auto pull_message_mock = [&](const std::string& target_host, const Metadata& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(ec, result); - }; - - EXPECT_CALL(*client_manager_, pullMessage) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(pull_message_mock)); - process_queue_->receiveMessage(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp b/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp deleted file mode 100644 index 5b990524d..000000000 --- a/src/test/cpp/ut/rocketmq/ProducerImplTest.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include "ClientManagerFactory.h" -#include "ClientManagerMock.h" -#include "ProducerImpl.h" -#include "Scheduler.h" -#include "SchedulerImpl.h" -#include "StaticNameServerResolver.h" -#include "TopicRouteData.h" -#include "rocketmq/AsyncCallback.h" -#include "rocketmq/MQMessage.h" -#include "rocketmq/MQSelector.h" -#include "rocketmq/RocketMQ.h" -#include "rocketmq/SendResult.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ProducerImplTest : public testing::Test { -public: - ProducerImplTest() : credentials_provider_(std::make_shared(access_key_, access_secret_)) { - } - - void SetUp() override { - grpc_init(); - scheduler_ = std::make_shared(); - scheduler_->start(); - name_server_resolver_ = std::make_shared(name_server_list_); - client_manager_ = std::make_shared>(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); - ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); - producer_ = std::make_shared(group_); - producer_->resourceNamespace(resource_namespace_); - producer_->withNameServerResolver(name_server_resolver_); - producer_->setCredentialsProvider(credentials_provider_); - - { - std::vector partitions; - Topic topic(resource_namespace_, topic_); - std::vector
broker_addresses{Address(broker_host_, broker_port_)}; - ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); - Broker broker(broker_name_, broker_id_, service_address); - Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); - partitions.emplace_back(partition); - std::string debug_string; - topic_route_data_ = std::make_shared(partitions, debug_string); - } - } - - void TearDown() override { - scheduler_->shutdown(); - grpc_shutdown(); - } - -protected: - SchedulerSharedPtr scheduler_; - std::shared_ptr> client_manager_; - std::shared_ptr producer_; - std::string name_server_list_{"10.0.0.1:9876"}; - std::shared_ptr name_server_resolver_; - std::string resource_namespace_{"mq://test"}; - std::string group_{"CID_test"}; - std::string topic_{"Topic0"}; - int queue_id_{1}; - std::string tag_{"TagA"}; - std::string key_{"key-0"}; - std::string message_group_{"group-0"}; - std::string broker_name_{"broker-a"}; - int broker_id_{0}; - std::string message_body_{"Message Body Content"}; - std::string broker_host_{"10.0.0.1"}; - int broker_port_{10911}; - TopicRouteDataPtr topic_route_data_; - std::string access_key_{"access_key"}; - std::string access_secret_{"access_secret"}; - std::shared_ptr credentials_provider_; -}; - -TEST_F(ProducerImplTest, testStartShutdown) { - producer_->start(); - producer_->shutdown(); -} - -TEST_F(ProducerImplTest, testSend) { - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - bool cb_invoked = false; - SendResult send_result; - auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) { - cb->onSuccess(send_result); - cb_invoked = true; - return true; - }; - - EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); - producer_->start(); - - MQMessage message(topic_, tag_, message_body_); - std::error_code ec; - producer_->send(message, ec); - EXPECT_FALSE(ec); - EXPECT_TRUE(cb_invoked); - producer_->shutdown(); -} - -TEST_F(ProducerImplTest, testSend_WithMessageGroup) { - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - bool cb_invoked = false; - SendResult send_result; - auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) { - cb->onSuccess(send_result); - cb_invoked = true; - return true; - }; - - EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); - producer_->start(); - - MQMessage message(topic_, tag_, message_body_); - message.bindMessageGroup(message_group_); - std::error_code ec; - producer_->send(message, ec); - EXPECT_FALSE(ec); - EXPECT_TRUE(cb_invoked); - producer_->shutdown(); -} - -TEST_F(ProducerImplTest, testSend_WithMessageQueueSelector) { - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - bool cb_invoked = false; - SendResult send_result; - auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) { - cb->onSuccess(send_result); - cb_invoked = true; - return true; - }; - - EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); - producer_->start(); - - MQMessage message(topic_, tag_, message_body_); - - std::error_code ec; - auto&& list = producer_->listMessageQueue(topic_, ec); - - EXPECT_FALSE(list.empty()); - - message.bindMessageQueue(list[0]); - - producer_->send(message, ec); - - EXPECT_TRUE(cb_invoked); - producer_->shutdown(); -} - -class TestSendCallback : public SendCallback { -public: - TestSendCallback(bool& completed, absl::Mutex& mtx, absl::CondVar& cv) : completed_(completed), mtx_(mtx), cv_(cv) { - } - void onSuccess(SendResult& send_result) noexcept override { - absl::MutexLock lk(&mtx_); - completed_ = true; - cv_.SignalAll(); - } - - void onFailure(const std::error_code& ec) noexcept override { - absl::MutexLock lk(&mtx_); - completed_ = true; - cv_.SignalAll(); - } - -protected: - bool& completed_; - absl::Mutex& mtx_; - absl::CondVar& cv_; -}; - -TEST_F(ProducerImplTest, testAsyncSend) { - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - bool cb_invoked = false; - SendResult send_result; - auto mock_send = [&](const std::string& target_host, const Metadata& metadata, SendMessageRequest& request, - SendCallback* cb) { - cb->onSuccess(send_result); - cb_invoked = true; - return true; - }; - - EXPECT_CALL(*client_manager_, send).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(mock_send)); - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - - auto send_callback = absl::make_unique(completed, mtx, cv); - - producer_->start(); - - MQMessage message(topic_, tag_, message_body_); - producer_->send(message, send_callback.get()); - - if (!completed) { - absl::MutexLock lk(&mtx); - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - - EXPECT_TRUE(cb_invoked); - producer_->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp deleted file mode 100644 index 729aac0ee..000000000 --- a/src/test/cpp/ut/rocketmq/PullConsumerImplTest.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "ClientManagerFactory.h" -#include "ClientManagerMock.h" -#include "InvocationContext.h" -#include "PullConsumerImpl.h" -#include "Scheduler.h" -#include "StaticNameServerResolver.h" -#include "apache/rocketmq/v1/definition.pb.h" -#include "rocketmq/AsyncCallback.h" -#include "rocketmq/ConsumeType.h" -#include "rocketmq/ErrorCode.h" -#include "rocketmq/MQMessageExt.h" -#include "rocketmq/RocketMQ.h" - -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PullConsumerImplTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - scheduler_ = std::make_shared(); - name_server_resolver_ = std::make_shared(name_server_list_); - - scheduler_->start(); - client_manager_ = std::make_shared>(); - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); - ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); - - pull_consumer_ = std::make_shared(group_); - pull_consumer_->withNameServerResolver(name_server_resolver_); - pull_consumer_->resourceNamespace(resource_namespace_); - - { - std::vector partitions; - Topic topic(resource_namespace_, topic_); - std::vector
broker_addresses{Address(broker_host_, broker_port_)}; - ServiceAddress service_address(AddressScheme::IPv4, broker_addresses); - Broker broker(broker_name_, broker_id_, service_address); - Partition partition(topic, queue_id_, Permission::READ_WRITE, broker); - partitions.emplace_back(partition); - std::string debug_string; - topic_route_data_ = std::make_shared(partitions, debug_string); - } - } - - void TearDown() override { - grpc_shutdown(); - scheduler_->shutdown(); - } - -protected: - std::string resource_namespace_{"mq://test"}; - std::string name_server_list_{"10.0.0.1:9876"}; - std::shared_ptr name_server_resolver_; - std::string group_{"Group-0"}; - std::string topic_{"Test"}; - std::string tag_{"TagB"}; - std::shared_ptr> client_manager_; - std::shared_ptr pull_consumer_; - SchedulerSharedPtr scheduler_; - std::string broker_name_{"broker-a"}; - int broker_id_{0}; - std::string message_body_{"Message Body Content"}; - std::string broker_host_{"10.0.0.1"}; - int broker_port_{10911}; - int queue_id_{1}; - TopicRouteDataPtr topic_route_data_; - int batch_size_{32}; -}; - -TEST_F(PullConsumerImplTest, testStartShutdown) { - pull_consumer_->start(); - pull_consumer_->shutdown(); -} - -TEST_F(PullConsumerImplTest, testQueuesFor) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - pull_consumer_->queuesFor(topic_); - - pull_consumer_->shutdown(); -} - -class TestPullCallback : public PullCallback { -public: - TestPullCallback(bool& success, bool& failure) : success_(success), failure_(failure) { - } - void onSuccess(const PullResult& pull_result) noexcept override { - success_ = true; - failure_ = false; - } - - void onFailure(const std::error_code& e) noexcept override { - failure_ = true; - success_ = false; - } - -private: - bool& success_; - bool& failure_; -}; - -TEST_F(PullConsumerImplTest, testPull) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::error_code ec; - ReceiveMessageResult result; - - for (int i = 0; i < batch_size_; i++) { - MQMessageExt message; - message.setBody(message_body_); - message.setTopic(topic_); - message.setTags(tag_); - result.messages.emplace_back(message); - } - - auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - cb(ec, result); - }; - - EXPECT_CALL(*client_manager_, pullMessage) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_pull_message)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - PullMessageQuery query; - query.message_queue = *queues.begin(); - query.offset = 0; - query.await_time = std::chrono::seconds(3); - - bool success = false; - bool failure = false; - auto pull_callback = new TestPullCallback(success, failure); - pull_consumer_->pull(query, pull_callback); - - EXPECT_TRUE(success); - EXPECT_FALSE(failure); - - pull_consumer_->shutdown(); - delete pull_callback; -} - -TEST_F(PullConsumerImplTest, testPull_gRPC_error) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::error_code ec; - ReceiveMessageResult result; - auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - ec = ErrorCode::BadRequest; - cb(ec, result); - }; - - EXPECT_CALL(*client_manager_, pullMessage) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_pull_message)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - PullMessageQuery query; - query.message_queue = *queues.begin(); - query.offset = 0; - query.await_time = std::chrono::seconds(3); - - bool success = false; - bool failure = false; - auto pull_callback = new TestPullCallback(success, failure); - pull_consumer_->pull(query, pull_callback); - - EXPECT_TRUE(failure); - EXPECT_FALSE(success); - - pull_consumer_->shutdown(); - delete pull_callback; -} - -TEST_F(PullConsumerImplTest, testPull_biz_error) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::error_code ec; - ReceiveMessageResult result; - - auto mock_pull_message = [&](const std::string& target_host, const Metadata& metadata, - const PullMessageRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - ec = ErrorCode::BadRequest; - cb(ec, result); - }; - - EXPECT_CALL(*client_manager_, pullMessage) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_pull_message)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - PullMessageQuery query; - query.message_queue = *queues.begin(); - query.offset = 0; - query.await_time = std::chrono::seconds(3); - - bool success = false; - bool failure = false; - auto pull_callback = new TestPullCallback(success, failure); - pull_consumer_->pull(query, pull_callback); - - EXPECT_FALSE(success); - EXPECT_TRUE(failure); - - pull_consumer_->shutdown(); - delete pull_callback; -} - -TEST_F(PullConsumerImplTest, testQueryOffset) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - QueryOffsetResponse response; - int64_t offset = 1; - response.set_offset(offset); - - auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, - const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, response); - }; - - EXPECT_CALL(*client_manager_, queryOffset) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_query_offset)); - - OffsetQuery query; - query.policy = QueryOffsetPolicy::BEGINNING; - query.message_queue = *queues.begin(); - - std::future offset_future = pull_consumer_->queryOffset(query); - EXPECT_EQ(offset, offset_future.get()); - - pull_consumer_->shutdown(); -} - -TEST_F(PullConsumerImplTest, testQueryOffset_End) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - QueryOffsetResponse response; - int64_t offset = 1; - response.set_offset(offset); - - auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, - const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, response); - }; - - EXPECT_CALL(*client_manager_, queryOffset) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_query_offset)); - - OffsetQuery query; - query.policy = QueryOffsetPolicy::END; - query.message_queue = *queues.begin(); - - std::future offset_future = pull_consumer_->queryOffset(query); - EXPECT_EQ(offset, offset_future.get()); - - pull_consumer_->shutdown(); -} - -TEST_F(PullConsumerImplTest, testQueryOffset_Timepoint) { - pull_consumer_->start(); - auto mock_resolve_route = - [this](const std::string& target_host, const Metadata& metadata, const QueryRouteRequest& request, - std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, topic_route_data_); - }; - - EXPECT_CALL(*client_manager_, resolveRoute) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_resolve_route)); - - std::future> future = pull_consumer_->queuesFor(topic_); - auto queues = future.get(); - EXPECT_FALSE(queues.empty()); - - QueryOffsetResponse response; - int64_t offset = 1; - response.set_offset(offset); - - auto mock_query_offset = [&](const std::string& target_host, const Metadata& metadata, - const QueryOffsetRequest& request, std::chrono::milliseconds timeout, - const std::function& cb) { - std::error_code ec; - cb(ec, response); - }; - - EXPECT_CALL(*client_manager_, queryOffset) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(mock_query_offset)); - - OffsetQuery query; - query.policy = QueryOffsetPolicy::TIME_POINT; - query.time_point = std::chrono::system_clock::now(); - query.message_queue = *queues.begin(); - - std::future offset_future = pull_consumer_->queryOffset(query); - EXPECT_EQ(offset, offset_future.get()); - - pull_consumer_->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp b/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp deleted file mode 100644 index b84dbea60..000000000 --- a/src/test/cpp/ut/rocketmq/PushConsumerImplTest.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "gtest/gtest.h" -#include - -#include "ClientManagerFactory.h" -#include "ClientManagerMock.h" -#include "InvocationContext.h" -#include "MessageAccessor.h" -#include "PushConsumerImpl.h" -#include "Scheduler.h" -#include "StaticNameServerResolver.h" -#include "grpc/grpc.h" -#include "rocketmq/MQMessageExt.h" -#include "rocketmq/MessageListener.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class TestStandardMessageListener : public StandardMessageListener { -public: - ConsumeMessageResult consumeMessage(const std::vector& msgs) override { - return ConsumeMessageResult::SUCCESS; - } -}; - -class PushConsumerImplTest : public testing::Test { -public: - PushConsumerImplTest() : message_listener_(absl::make_unique()) { - } - - void SetUp() override { - grpc_init(); - scheduler_ = std::make_shared(); - name_server_resolver_ = std::make_shared(name_server_list_); - client_manager_ = std::make_shared>(); - ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_manager_); - push_consumer_ = std::make_shared(group_); - push_consumer_->resourceNamespace(resource_namespace_); - push_consumer_->withNameServerResolver(name_server_resolver_); - push_consumer_->registerMessageListener(message_listener_.get()); - scheduler_->start(); - - ON_CALL(*client_manager_, getScheduler).WillByDefault(testing::Return(scheduler_)); - } - - void TearDown() override { - scheduler_->shutdown(); - grpc_shutdown(); - } - -protected: - SchedulerSharedPtr scheduler_; - std::string name_server_list_{"10.0.0.1:9876"}; - std::shared_ptr name_server_resolver_; - std::string resource_namespace_{"mq://test"}; - std::string group_{"CID_test"}; - std::string topic_{"Topic0"}; - std::string tag_{"TagA"}; - std::string key_{"key-0"}; - std::string message_body_{"Message Body Content"}; - int delay_level_{1}; - std::shared_ptr> client_manager_; - std::unique_ptr message_listener_; - std::shared_ptr push_consumer_; - const std::string target_endpoint_{"localhost:10911"}; -}; - -TEST_F(PushConsumerImplTest, testAck) { - auto ack_cb = [](const std::string& target_host, const Metadata& metadata, const AckMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) { - std::error_code ec; - cb(ec); - }; - - EXPECT_CALL(*client_manager_, ack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(ack_cb)); - - push_consumer_->start(); - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - auto callback = [&](const std::error_code& ec) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - - MQMessageExt message; - message.setTopic(topic_); - message.setBody(message_body_); - message.setTags(tag_); - message.setKey(key_); - message.setDelayTimeLevel(delay_level_); - MessageAccessor::setTargetEndpoint(message, target_endpoint_); - - push_consumer_->ack(message, callback); - - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - push_consumer_->shutdown(); -} - -TEST_F(PushConsumerImplTest, testNack) { - auto nack_cb = [](const std::string& target_host, const Metadata& metadata, const NackMessageRequest& request, - std::chrono::milliseconds timeout, const std::function& cb) { - std::error_code ec; - cb(ec); - }; - - EXPECT_CALL(*client_manager_, nack).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(nack_cb)); - - push_consumer_->start(); - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - auto callback = [&](const std::error_code& ec) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - - MQMessageExt message; - message.setTopic(topic_); - message.setBody(message_body_); - message.setTags(tag_); - message.setKey(key_); - message.setDelayTimeLevel(delay_level_); - MessageAccessor::setTargetEndpoint(message, target_endpoint_); - push_consumer_->nack(message, callback); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - push_consumer_->shutdown(); -} - -TEST_F(PushConsumerImplTest, testForward) { - InvocationContext invocation_context; - - auto forward_cb = - [&](const std::string& target_host, const Metadata& metadata, - const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) { - cb(&invocation_context); - }; - - EXPECT_CALL(*client_manager_, forwardMessageToDeadLetterQueue) - .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::Invoke(forward_cb)); - - push_consumer_->start(); - - bool completed = false; - absl::Mutex mtx; - absl::CondVar cv; - auto callback = [&](bool ok) { - absl::MutexLock lk(&mtx); - completed = true; - cv.SignalAll(); - }; - - MQMessageExt message; - message.setTopic(topic_); - message.setBody(message_body_); - message.setTags(tag_); - message.setKey(key_); - message.setDelayTimeLevel(delay_level_); - - push_consumer_->forwardToDeadLetterQueue(message, callback); - { - absl::MutexLock lk(&mtx); - if (!completed) { - cv.WaitWithDeadline(&mtx, absl::Now() + absl::Seconds(3)); - } - } - EXPECT_TRUE(completed); - push_consumer_->shutdown(); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp b/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp deleted file mode 100644 index da037d99a..000000000 --- a/src/test/cpp/ut/rocketmq/SendCallbacksTest.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "SendCallbacks.h" -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class SendCallbacksTest : public testing::Test {}; - -TEST_F(SendCallbacksTest, testAwait) { - std::string msg_id("Msg-0"); - AwaitSendCallback callback; - std::thread waker([&]() { - std::this_thread::sleep_for(std::chrono::seconds(3)); - SendResult send_result; - send_result.setMsgId(msg_id); - callback.onSuccess(send_result); - }); - - callback.await(); - EXPECT_TRUE(callback); - EXPECT_EQ(callback.sendResult().getMsgId(), msg_id); - if (waker.joinable()) { - waker.join(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp deleted file mode 100644 index 0bca7e429..000000000 --- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "StaticNameServerResolver.h" - -#include "absl/strings/str_replace.h" -#include "absl/strings/str_split.h" - -#include "gtest/gtest.h" -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class StaticNameServerResolverTest : public testing::Test { -public: - StaticNameServerResolverTest() : resolver_(name_server_list_) { - } - - void SetUp() override { - resolver_.start(); - } - - void TearDown() override { - resolver_.shutdown(); - } - -protected: - std::string name_server_list_{"10.0.0.1:9876;10.0.0.2:9876"}; - StaticNameServerResolver resolver_; -}; - -TEST_F(StaticNameServerResolverTest, testResolve) { - std::string result = - "ipv4:" + absl::StrReplaceAll(name_server_list_, {std::pair(";", ",")}); - ASSERT_EQ(result, resolver_.resolve()); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp b/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp deleted file mode 100644 index 9cdec60fd..000000000 --- a/src/test/cpp/ut/rocketmq/StsCredentialsProviderImplTest.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "StsCredentialsProviderImpl.h" -#include "HttpClientMock.h" -#include "absl/memory/memory.h" -#include "grpc/grpc.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class StsCredentialsProviderImplTest : public testing::Test { -public: - void SetUp() override { - grpc_init(); - sts_credentials_provider = std::make_shared("test"); - auto http_client_ = absl::make_unique>(); - auto http_get_action = - [](HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path, - const std::function&, const std::string&)>& cb) { - std::multimap header; - std::string body = R"( - { - "AccessKeyId": "key", - "AccessKeySecret": "secret", - "SecurityToken": "token", - "Expiration" : "2017-11-01T05:20:01Z", - "LastUpdated" : "2017-10-31T23:20:01Z", - "Code" : "Success" - } - )"; - cb(200, header, body); - }; - - EXPECT_CALL(*http_client_, get).Times(testing::AtLeast(1)).WillRepeatedly(testing::Invoke(http_get_action)); - sts_credentials_provider->withHttpClient(std::move(http_client_)); - } - - void TearDown() override { - grpc_shutdown(); - } - -protected: - std::shared_ptr sts_credentials_provider; -}; - -TEST_F(StsCredentialsProviderImplTest, testGetCredentials) { - auto credentials = sts_credentials_provider->getCredentials(); - EXPECT_EQ(credentials.accessKey(), "key"); - EXPECT_EQ(credentials.accessSecret(), "secret"); - absl::Time time; - std::string input = "2017-11-01 05:20:01"; - std::string format = "%Y-%m-%d %H:%H:%S"; - std::string error; - EXPECT_TRUE(absl::ParseTime(format, input, &time, &error)); - EXPECT_EQ(credentials.expirationInstant(), absl::ToChronoTime(time)); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/rocketmq/include/MQClientTest.h b/src/test/cpp/ut/rocketmq/include/MQClientTest.h deleted file mode 100644 index 3e84ef2b0..000000000 --- a/src/test/cpp/ut/rocketmq/include/MQClientTest.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include "google/rpc/code.pb.h" -#include "grpc/grpc.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "ClientManagerFactory.h" -#include "ClientManagerImpl.h" -#include "RpcClientMock.h" -#include "StaticNameServerResolver.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class MQClientTest : public testing::Test { -public: - MQClientTest() = default; - - void SetUp() override { - grpc_init(); - name_server_list_.emplace_back(name_server_address_); - client_instance_ = std::make_shared(resource_namespace_); - rpc_client_ns_ = std::make_shared>(); - ON_CALL(*rpc_client_ns_, needHeartbeat()).WillByDefault(testing::Return(false)); - ON_CALL(*rpc_client_ns_, ok()).WillByDefault(testing::Invoke([this]() { return client_ok_; })); - ON_CALL(*rpc_client_ns_, asyncQueryRoute(testing::_, testing::_)) - .WillByDefault(testing::Invoke( - std::bind(&MQClientTest::mockQueryRoute, this, std::placeholders::_1, std::placeholders::_2))); - client_instance_->addRpcClient(name_server_address_, rpc_client_ns_); - ClientManagerFactory::getInstance().addClientManager(resource_namespace_, client_instance_); - - name_server_resolver_ = std::make_shared(name_server_address_); - } - - void TearDown() override { - rpc_client_ns_.reset(); - client_instance_->cleanRpcClients(); - client_instance_.reset(); - grpc_shutdown(); - } - -protected: - std::shared_ptr client_instance_; - std::shared_ptr> rpc_client_ns_; - const int16_t port_{10911}; - const int32_t partition_num_{24}; - const int32_t avg_partition_per_host_{8}; - std::string name_server_address_{"ipv4:127.0.0.1:9876"}; - std::shared_ptr name_server_resolver_; - std::string group_name_{"CID_Test"}; - std::string topic_{"Topic_Test"}; - std::string resource_namespace_{"mq://test"}; - google::rpc::Code ok_{google::rpc::Code::OK}; - bool client_ok_{true}; - std::vector name_server_list_; - -private: - void mockQueryRoute(const QueryRouteRequest& request, - InvocationContext* invocation_context) const { - invocation_context->response.mutable_common()->mutable_status()->set_code(google::rpc::Code::OK); - auto partitions = invocation_context->response.mutable_partitions(); - for (int i = 0; i < partition_num_; i++) { - auto partition = new rmq::Partition(); - partition->set_id(i % avg_partition_per_host_); - partition->set_permission(rmq::Permission::READ_WRITE); - partition->mutable_topic()->set_name(request.topic().name()); - partition->mutable_topic()->set_resource_namespace(request.topic().resource_namespace()); - std::string broker_name{"broker-"}; - broker_name.push_back('a' + i / avg_partition_per_host_); - partition->mutable_broker()->set_name(broker_name); - - auto endpoint = partition->mutable_broker()->mutable_endpoints(); - endpoint->set_scheme(rmq::AddressScheme::IPv4); - auto addresses = endpoint->mutable_addresses(); - auto address = new rmq::Address; - address->set_host(fmt::format("10.0.0.{}", i / avg_partition_per_host_)); - address->set_port(port_); - addresses->AddAllocated(address); - partitions->AddAllocated(partition); - } - // Mock invoke callback. - invocation_context->onCompletion(true); - } -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/scheduler/SchedulerTest.cpp b/src/test/cpp/ut/scheduler/SchedulerTest.cpp deleted file mode 100644 index 19a3dae72..000000000 --- a/src/test/cpp/ut/scheduler/SchedulerTest.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include -#include - -#include "SchedulerImpl.h" -#include "gtest/gtest.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class SchedulerTest : public testing::Test { -public: - SchedulerTest() : scheduler(std::make_shared()) { - } - - void SetUp() override { - scheduler->start(); - } - - void TearDown() override { - scheduler->shutdown(); - } - -protected: - SchedulerSharedPtr scheduler; -}; - -TEST_F(SchedulerTest, testSingleShot) { - absl::Mutex mtx; - absl::CondVar cv; - int callback_fire_count{0}; - auto callback = [&]() { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - }; - - scheduler->schedule(callback, "single-shot", std::chrono::milliseconds(10), std::chrono::milliseconds(0)); - - // Wait till callback is executed. - { - absl::MutexLock lock(&mtx); - if (!callback_fire_count) { - cv.Wait(&mtx); - } - } -} - -TEST_F(SchedulerTest, testCancel) { - absl::Mutex mtx; - absl::CondVar cv; - int callback_fire_count{0}; - auto callback = [&]() { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - }; - - std::uint32_t task_id = - scheduler->schedule(callback, "test-cancel", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); - scheduler->cancel(task_id); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - ASSERT_EQ(0, callback_fire_count); -} - -TEST_F(SchedulerTest, testPeriodicShot) { - absl::Mutex mtx; - absl::CondVar cv; - int callback_fire_count{0}; - auto callback = [&]() { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - }; - - std::uintptr_t task_id = - scheduler->schedule(callback, "periodic-task", std::chrono::milliseconds(10), std::chrono::milliseconds(100)); - // Wait till callback is executed. - std::this_thread::sleep_for(std::chrono::milliseconds(600)); - ASSERT_TRUE(callback_fire_count >= 4); - scheduler->cancel(task_id); -} - -TEST_F(SchedulerTest, testSingleShotWithZeroDelay) { - absl::Mutex mtx; - absl::CondVar cv; - int callback_fire_count{0}; - auto callback = [&]() { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - }; - - scheduler->schedule(callback, "single-shot-with-0-delay", std::chrono::milliseconds(0), std::chrono::milliseconds(0)); - - // Wait till callback is executed. - { - absl::MutexLock lock(&mtx); - if (!callback_fire_count) { - cv.Wait(&mtx); - } - } -} - -TEST_F(SchedulerTest, testException) { - absl::Mutex mtx; - absl::CondVar cv; - int callback_fire_count{0}; - auto callback = [&]() { - { - absl::MutexLock lock(&mtx); - cv.Signal(); - callback_fire_count++; - } - - std::exception e; - throw e; - }; - - scheduler->schedule(callback, "test-exception", std::chrono::milliseconds(100), std::chrono::milliseconds(100)); - - // Wait till callback is executed. - { - absl::MutexLock lock(&mtx); - if (callback_fire_count <= 5) { - cv.Wait(&mtx); - } - } -} -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/BUILD.bazel b/src/test/cpp/ut/tracing/exporters/BUILD.bazel deleted file mode 100644 index 1fc34774a..000000000 --- a/src/test/cpp/ut/tracing/exporters/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -load("@rules_cc//cc:defs.bzl", "cc_test") - -cc_test( - name = "otlp_exporter_test", - srcs = [ - "OtlpExportersTest.cpp", - ], - deps = [ - "//src/main/cpp/client/mocks:client_mocks", - "//src/main/cpp/tracing/exporters:otlp_exporter", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "span_context_test", - srcs = [ - "SpanContextTest.cpp", - ], - deps = [ - "@io_opencensus_cpp//opencensus/trace", - "@com_google_googletest//:gtest_main", - ], -) \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp b/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp deleted file mode 100644 index 1d859dc85..000000000 --- a/src/test/cpp/ut/tracing/exporters/OtlpExportersTest.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClientConfigMock.h" -#include "ClientManagerMock.h" -#include "OtlpExporter.h" -#include "opencensus/trace/sampler.h" -#include "opencensus/trace/span.h" -#include "rocketmq/RocketMQ.h" -#include "gtest/gtest.h" -#include -#include -#include - -ROCKETMQ_NAMESPACE_BEGIN - -class OtlpExporterTest : public testing::Test { -public: - void SetUp() override { - client_manager_ = std::make_shared>(); - } - - void TearDown() override { - } - -protected: - std::shared_ptr> client_manager_; - ClientConfigMock client_config_; -}; - -TEST_F(OtlpExporterTest, testExport) { - auto exporter = std::make_shared(client_manager_, &client_config_); - exporter->traceMode(TraceMode::Develop); - exporter->start(); - - auto& sampler = Samplers::always(); - - auto span_generator = [&] { - int total = 20; - while (total) { - auto span = opencensus::trace::Span::StartSpan("TestSpan", nullptr, {&sampler}); - span.AddAnnotation("annotation-1", {{"key-1", "value-1"}, {"key-2", 2}, {"key-3", true}}); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - span.End(); - if (0 == --total % 10) { - std::cout << "Total: " << total << std::endl; - } - } - }; - - std::thread t(span_generator); - if (t.joinable()) { - t.join(); - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp b/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp deleted file mode 100644 index beb39fa13..000000000 --- a/src/test/cpp/ut/tracing/exporters/SpanContextTest.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "opencensus/trace/propagation/trace_context.h" -#include "gtest/gtest.h" - -TEST(SpanContextTest, testSpanContext) { - std::string empty_context; - opencensus::trace::SpanContext span_context; - EXPECT_NO_THROW(span_context = opencensus::trace::propagation::FromTraceParentHeader(empty_context)); - EXPECT_FALSE(span_context.IsValid()); -} diff --git a/tools/trouble_shooting.sh b/tools/trouble_shooting.sh new file mode 100644 index 000000000..738bd6b62 --- /dev/null +++ b/tools/trouble_shooting.sh @@ -0,0 +1,2 @@ +export GRPC_VERBOSITY=debug +export GRPC_TRACE=tcp,http,api \ No newline at end of file diff --git a/tools/use_clang.sh b/tools/use_clang.sh new file mode 100755 index 000000000..7725259c9 --- /dev/null +++ b/tools/use_clang.sh @@ -0,0 +1,2 @@ +export CC=/usr/bin/clang +export CXX=/usr/bin/clang++ \ No newline at end of file From 28a0054ce8b41cbbcad9dbef43c188f55dabc621 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 23 May 2022 10:33:29 +0800 Subject: [PATCH 131/133] Unify standard and FIFO message processing implementation --- src/main/cpp/client/ClientManagerImpl.cpp | 29 +- src/main/cpp/client/include/ClientManager.h | 2 +- .../cpp/client/include/ClientManagerImpl.h | 9 +- .../rocketmq/AsyncReceiveMessageCallback.cpp | 3 +- .../rocketmq/ConsumeFifoMessageService.cpp | 322 ------------------ .../rocketmq/ConsumeMessageServiceBase.cpp | 104 ------ .../rocketmq/ConsumeMessageServiceImpl.cpp | 125 +++++++ .../ConsumeStandardMessageService.cpp | 275 --------------- src/main/cpp/rocketmq/ConsumeTask.cpp | 167 +++++++++ src/main/cpp/rocketmq/PushConsumerImpl.cpp | 41 +-- .../include/ConsumeFifoMessageService.h | 55 --- .../rocketmq/include/ConsumeMessageService.h | 27 +- .../include/ConsumeMessageServiceBase.h | 101 ------ .../include/ConsumeMessageServiceImpl.h | 85 +++++ .../include/ConsumeStandardMessageService.h | 46 --- src/main/cpp/rocketmq/include/ConsumeTask.h | 87 +++++ .../cpp/rocketmq/include/PushConsumerImpl.h | 4 +- .../rocketmq/mocks/include/PushConsumerMock.h | 2 - 18 files changed, 526 insertions(+), 958 deletions(-) delete mode 100644 src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp delete mode 100644 src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp create mode 100644 src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp delete mode 100644 src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp create mode 100644 src/main/cpp/rocketmq/ConsumeTask.cpp delete mode 100644 src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h delete mode 100644 src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h create mode 100644 src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h delete mode 100644 src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h create mode 100644 src/main/cpp/rocketmq/include/ConsumeTask.h diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index 25fc55923..a5fe96a8c 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -957,10 +957,11 @@ void ClientManagerImpl::endTransaction( client->asyncEndTransaction(request, invocation_context); } -void ClientManagerImpl::forwardMessageToDeadLetterQueue( - const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) { +void ClientManagerImpl::forwardMessageToDeadLetterQueue(const std::string& target_host, + const Metadata& metadata, + const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) { SPDLOG_DEBUG("ForwardMessageToDeadLetterQueue Request: {}", request.DebugString()); auto client = getRpcClient(target_host); auto invocation_context = new InvocationContext(); @@ -977,12 +978,28 @@ void ClientManagerImpl::forwardMessageToDeadLetterQueue( if (!invocation_context->status.ok()) { SPDLOG_WARN("Failed to transmit SendMessageToDeadLetterQueueRequest to host={}", invocation_context->remote_address); - cb(invocation_context); + std::error_code ec = ErrorCode::BadRequest; + cb(ec); return; } SPDLOG_DEBUG("Received forwardToDeadLetterQueue response from server[host={}]", invocation_context->remote_address); - cb(invocation_context); + std::error_code ec; + switch (invocation_context->response.status().code()) { + case rmq::Code::OK: { + break; + } + case rmq::Code::INTERNAL_SERVER_ERROR: { + ec = ErrorCode::ServiceUnavailable; + } + case rmq::Code::TOO_MANY_REQUESTS: { + ec = ErrorCode::TooManyRequest; + } + default: { + ec = ErrorCode::NotImplemented; + } + } + cb(ec); }; invocation_context->callback = callback; client->asyncForwardMessageToDeadLetterQueue(request, invocation_context); diff --git a/src/main/cpp/client/include/ClientManager.h b/src/main/cpp/client/include/ClientManager.h index c85c7ec2f..99ec6adeb 100644 --- a/src/main/cpp/client/include/ClientManager.h +++ b/src/main/cpp/client/include/ClientManager.h @@ -72,7 +72,7 @@ class ClientManager { virtual void forwardMessageToDeadLetterQueue( const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, std::chrono::milliseconds timeout, - const std::function*)>& cb) = 0; + const std::function& cb) = 0; virtual void endTransaction(const std::string& target_host, const Metadata& metadata, const EndTransactionRequest& request, std::chrono::milliseconds timeout, diff --git a/src/main/cpp/client/include/ClientManagerImpl.h b/src/main/cpp/client/include/ClientManagerImpl.h index 50d8b0525..63b07137d 100644 --- a/src/main/cpp/client/include/ClientManagerImpl.h +++ b/src/main/cpp/client/include/ClientManagerImpl.h @@ -150,10 +150,11 @@ class ClientManagerImpl : virtual public ClientManager, public std::enable_share const ChangeInvisibleDurationRequest&, std::chrono::milliseconds timeout, const std::function&) override; - void forwardMessageToDeadLetterQueue( - const std::string& target_host, const Metadata& metadata, const ForwardMessageToDeadLetterQueueRequest& request, - std::chrono::milliseconds timeout, - const std::function*)>& cb) override; + void forwardMessageToDeadLetterQueue(const std::string& target_host, + const Metadata& metadata, + const ForwardMessageToDeadLetterQueueRequest& request, + std::chrono::milliseconds timeout, + const std::function& cb) override; /** * End a transaction asynchronously. diff --git a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp index a9df915ae..c1b57d455 100644 --- a/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp +++ b/src/main/cpp/rocketmq/AsyncReceiveMessageCallback.cpp @@ -52,7 +52,8 @@ void AsyncReceiveMessageCallback::onCompletion(const std::error_code& ec, const SPDLOG_DEBUG("Receive messages from broker[host={}] returns with status=FOUND, msgListSize={}, queue={}", result.source_host, result.messages.size(), process_queue->simpleName()); process_queue->cacheMessages(result.messages); - consumer->getConsumeMessageService()->signalDispatcher(); + + consumer->getConsumeMessageService()->dispatch(process_queue, result.messages); checkThrottleThenReceive(); } diff --git a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp b/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp deleted file mode 100644 index aa99543da..000000000 --- a/src/main/cpp/rocketmq/ConsumeFifoMessageService.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "TracingUtility.h" -#include "absl/strings/str_join.h" - -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" - -#include "ConsumeFifoMessageService.h" -#include "MixAll.h" -#include "ProcessQueue.h" -#include "PushConsumerImpl.h" -#include "rocketmq/MessageListener.h" -#include "rocketmq/Tracing.h" - -ROCKETMQ_NAMESPACE_BEGIN - -ConsumeFifoMessageService::ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, - MessageListener message_listener) - : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { -} - -void ConsumeFifoMessageService::start() { - ConsumeMessageServiceBase::start(); - State expected = State::STARTING; - if (state_.compare_exchange_strong(expected, State::STARTED)) { - SPDLOG_DEBUG("ConsumeMessageOrderlyService started"); - } -} - -void ConsumeFifoMessageService::shutdown() { - // Wait till consume-message-orderly-service has fully started; otherwise, we may potentially miss closing resources - // in concurrent scenario. - while (State::STARTING == state_.load(std::memory_order_relaxed)) { - absl::SleepFor(absl::Milliseconds(10)); - } - - State expected = State::STARTED; - if (state_.compare_exchange_strong(expected, STOPPING)) { - ConsumeMessageServiceBase::shutdown(); - SPDLOG_INFO("ConsumeMessageOrderlyService shut down"); - } -} - -void ConsumeFifoMessageService::submitConsumeTask0(const std::shared_ptr& consumer, - const std::weak_ptr& process_queue, - MessageConstSharedPtr message) { - // In case custom executor is used. - const auto& custom_executor = consumer->customExecutor(); - if (custom_executor) { - std::function consume_task = - std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); - custom_executor(consume_task); - SPDLOG_DEBUG("Submit FIFO consume task to custom executor"); - return; - } - - // submit batch message - std::function consume_task = - std::bind(&ConsumeFifoMessageService::consumeTask, this, process_queue, message); - SPDLOG_DEBUG("Submit FIFO consume task to thread pool"); - pool_->submit(consume_task); -} - -void ConsumeFifoMessageService::submitConsumeTask(const std::weak_ptr& process_queue) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - SPDLOG_INFO("Process queue has destructed"); - return; - } - - auto consumer = consumer_.lock(); - if (!consumer) { - SPDLOG_INFO("Consumer has destructed"); - return; - } - - if (process_queue_ptr->bindFifoConsumeTask()) { - std::vector messages; - process_queue_ptr->take(1, messages); - if (!messages.empty()) { - assert(1 == messages.size()); - submitConsumeTask0(consumer, process_queue, std::move(*messages.begin())); - } - } -} - -void ConsumeFifoMessageService::consumeTask(const std::weak_ptr& process_queue, - MessageConstSharedPtr message) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - return; - } - const std::string& topic = message->topic(); - ConsumeResult result; - std::shared_ptr consumer = consumer_.lock(); - // consumer might have been destructed. - if (!consumer) { - return; - } - - std::shared_ptr> rate_limiter = rateLimiter(topic); - if (rate_limiter) { - rate_limiter->acquire(); - SPDLOG_DEBUG("Rate-limit permit acquired"); - } - - // Record await-consumption-span - if (message->traceContext().has_value()) { - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message->traceContext().value()); - - auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = - consumer->resourceNamespace() + "/" + topic + " " + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, traceSampler()); - } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - TracingUtility::addUniversalSpanAttributes(*message, consumer->config(), span); - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, message.extension.store_time); - absl::Time decoded_timestamp = absl::FromChrono(message->extension().decode_time); - span.AddAnnotation( - MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, - {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, - opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); - span.End(); - // message.message.traceContext() - // MessageAccessor::setTraceContext(const_cast(message), - // opencensus::trace::propagation::ToTraceParentHeader(span.context())); - } - - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(message->traceContext().value()); - auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + message->topic() + " " + - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); - } else { - span = opencensus::trace::Span::StartSpan(span_name); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); - TracingUtility::addUniversalSpanAttributes(*message, consumer->config(), span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, std::to_string(message->extension().delivery_attempt)); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, - MixAll::format(message->extension().store_time)); - - // MessageAccessor::setTraceContext(const_cast(message), - // opencensus::trace::propagation::ToTraceParentHeader(span.context())); - auto steady_start = std::chrono::steady_clock::now(); - - try { - result = message_listener_(*message); - } catch (...) { - result = ConsumeResult::FAILURE; - SPDLOG_ERROR("Business FIFO callback raised an exception when consumeMessage"); - } - - switch (result) { - case ConsumeResult::SUCCESS: - span.SetStatus(opencensus::trace::StatusCode::OK); - break; - case ConsumeResult::FAILURE: - span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); - break; - } - span.End(); - - auto duration = std::chrono::steady_clock::now() - steady_start; - - // Log client consume-time costs - SPDLOG_DEBUG("Business callback spent {}ms processing message[Topic={}, MessageId={}].", - MixAll::millisecondsOf(duration), message->topic(), message->id()); - - if (result == ConsumeResult::SUCCESS) { - // Release message number and memory quota - process_queue_ptr->release(message->body().size()); - - // Ensure current message is acked before moving to the next message. - auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); - consumer->ack(*message, callback); - } else { - const Message* ptr = message.get(); - Message* raw = const_cast(ptr); - raw->mutableExtension().delivery_attempt++; - - if (message->extension().delivery_attempt < consumer->maxDeliveryAttempts()) { - auto task = std::bind(&ConsumeFifoMessageService::scheduleConsumeTask, this, process_queue, message); - consumer->schedule("Scheduled-Consume-FIFO-Message-Task", task, std::chrono::seconds(1)); - } else { - auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, - std::placeholders::_1); - consumer->forwardToDeadLetterQueue(*message, callback); - } - } -} - -void ConsumeFifoMessageService::onAck(const std::weak_ptr& process_queue, - MessageConstSharedPtr message, - const std::error_code& ec) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - SPDLOG_WARN("ProcessQueue has destructed."); - return; - } - - if (ec) { - SPDLOG_WARN("Failed to acknowledge FIFO message[MessageQueue={}, MsgId={}]. Cause: {}", - process_queue_ptr->simpleName(), message->id(), ec.message()); - auto consumer = consumer_.lock(); - if (!consumer) { - SPDLOG_WARN("Consumer instance has destructed"); - return; - } - auto task = std::bind(&ConsumeFifoMessageService::scheduleAckTask, this, process_queue, message); - int32_t duration = 100; - consumer->schedule("Ack-FIFO-Message-On-Failure", task, std::chrono::milliseconds(duration)); - SPDLOG_INFO("Scheduled to ack message[Topic={}, MessageId={}] in {}ms", message->topic(), message->id(), duration); - } else { - SPDLOG_DEBUG("Acknowledge FIFO message[MessageQueue={}, MsgId={}] OK", process_queue_ptr->simpleName(), - message->id()); - process_queue_ptr->unbindFifoConsumeTask(); - submitConsumeTask(process_queue); - } -} - -void ConsumeFifoMessageService::onForwardToDeadLetterQueue(const std::weak_ptr& process_queue, - MessageConstSharedPtr message, - bool ok) { - if (ok) { - SPDLOG_DEBUG("Forward message[Topic={}, MessagId={}] to DLQ OK", message->topic(), message->id()); - auto process_queue_ptr = process_queue.lock(); - if (process_queue_ptr) { - process_queue_ptr->unbindFifoConsumeTask(); - } - return; - } - - SPDLOG_INFO("Failed to forward message[topic={}, MessageId={}] to DLQ", message->topic(), message->id()); - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - SPDLOG_INFO("Abort further attempts considering its process queue has destructed"); - return; - } - - auto consumer = consumer_.lock(); - assert(consumer); - - auto task = std::bind(&ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask, this, process_queue, message); - consumer->schedule("Scheduled-Forward-DLQ-Task", task, std::chrono::milliseconds(100)); -} - -void ConsumeFifoMessageService::scheduleForwardDeadLetterQueueTask(const std::weak_ptr& process_queue, - MessageConstSharedPtr message) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - return; - } - auto consumer = consumer_.lock(); - assert(consumer); - auto callback = std::bind(&ConsumeFifoMessageService::onForwardToDeadLetterQueue, this, process_queue, message, - std::placeholders::_1); - consumer->forwardToDeadLetterQueue(*message, callback); -} - -void ConsumeFifoMessageService::scheduleAckTask(const std::weak_ptr& process_queue, - MessageConstSharedPtr message) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - return; - } - - auto callback = std::bind(&ConsumeFifoMessageService::onAck, this, process_queue, message, std::placeholders::_1); - auto consumer = consumer_.lock(); - if (consumer) { - consumer->ack(*message, callback); - } -} - -void ConsumeFifoMessageService::scheduleConsumeTask(const std::weak_ptr& process_queue, - MessageConstSharedPtr message) { - auto consumer_ptr = consumer_.lock(); - if (!consumer_ptr) { - return; - } - - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - return; - } - - submitConsumeTask0(consumer_ptr, process_queue_ptr, message); - SPDLOG_INFO("Business callback failed to process FIFO messages. Re-submit consume task back to thread pool"); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp deleted file mode 100644 index 71904221e..000000000 --- a/src/main/cpp/rocketmq/ConsumeMessageServiceBase.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ConsumeMessageServiceBase.h" -#include "LoggerImpl.h" -#include "PushConsumerImpl.h" -#include "ThreadPoolImpl.h" - -ROCKETMQ_NAMESPACE_BEGIN - -ConsumeMessageServiceBase::ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, - MessageListener message_listener) - : state_(State::CREATED), thread_count_(thread_count), pool_(absl::make_unique(thread_count_)), - consumer_(std::move(consumer)), message_listener_(message_listener) { -} - -void ConsumeMessageServiceBase::start() { - State expected = State::CREATED; - if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { - pool_->start(); - dispatch_thread_ = std::thread([this] { - State current_state = state_.load(std::memory_order_relaxed); - while (State::STOPPED != current_state && State::STOPPING != current_state) { - dispatch(); - { - absl::MutexLock lk(&dispatch_mtx_); - dispatch_cv_.WaitWithTimeout(&dispatch_mtx_, absl::Milliseconds(100)); - } - - // Update current state - current_state = state_.load(std::memory_order_relaxed); - } - }); - } -} - -void ConsumeMessageServiceBase::signalDispatcher() { - absl::MutexLock lk(&dispatch_mtx_); - // Wake up dispatch_thread_ - dispatch_cv_.Signal(); -} - -void ConsumeMessageServiceBase::throttle(const std::string& topic, std::uint32_t threshold) { - absl::MutexLock lk(&rate_limiter_table_mtx_); - std::shared_ptr> rate_limiter = std::make_shared>(threshold); - rate_limiter_table_.insert_or_assign(topic, rate_limiter); - rate_limiter_observer_.subscribe(rate_limiter); -} - -void ConsumeMessageServiceBase::shutdown() { - State expected = State::STOPPING; - if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { - pool_->shutdown(); - { - absl::MutexLock lk(&dispatch_mtx_); - dispatch_cv_.SignalAll(); - } - - if (dispatch_thread_.joinable()) { - dispatch_thread_.join(); - } - - rate_limiter_observer_.stop(); - } -} - -bool ConsumeMessageServiceBase::hasConsumeRateLimiter(const std::string& topic) const { - absl::MutexLock lk(&rate_limiter_table_mtx_); - return rate_limiter_table_.contains(topic); -} - -std::shared_ptr> ConsumeMessageServiceBase::rateLimiter(const std::string& topic) const { - if (!hasConsumeRateLimiter(topic)) { - return nullptr; - } - absl::MutexLock lk(&rate_limiter_table_mtx_); - return rate_limiter_table_[topic]; -} - -void ConsumeMessageServiceBase::dispatch() { - std::shared_ptr consumer = consumer_.lock(); - if (!consumer) { - SPDLOG_WARN("The consumer has already destructed"); - return; - } - - auto callback = [this](const std::shared_ptr& process_queue) { submitConsumeTask(process_queue); }; - consumer->iterateProcessQueue(callback); -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp new file mode 100644 index 000000000..9c9ad49c6 --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConsumeMessageServiceImpl.h" + +#include "ConsumeTask.h" +#include "LoggerImpl.h" +#include "PushConsumerImpl.h" +#include "ThreadPoolImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeMessageServiceImpl::ConsumeMessageServiceImpl(std::weak_ptr consumer, + int thread_count, + MessageListener message_listener) + : state_(State::CREATED), + thread_count_(thread_count), + pool_(absl::make_unique(thread_count_)), + consumer_(std::move(consumer)), + message_listener_(message_listener) { +} + +void ConsumeMessageServiceImpl::start() { + State expected = State::CREATED; + if (state_.compare_exchange_strong(expected, State::STARTING, std::memory_order_relaxed)) { + pool_->start(); + } +} + +void ConsumeMessageServiceImpl::shutdown() { + State expected = State::STOPPING; + if (state_.compare_exchange_strong(expected, State::STOPPED, std::memory_order_relaxed)) { + pool_->shutdown(); + } +} + +void ConsumeMessageServiceImpl::dispatch(std::shared_ptr process_queue, + std::vector messages) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + if (consumer->config().subscriber.fifo) { + auto consume_task = std::make_shared(shared_from_this(), process_queue, std::move(messages)); + pool_->submit([consume_task]() { consume_task->process(); }); + return; + } + + for (auto message : messages) { + auto consume_task = std::make_shared(shared_from_this(), process_queue, message); + pool_->submit([consume_task]() { consume_task->process(); }); + } +} + +void ConsumeMessageServiceImpl::submit(std::shared_ptr task) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + pool_->submit([task]() { task->process(); }); +} + +void ConsumeMessageServiceImpl::ack(const Message& message, std::function cb) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + consumer->ack(message, cb); +} + +void ConsumeMessageServiceImpl::nack(const Message& message, std::function cb) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + + consumer->nack(message, cb); +} + +void ConsumeMessageServiceImpl::forward(const Message& message, std::function cb) { + auto consumer = consumer_.lock(); + if (!consumer) { + return; + } + consumer->forwardToDeadLetterQueue(message, cb); +} + +void ConsumeMessageServiceImpl::schedule(std::shared_ptr task, std::chrono::milliseconds delay) { +} + +std::size_t ConsumeMessageServiceImpl::maxDeliveryAttempt() { + std::shared_ptr consumer = consumer_.lock(); + if (!consumer) { + SPDLOG_WARN("The consumer has already destructed"); + return 0; + } + + return consumer->maxDeliveryAttempts(); +} + +bool ConsumeMessageServiceImpl::preHandle(const Message& message) { + return true; +} + +bool ConsumeMessageServiceImpl::postHandle(const Message& message, ConsumeResult result) { + return true; +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp b/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp deleted file mode 100644 index b478df9c8..000000000 --- a/src/main/cpp/rocketmq/ConsumeStandardMessageService.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ConsumeStandardMessageService.h" - -#include -#include -#include -#include - -#include "LoggerImpl.h" -#include "MessageExt.h" -#include "MixAll.h" -#include "Protocol.h" -#include "PushConsumerImpl.h" -#include "TracingUtility.h" -#include "UtilAll.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_join.h" -#include "absl/time/time.h" -#include "absl/types/span.h" -#include "rocketmq/Message.h" -#include "rocketmq/MessageListener.h" -#include "rocketmq/Tracing.h" - -ROCKETMQ_NAMESPACE_BEGIN - -ConsumeStandardMessageService::ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, - MessageListener message_listener) - : ConsumeMessageServiceBase(std::move(consumer), thread_count, message_listener) { -} - -void ConsumeStandardMessageService::start() { - ConsumeMessageServiceBase::start(); - State expected = State::STARTING; - if (state_.compare_exchange_strong(expected, State::STARTED)) { - SPDLOG_DEBUG("ConsumeMessageConcurrentlyService started"); - } -} - -void ConsumeStandardMessageService::shutdown() { - while (State::STARTING == state_.load(std::memory_order_relaxed)) { - absl::SleepFor(absl::Milliseconds(10)); - } - - State expected = State::STARTED; - if (state_.compare_exchange_strong(expected, State::STOPPING)) { - ConsumeMessageServiceBase::shutdown(); - SPDLOG_DEBUG("ConsumeMessageConcurrentlyService shut down"); - } -} - -void ConsumeStandardMessageService::submitConsumeTask(const std::weak_ptr& process_queue) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr) { - SPDLOG_WARN("ProcessQueue was destructed. It is likely that client should have shutdown."); - return; - } - std::shared_ptr consumer = process_queue_ptr->getConsumer().lock(); - - if (!consumer) { - return; - } - - std::string topic = process_queue_ptr->topic(); - bool has_more = true; - std::weak_ptr service(shared_from_this()); - while (has_more) { - std::vector messages; - uint32_t batch_size = 1; - has_more = process_queue_ptr->take(batch_size, messages); - if (messages.empty()) { - assert(!has_more); - break; - } - - // In case custom executor is used. - const Executor& custom_executor = consumer->customExecutor(); - if (custom_executor) { - std::function consume_task = - std::bind(&ConsumeStandardMessageService::consumeTask, service, process_queue, messages); - custom_executor(consume_task); - SPDLOG_DEBUG("Submit consumer task to custom executor with message-batch-size={}", messages.size()); - continue; - } - - // submit batch message - std::function consume_task = - std::bind(&ConsumeStandardMessageService::consumeTask, service, process_queue_ptr, messages); - SPDLOG_DEBUG("Submit consumer task to thread pool with message-batch-size={}", messages.size()); - pool_->submit(consume_task); - } -} - -void ConsumeStandardMessageService::consumeTask(std::weak_ptr service, - const std::weak_ptr& process_queue, - const std::vector& msgs) { - auto process_queue_ptr = process_queue.lock(); - if (!process_queue_ptr || msgs.empty()) { - return; - } - - auto svc = service.lock(); - if (!svc) { - return; - } - - auto process_queue_shared_ptr = process_queue.lock(); - if (!process_queue_shared_ptr) { - return; - } - - svc->consume(process_queue_shared_ptr, msgs); -} - -void ConsumeStandardMessageService::consume(const std::shared_ptr& process_queue, - const std::vector& msgs) { - std::string topic = (*msgs.begin())->topic(); - ConsumeResult status; - - std::shared_ptr consumer = consumer_.lock(); - // consumer might have been destructed. - if (!consumer) { - return; - } - - std::shared_ptr> rate_limiter = rateLimiter(topic); - if (rate_limiter) { - // Acquire permits one-by-one to avoid large batch hungry issue. - for (std::size_t i = 0; i < msgs.size(); i++) { - rate_limiter->acquire(); - } - SPDLOG_DEBUG("{} rate-limit permits acquired", msgs.size()); - } - - // Record await-consumption-span - { - for (const auto& msg : msgs) { - if (!msg->traceContext().has_value()) { - continue; - } - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg->traceContext().value()); - - auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + msg->topic() + " " + - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, traceSampler()); - } else { - span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_AWAIT_OPERATION); - TracingUtility::addUniversalSpanAttributes(*msg, consumer->config(), span); - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.getStoreTimestamp()); - absl::Time decoded_timestamp = absl::FromChrono(msg->extension().decode_time); - span.AddAnnotation( - MixAll::SPAN_ANNOTATION_AWAIT_CONSUMPTION, - {{MixAll::SPAN_ANNOTATION_ATTR_START_TIME, - opencensus::trace::AttributeValueRef(absl::ToInt64Milliseconds(decoded_timestamp - absl::UnixEpoch()))}}); - span.End(); - // MessageAccessor::setTraceContext(const_cast(msg), - // opencensus::trace::propagation::ToTraceParentHeader(span.context())); - } - } - - // Trace start of consume message - std::vector spans; - { - for (const auto& msg : msgs) { - if (!msg->traceContext().has_value()) { - continue; - } - auto span_context = opencensus::trace::propagation::FromTraceParentHeader(msg->traceContext().value()); - auto span = opencensus::trace::Span::BlankSpan(); - std::string span_name = consumer->resourceNamespace() + "/" + msg->topic() + " " + - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION; - if (span_context.IsValid()) { - span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context); - } else { - span = opencensus::trace::Span::StartSpan(span_name); - } - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_PROCESS_OPERATION); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_PROCESS_OPERATION); - TracingUtility::addUniversalSpanAttributes(*msg, consumer->config(), span); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, msg->extension().delivery_attempt); - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_AVAILABLE_TIMESTAMP, msg.extension.store_time); - span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_BATCH_SIZE, msgs.size()); - spans.emplace_back(std::move(span)); - // MessageAccessor::setTraceContext(const_cast(msg), - // opencensus::trace::propagation::ToTraceParentHeader(span.context())); - } - } - - auto steady_start = std::chrono::steady_clock::now(); - - try { - // TODO: - status = message_listener_(**msgs.begin()); - } catch (...) { - status = ConsumeResult::FAILURE; - SPDLOG_ERROR("Business callback raised an exception when consumeMessage"); - } - - auto duration = std::chrono::steady_clock::now() - steady_start; - - // Trace end of consumption - { - for (auto& span : spans) { - switch (status) { - case ConsumeResult::SUCCESS: - span.SetStatus(opencensus::trace::StatusCode::OK); - break; - case ConsumeResult::FAILURE: - span.SetStatus(opencensus::trace::StatusCode::UNKNOWN); - break; - } - span.End(); - } - } - - // Log client consume-time costs - SPDLOG_DEBUG("Business callback spent {}ms processing {} messages.", MixAll::millisecondsOf(duration), msgs.size()); - - for (const auto& msg : msgs) { - const std::string& message_id = msg->id(); - - // Release message number and memory quota - process_queue->release(msg->body().size()); - - if (status == ConsumeResult::SUCCESS) { - auto callback = [process_queue, message_id](const std::error_code& ec) { - if (ec) { - SPDLOG_WARN("Failed to acknowledge message[MessageQueue={}, MsgId={}]. Cause: {}", - process_queue->simpleName(), message_id, ec.message()); - } else { - SPDLOG_DEBUG("Acknowledge message[MessageQueue={}, MsgId={}] OK", process_queue->simpleName(), message_id); - } - }; - consumer->ack(*msg, callback); - } else { - auto callback = [process_queue, message_id](const std::error_code& ec) { - if (ec) { - SPDLOG_WARN( - "Failed to negative acknowledge message[MessageQueue={}, MsgId={}]. Cause: {} Message will be " - "re-consumed after default invisible time", - process_queue->simpleName(), message_id, ec.message()); - return; - } - - SPDLOG_DEBUG("Nack message[MessageQueue={}, MsgId={}] OK", process_queue->simpleName(), message_id); - }; - consumer->nack(*msg, callback); - } - } -} - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeTask.cpp b/src/main/cpp/rocketmq/ConsumeTask.cpp new file mode 100644 index 000000000..dede4a4aa --- /dev/null +++ b/src/main/cpp/rocketmq/ConsumeTask.cpp @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConsumeTask.h" + +#include "LoggerImpl.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeTask::ConsumeTask(ConsumeMessageServiceWeakPtr service, + std::weak_ptr process_queue, + MessageConstSharedPtr message) + : service_(service), process_queue_(std::move(process_queue)) { + messages_.emplace_back(message); +} + +ConsumeTask::ConsumeTask(ConsumeMessageServiceWeakPtr service, + std::weak_ptr process_queue, + std::vector messages) + : service_(service), process_queue_(std::move(process_queue)), messages_(std::move(messages)) { + fifo_ = messages_.size() > 1; +} + +void ConsumeTask::pop() { + assert(!messages_.empty()); + auto process_queue = process_queue_.lock(); + if (!process_queue) { + return; + } + + process_queue->release(messages_[0]->body().size()); + + messages_.erase(messages_.begin()); +} + +void ConsumeTask::submit() { + auto svc = service_.lock(); + if (!svc) { + return; + } + svc->submit(shared_from_this()); +} + +void ConsumeTask::schedule() { + auto svc = service_.lock(); + if (!svc) { + return; + } + + svc->schedule(shared_from_this(), std::chrono::seconds(1)); +} + +void ConsumeTask::onAck(std::shared_ptr task, const std::error_code& ec) { + if (task->fifo_ && ec) { + auto service = task->service_.lock(); + task->next_step_ = NextStep::Ack; + task->schedule(); + } else { + // If it is not FIFO or ack operation succeeded + task->pop(); + task->next_step_ = NextStep::Consume; + } + task->submit(); +} + +void ConsumeTask::onNack(std::shared_ptr task, const std::error_code& ec) { + assert(!task->fifo_); + assert(!task->messages_.empty()); + if (ec) { + SPDLOG_WARN("Failed to nack message[message-id={}]. Cause: {}", task->messages_[0]->id(), ec.message()); + } + task->pop(); + task->next_step_ = NextStep::Consume; + task->submit(); +} + +void ConsumeTask::onForward(std::shared_ptr task, const std::error_code& ec) { + assert(task->fifo_); + assert(!task->messages_.empty()); + if (ec) { + SPDLOG_DEBUG("Failed to forward Message[message-id={}] to DLQ", task->messages_[0]->id()); + task->next_step_ = NextStep::Forward; + task->schedule(); + } else { + SPDLOG_DEBUG("Message[message-id={}] forwarded to DLQ", task->messages_[0]->id()); + task->pop(); + task->next_step_ = NextStep::Consume; + task->submit(); + } +} + +void ConsumeTask::process() { + auto svc = service_.lock(); + if (!svc) { + SPDLOG_DEBUG("ConsumeMessageService has destructed"); + return; + } + + if (messages_.empty()) { + SPDLOG_DEBUG("No more messages to process"); + return; + } + + auto self = shared_from_this(); + + switch (next_step_) { + case NextStep::Consume: { + const auto& listener = svc->listener(); + auto it = messages_.begin(); + SPDLOG_DEBUG("Start to process message[message-id={}]", (*it)->id()); + svc->preHandle(**it); + auto result = listener(**it); + svc->postHandle(**it, result); + switch (result) { + case ConsumeResult::SUCCESS: { + auto callback = std::bind(&ConsumeTask::onAck, self, std::placeholders::_1); + svc->ack(**it, callback); + break; + } + case ConsumeResult::FAILURE: { + if (fifo_) { + next_step_ = NextStep::Consume; + // Increase delivery attempts. + auto raw = const_cast((*it).get()); + raw->mutableExtension().delivery_attempt++; + schedule(); + } else { + // For standard way of processing, Nack to server. + auto callback = std::bind(&ConsumeTask::onNack, self, std::placeholders::_1); + svc->nack(**it, callback); + } + break; + } + } + break; + } + + case NextStep::Ack: { + assert(!messages_.empty()); + auto callback = std::bind(&ConsumeTask::onAck, self, std::placeholders::_1); + svc->ack(*messages_[0], callback); + break; + } + case NextStep::Forward: { + assert(!messages_.empty()); + auto callback = std::bind(&ConsumeTask::onForward, self, std::placeholders::_1); + svc->forward(*messages_[0], callback); + break; + } + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/PushConsumerImpl.cpp b/src/main/cpp/rocketmq/PushConsumerImpl.cpp index cd94d9f41..89dc11223 100644 --- a/src/main/cpp/rocketmq/PushConsumerImpl.cpp +++ b/src/main/cpp/rocketmq/PushConsumerImpl.cpp @@ -24,8 +24,7 @@ #include "AsyncReceiveMessageCallback.h" #include "ClientManagerFactory.h" -#include "ConsumeFifoMessageService.h" -#include "ConsumeStandardMessageService.h" +#include "ConsumeMessageServiceImpl.h" #include "MixAll.h" #include "ProcessQueueImpl.h" #include "Protocol.h" @@ -72,27 +71,11 @@ void PushConsumerImpl::start() { fetchRoutes(); - if (client_config_.subscriber.fifo) { - SPDLOG_INFO("start orderly consume service: {}", client_config_.subscriber.group.name()); - consume_message_service_ = - std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); - consume_batch_size_ = 1; - } else { - // For backward compatibility, by default, ConsumeMessageConcurrentlyService is assumed. - SPDLOG_INFO("start concurrently consume service: {}", client_config_.subscriber.group.name()); - consume_message_service_ = std::make_shared( - shared_from_this(), consume_thread_pool_size_, message_listener_); - } + SPDLOG_INFO("start concurrently consume service: {}", client_config_.subscriber.group.name()); + consume_message_service_ = + std::make_shared(shared_from_this(), consume_thread_pool_size_, message_listener_); consume_message_service_->start(); - { - // Set consumer throttling - absl::MutexLock lock(&throttle_table_mtx_); - for (const auto& item : throttle_table_) { - consume_message_service_->throttle(item.first, item.second); - } - } - // Heartbeat depends on initialization of consume-message-service heartbeat(); @@ -394,7 +377,8 @@ void PushConsumerImpl::nack(const Message& message, const std::function& cb) { +void PushConsumerImpl::forwardToDeadLetterQueue(const Message& message, + const std::function& cb) { std::string target_host = message.extension().target_endpoint; absl::flat_hash_map metadata; @@ -449,19 +433,6 @@ std::size_t PushConsumerImpl::getProcessQueueTableSize() { void PushConsumerImpl::setThrottle(const std::string& topic, uint32_t threshold) { absl::MutexLock lock(&throttle_table_mtx_); throttle_table_.emplace(topic, threshold); - // If consumer has started, update it dynamically. - if (getConsumeMessageService()) { - getConsumeMessageService()->throttle(topic, threshold); - } -} - -void PushConsumerImpl::iterateProcessQueue(const std::function)>& callback) { - absl::MutexLock lock(&process_queue_table_mtx_); - for (const auto& item : process_queue_table_) { - if (item.second->hasPendingMessages()) { - callback(item.second); - } - } } void PushConsumerImpl::fetchRoutes() { diff --git a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h b/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h deleted file mode 100644 index 5f95d9676..000000000 --- a/src/main/cpp/rocketmq/include/ConsumeFifoMessageService.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ConsumeMessageServiceBase.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ConsumeFifoMessageService : public ConsumeMessageServiceBase, - public std::enable_shared_from_this { -public: - ConsumeFifoMessageService(std::weak_ptr consumer, int thread_count, - MessageListener message_listener); - void start() override; - - void shutdown() override; - - /** - * @brief Entry of ConsumeMessageService - * - * @param process_queue - */ - void submitConsumeTask(const std::weak_ptr& process_queue) override; - -private: - void consumeTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - - void submitConsumeTask0(const std::shared_ptr& consumer, - const std::weak_ptr& process_queue, - MessageConstSharedPtr message); - - void scheduleAckTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - - void onAck(const std::weak_ptr& process_queue, MessageConstSharedPtr message, const std::error_code& ec); - - void scheduleConsumeTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); - - void onForwardToDeadLetterQueue(const std::weak_ptr& process_queue, MessageConstSharedPtr message, bool ok); - - void scheduleForwardDeadLetterQueueTask(const std::weak_ptr& process_queue, MessageConstSharedPtr message); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index 0c541eac9..39d9d8ed6 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -17,12 +17,16 @@ #pragma once #include +#include +#include #include "ProcessQueue.h" #include "rocketmq/MessageListener.h" ROCKETMQ_NAMESPACE_BEGIN +class ConsumeTask; + class ConsumeMessageService { public: virtual ~ConsumeMessageService() = default; @@ -38,11 +42,28 @@ class ConsumeMessageService { */ virtual void shutdown() = 0; - virtual void submitConsumeTask(const std::weak_ptr& process_queue) = 0; + virtual void dispatch(std::shared_ptr process_queue, std::vector messages) = 0; + + virtual void submit(std::shared_ptr task) = 0; + + virtual MessageListener& listener() = 0; + + virtual bool preHandle(const Message& message) = 0; + + virtual bool postHandle(const Message& message, ConsumeResult result) = 0; - virtual void signalDispatcher() = 0; + virtual void ack(const Message& message, std::function cb) = 0; - virtual void throttle(const std::string& topic, std::uint32_t threshold) = 0; + virtual void nack(const Message& message, std::function cb) = 0; + + virtual void forward(const Message& message, std::function cb) = 0; + + virtual void schedule(std::shared_ptr task, std::chrono::milliseconds delay) = 0; + + virtual std::size_t maxDeliveryAttempt() = 0; }; +using ConsumeMessageServiceWeakPtr = std::weak_ptr; +using ConsumeMessageServiceSharedPtr = std::shared_ptr; + ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h deleted file mode 100644 index 9e9ff43ef..000000000 --- a/src/main/cpp/rocketmq/include/ConsumeMessageServiceBase.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" - -#include "ConsumeMessageService.h" -#include "RateLimiter.h" -#include "ThreadPool.h" -#include "rocketmq/State.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class PushConsumerImpl; - -class ConsumeMessageServiceBase : public ConsumeMessageService { -public: - ConsumeMessageServiceBase(std::weak_ptr consumer, int thread_count, - MessageListener message_listener); - - ~ConsumeMessageServiceBase() override = default; - - /** - * Make it noncopyable. - */ - ConsumeMessageServiceBase(const ConsumeMessageServiceBase& other) = delete; - ConsumeMessageServiceBase& operator=(const ConsumeMessageServiceBase& other) = delete; - - /** - * Start the dispatcher thread, which will dispatch messages in process queue to thread pool in form of runnable - * functor. - */ - void start() override; - - /** - * Stop the dispatcher thread and then reset the thread pool. - */ - void shutdown() override; - - /** - * Signal dispatcher thread to check new pending messages. - */ - void signalDispatcher() override; - - /** - * Set throttle threshold per topic. - * - * @param topic - * @param threshold - */ - void throttle(const std::string& topic, std::uint32_t threshold) override; - - bool hasConsumeRateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); - - std::shared_ptr> rateLimiter(const std::string& topic) const LOCKS_EXCLUDED(rate_limiter_table_mtx_); - -protected: - RateLimiterObserver rate_limiter_observer_; - - mutable absl::flat_hash_map>> - rate_limiter_table_ GUARDED_BY(rate_limiter_table_mtx_); - mutable absl::Mutex rate_limiter_table_mtx_; // Protects rate_limiter_table_ - - std::atomic state_; - - int thread_count_; - std::unique_ptr pool_; - std::weak_ptr consumer_; - - absl::Mutex dispatch_mtx_; - std::thread dispatch_thread_; - absl::CondVar dispatch_cv_; - - MessageListener message_listener_; - - /** - * Dispatch messages to thread pool. Implementation of this function should be sub-class specific. - */ - void dispatch(); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h new file mode 100644 index 000000000..7d6c34afb --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +#include "ConsumeMessageService.h" +#include "RateLimiter.h" +#include "ThreadPool.h" +#include "absl/container/flat_hash_map.h" +#include "rocketmq/State.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class PushConsumerImpl; + +class ConsumeMessageServiceImpl : public ConsumeMessageService, + public std::enable_shared_from_this { +public: + ConsumeMessageServiceImpl(std::weak_ptr consumer, + int thread_count, + MessageListener message_listener); + + ~ConsumeMessageServiceImpl() override = default; + + /** + * Make it noncopyable. + */ + ConsumeMessageServiceImpl(const ConsumeMessageServiceImpl& other) = delete; + ConsumeMessageServiceImpl& operator=(const ConsumeMessageServiceImpl& other) = delete; + + void start() override; + + void shutdown() override; + + MessageListener& listener() override { + return message_listener_; + } + + bool preHandle(const Message& message) override; + + bool postHandle(const Message& message, ConsumeResult result) override; + + void submit(std::shared_ptr task) override; + + void dispatch(std::shared_ptr process_queue, std::vector messages) override; + + void ack(const Message& message, std::function cb) override; + + void nack(const Message& message, std::function cb) override; + + void forward(const Message& message, std::function cb) override; + + void schedule(std::shared_ptr task, std::chrono::milliseconds delay) override; + + std::size_t maxDeliveryAttempt() override; + +protected: + std::atomic state_; + + int thread_count_; + std::unique_ptr pool_; + std::weak_ptr consumer_; + + MessageListener message_listener_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h b/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h deleted file mode 100644 index e98f257ec..000000000 --- a/src/main/cpp/rocketmq/include/ConsumeStandardMessageService.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "ConsumeMessageServiceBase.h" -#include "ProcessQueue.h" - -ROCKETMQ_NAMESPACE_BEGIN - -class ConsumeStandardMessageService : public ConsumeMessageServiceBase, - public std::enable_shared_from_this { -public: - ConsumeStandardMessageService(std::weak_ptr consumer, int thread_count, - MessageListener message_listener); - - ~ConsumeStandardMessageService() override = default; - - void start() override; - - void shutdown() override; - - void submitConsumeTask(const std::weak_ptr& process_queue) override; - -private: - static void consumeTask(std::weak_ptr service, - const std::weak_ptr& process_queue, - const std::vector& msgs); - - void consume(const std::shared_ptr& process_queue, const std::vector& msgs); -}; - -ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/ConsumeTask.h b/src/main/cpp/rocketmq/include/ConsumeTask.h new file mode 100644 index 000000000..80ca97151 --- /dev/null +++ b/src/main/cpp/rocketmq/include/ConsumeTask.h @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include "ConsumeMessageService.h" +#include "rocketmq/Message.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ProcessQueue; + +/** + * @brief Operation to take for the consume-task. + */ +enum class NextStep : std::uint8_t +{ + /** + * @brief Continue to consume the remaining messages. + */ + Consume = 0, + + /** + * @brief Ack the head, aka, messages_[0]. + */ + Ack, + + /** + * @brief Forward the head, aka, messages_[0], to dead-letter-queue. + */ + Forward, +}; + +class ConsumeTask : public std::enable_shared_from_this { +public: + ConsumeTask(ConsumeMessageServiceWeakPtr service, + std::weak_ptr process_queue, + MessageConstSharedPtr message); + + ConsumeTask(ConsumeMessageServiceWeakPtr service, + std::weak_ptr process_queue, + std::vector messages); + + void process(); + + void submit(); + + void schedule(); + +private: + ConsumeMessageServiceWeakPtr service_; + std::weak_ptr process_queue_; + std::vector messages_; + bool fifo_{false}; + NextStep next_step_{NextStep::Consume}; + + /** + * @brief messages_[0] has completed its life-cycle. + */ + void pop(); + + static void onAck(std::shared_ptr task, const std::error_code& ec); + + static void onNack(std::shared_ptr task, const std::error_code& ec); + + static void onForward(std::shared_ptr task, const std::error_code& ec); +}; + +using ConsumeTaskSharedPtr = std::shared_ptr; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index 58ba4e530..cf5fe50ac 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -119,7 +119,7 @@ class PushConsumerImpl : virtual public ClientImpl, public std::enable_shared_fr void nack(const Message& message, const std::function& callback); - void forwardToDeadLetterQueue(const Message& message, const std::function& cb); + void forwardToDeadLetterQueue(const Message& message, const std::function& cb); void wrapAckMessageRequest(const Message& msg, AckMessageRequest& request); @@ -154,8 +154,6 @@ class PushConsumerImpl : virtual public ClientImpl, public std::enable_shared_fr return MixAll::DEFAULT_CACHED_MESSAGE_MEMORY; } - void iterateProcessQueue(const std::function)>& callback); - MessageListener& messageListener() { return message_listener_; } diff --git a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h index 0a9a3611e..c6050aec3 100644 --- a/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h +++ b/src/main/cpp/rocketmq/mocks/include/PushConsumerMock.h @@ -25,8 +25,6 @@ ROCKETMQ_NAMESPACE_BEGIN class PushConsumerMock : virtual public PushConsumer, virtual public ConsumerMock { public: - MOCK_METHOD(void, iterateProcessQueue, (const std::function)>&), (override)); - MOCK_METHOD(MessageModel, messageModel, (), (const override)); MOCK_METHOD(void, ack, (const MQMessageExt&, const std::function&), (override)); From e7d188a3dc3ec00e11e0c00aba45b5e135b1185b Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Mon, 30 May 2022 15:04:33 +0800 Subject: [PATCH 132/133] Instrument with opencensus to collect various metrics (#415) * Sync with protobuf files * Add more tags * Collect send success, failure and latency statistics data * Collect metrics for tx commit/rollback success/failure * Extract tag definitions to Tag * Define measures and metrics for consumer * Collect metrics for PushConsumer * Collect more metrics * Use latest opencensus to configure exporter interval * WIP * WIP * WIP * Remove metrics of committing/rolling-back transactional messages * Fix trace --- .vscode/settings.json | 4 + api/rocketmq/Message.h | 4 + bazel/rocketmq_deps.bzl | 11 + proto/apache/rocketmq/v2/definition.proto | 9 +- proto/apache/rocketmq/v2/service.proto | 25 ++- src/main/cpp/client/ClientManagerImpl.cpp | 8 +- src/main/cpp/client/TelemetryBidiReactor.cpp | 8 + src/main/cpp/client/include/ClientConfig.h | 6 + .../rocketmq/ConsumeMessageServiceImpl.cpp | 29 ++- src/main/cpp/rocketmq/ConsumeTask.cpp | 15 ++ src/main/cpp/rocketmq/ProducerImpl.cpp | 63 +++--- src/main/cpp/rocketmq/SendContext.cpp | 48 ++++- .../rocketmq/include/ConsumeMessageService.h | 2 + .../include/ConsumeMessageServiceImpl.h | 2 + src/main/cpp/rocketmq/include/ProducerImpl.h | 8 +- .../cpp/rocketmq/include/PushConsumerImpl.h | 7 + src/main/cpp/rocketmq/include/SendContext.h | 2 + src/main/cpp/stats/BUILD.bazel | 2 + src/main/cpp/stats/ConsumeStats.cpp | 148 ++++++++++++++ src/main/cpp/stats/MetricBidiReactor.cpp | 116 +++++++++++ src/main/cpp/stats/OpencensusExporter.cpp | 190 ++++++++++++++++++ src/main/cpp/stats/PublishStats.cpp | 20 +- src/main/cpp/stats/Tag.cpp | 41 ++++ src/main/cpp/stats/include/ConsumeStats.h | 87 ++++++++ src/main/cpp/stats/include/Exporter.h | 30 +++ .../cpp/stats/include/MetricBidiReactor.h | 78 +++++++ .../cpp/stats/include/OpencensusExporter.h | 53 +++++ src/main/cpp/stats/include/PublishStats.h | 9 +- src/main/cpp/stats/include/Tag.h | 35 ++++ src/main/cpp/stats/tests/PublishStatsTest.cpp | 71 ++++++- 30 files changed, 1063 insertions(+), 68 deletions(-) create mode 100644 src/main/cpp/stats/ConsumeStats.cpp create mode 100644 src/main/cpp/stats/MetricBidiReactor.cpp create mode 100644 src/main/cpp/stats/OpencensusExporter.cpp create mode 100644 src/main/cpp/stats/Tag.cpp create mode 100644 src/main/cpp/stats/include/ConsumeStats.h create mode 100644 src/main/cpp/stats/include/Exporter.h create mode 100644 src/main/cpp/stats/include/MetricBidiReactor.h create mode 100644 src/main/cpp/stats/include/OpencensusExporter.h create mode 100644 src/main/cpp/stats/include/Tag.h diff --git a/.vscode/settings.json b/.vscode/settings.json index cb613f96a..9d42abdaf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,9 @@ "--header-insertion=never", "--compile-commands-dir=${workspaceFolder}/", "--query-driver=/**/*", + ], + "cSpell.words": [ + "Opencensus", + "SPDLOG" ] } \ No newline at end of file diff --git a/api/rocketmq/Message.h b/api/rocketmq/Message.h index 0537fa4d6..0c2ef02a1 100644 --- a/api/rocketmq/Message.h +++ b/api/rocketmq/Message.h @@ -78,6 +78,10 @@ class Message { return absl::make_optional(trace_context_); } + void traceContext(std::string &&trace_context) { + trace_context_ = std::move(trace_context); + } + const std::string& bornHost() const { return born_host_; } diff --git a/bazel/rocketmq_deps.bzl b/bazel/rocketmq_deps.bzl index 7282dd7fe..39f364292 100644 --- a/bazel/rocketmq_deps.bzl +++ b/bazel/rocketmq_deps.bzl @@ -85,6 +85,17 @@ def rocketmq_deps(): ], ) + maybe( + http_archive, + name = "io_opencensus_cpp", + sha256 = "317f2bfdaba469561c7e64b1a55282b87e677c109c9d8877097940e6d5cbca08", + urls = [ + "https://shutian.oss-cn-hangzhou.aliyuncs.com/cdn/opencensus-cpp/opencensus-cpp-0.4.1.tar.gz", + "https://github.com/lizhanhui/opencensus-cpp/archive/refs/tags/v0.4.1.tar.gz", + ], + strip_prefix = "opencensus-cpp-0.4.1", + ) + if "com_google_absl" not in native.existing_rules(): http_archive( name = "com_google_absl", diff --git a/proto/apache/rocketmq/v2/definition.proto b/proto/apache/rocketmq/v2/definition.proto index 6565a21b9..58bf06d29 100644 --- a/proto/apache/rocketmq/v2/definition.proto +++ b/proto/apache/rocketmq/v2/definition.proto @@ -291,12 +291,6 @@ message Assignment { MessageQueue message_queue = 1; } -message SendReceipt { - string message_id = 1; - string transaction_id = 2; - int64 offset = 3; -} - enum Code { // Success. OK = 0; @@ -395,6 +389,9 @@ enum Code { // Client type could not be recognized. UNRECOGNIZED_CLIENT_TYPE = 32; + // Return different results for entries in composite request. + MULTIPLE_RESULTS = 33; + // Code indicates that the server encountered an unexpected condition // that prevented it from fulfilling the request. // This error response is a generic "catch-all" response. diff --git a/proto/apache/rocketmq/v2/service.proto b/proto/apache/rocketmq/v2/service.proto index aa688b454..fa2a2f054 100644 --- a/proto/apache/rocketmq/v2/service.proto +++ b/proto/apache/rocketmq/v2/service.proto @@ -60,9 +60,19 @@ message SendMessageRequest { repeated Message messages = 1; } +message SendResultEntry { + Status status = 1; + string message_id = 2; + string transaction_id = 3; + int64 offset = 4; +} + message SendMessageResponse { Status status = 1; - repeated SendReceipt receipts = 2; + + // Some implementation may have partial failure issues. Client SDK developers are expected to inspect + // each entry for best certainty. + repeated SendResultEntry entries = 2; } message QueryAssignmentRequest { @@ -169,7 +179,8 @@ message ThreadStackTrace { message VerifyMessageCommand { string nonce = 1; - Message message = 2; + MessageQueue message_queue = 2; + Message message = 3; } message VerifyMessageResult { @@ -230,6 +241,14 @@ message Subscription { optional google.protobuf.Duration long_polling_timeout = 5; } +message Metric { + // Indicates that if client should export local metrics to server. + bool on = 1; + + // The endpoint that client metrics should be exported to, which is required if the switch is on. + optional Endpoints endpoints = 2; +} + message Settings { // Configurations for all clients. optional ClientType client_type = 1; @@ -259,6 +278,8 @@ message Settings { // User agent details UA user_agent = 7; + + Metric metric = 8; } message TelemetryCommand { diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp index a5fe96a8c..0c6e155f7 100644 --- a/src/main/cpp/client/ClientManagerImpl.cpp +++ b/src/main/cpp/client/ClientManagerImpl.cpp @@ -316,7 +316,7 @@ bool ClientManagerImpl::send(const std::string& target_host, const Metadata& met auto&& status = invocation_context->response.status(); switch (invocation_context->response.status().code()) { case rmq::Code::OK: { - send_receipt.message_id = invocation_context->response.receipts().begin()->message_id(); + send_receipt.message_id = invocation_context->response.entries().begin()->message_id(); break; } case rmq::Code::TOPIC_NOT_FOUND: { @@ -416,9 +416,9 @@ SendReceipt ClientManagerImpl::processSendResponse(const rmq::MessageQueue& mess switch (response.status().code()) { case rmq::Code::OK: { - assert(response.receipts_size() > 0); - send_receipt.message_id = response.receipts().begin()->message_id(); - send_receipt.transaction_id = response.receipts().begin()->transaction_id(); + assert(response.entries_size() > 0); + send_receipt.message_id = response.entries().begin()->message_id(); + send_receipt.transaction_id = response.entries().begin()->transaction_id(); return send_receipt; } case rmq::Code::ILLEGAL_TOPIC: { diff --git a/src/main/cpp/client/TelemetryBidiReactor.cpp b/src/main/cpp/client/TelemetryBidiReactor.cpp index f39cdecfd..cf251e49f 100644 --- a/src/main/cpp/client/TelemetryBidiReactor.cpp +++ b/src/main/cpp/client/TelemetryBidiReactor.cpp @@ -194,6 +194,14 @@ void TelemetryBidiReactor::applySettings(const rmq::Settings& settings) { applyBackoffPolicy(settings, ptr); + // Sync metrics collector configuration + if (settings.has_metric()) { + const auto& metric = settings.metric(); + ptr->config().metric.on = metric.on(); + ptr->config().metric.endpoints.set_scheme(metric.endpoints().scheme()); + ptr->config().metric.endpoints.mutable_addresses()->CopyFrom(metric.endpoints().addresses()); + } + switch (settings.pub_sub_case()) { case rmq::Settings::PubSubCase::kPublishing: { applyPublishingConfig(settings, ptr); diff --git a/src/main/cpp/client/include/ClientConfig.h b/src/main/cpp/client/include/ClientConfig.h index 458ca5f3c..66ca230b2 100644 --- a/src/main/cpp/client/include/ClientConfig.h +++ b/src/main/cpp/client/include/ClientConfig.h @@ -44,6 +44,11 @@ struct SubscriberConfig { absl::Duration polling_timeout{absl::Seconds(30)}; }; +struct Metric { + bool on{false}; + rmq::Endpoints endpoints; +}; + struct ClientConfig { std::string client_id; rmq::ClientType client_type{rmq::ClientType::CLIENT_TYPE_UNSPECIFIED}; @@ -55,6 +60,7 @@ struct ClientConfig { std::shared_ptr credentials_provider; PublisherConfig publisher; SubscriberConfig subscriber; + Metric metric; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp b/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp index 9c9ad49c6..74d8176c0 100644 --- a/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp +++ b/src/main/cpp/rocketmq/ConsumeMessageServiceImpl.cpp @@ -16,9 +16,11 @@ */ #include "ConsumeMessageServiceImpl.h" +#include "ConsumeStats.h" #include "ConsumeTask.h" #include "LoggerImpl.h" #include "PushConsumerImpl.h" +#include "Tag.h" #include "ThreadPoolImpl.h" ROCKETMQ_NAMESPACE_BEGIN @@ -81,7 +83,24 @@ void ConsumeMessageServiceImpl::ack(const Message& message, std::functionack(message, cb); + std::weak_ptr client(consumer_); + const auto& topic = message.topic(); + // Collect metrics + opencensus::stats::Record({{consumer->stats().processSuccess(), 1}}, + {{Tag::topicTag(), topic}, {Tag::clientIdTag(), consumer->config().client_id}}); + auto callback = [cb, client, topic](const std::error_code& ec) { + auto consumer = client.lock(); + if (ec) { + opencensus::stats::Record({{consumer->stats().ackFailure(), 1}}, + {{Tag::topicTag(), topic}, {Tag::clientIdTag(), consumer->config().client_id}}); + } else { + opencensus::stats::Record({{consumer->stats().ackSuccess(), 1}}, + {{Tag::topicTag(), topic}, {Tag::clientIdTag(), consumer->config().client_id}}); + } + cb(ec); + }; + + consumer->ack(message, callback); } void ConsumeMessageServiceImpl::nack(const Message& message, std::function cb) { @@ -89,7 +108,9 @@ void ConsumeMessageServiceImpl::nack(const Message& message, std::functionstats().processFailure(), 1}}, + {{Tag::topicTag(), message.topic()}, {Tag::clientIdTag(), consumer->config().client_id}}); consumer->nack(message, cb); } @@ -114,6 +135,10 @@ std::size_t ConsumeMessageServiceImpl::maxDeliveryAttempt() { return consumer->maxDeliveryAttempts(); } +std::weak_ptr ConsumeMessageServiceImpl::consumer() { + return consumer_; +} + bool ConsumeMessageServiceImpl::preHandle(const Message& message) { return true; } diff --git a/src/main/cpp/rocketmq/ConsumeTask.cpp b/src/main/cpp/rocketmq/ConsumeTask.cpp index dede4a4aa..69d299a3e 100644 --- a/src/main/cpp/rocketmq/ConsumeTask.cpp +++ b/src/main/cpp/rocketmq/ConsumeTask.cpp @@ -17,7 +17,10 @@ #include "ConsumeTask.h" +#include "ConsumeStats.h" #include "LoggerImpl.h" +#include "PushConsumerImpl.h" +#include "Tag.h" ROCKETMQ_NAMESPACE_BEGIN @@ -115,6 +118,8 @@ void ConsumeTask::process() { return; } + std::shared_ptr consumer = svc->consumer().lock(); + auto self = shared_from_this(); switch (next_step_) { @@ -123,7 +128,17 @@ void ConsumeTask::process() { auto it = messages_.begin(); SPDLOG_DEBUG("Start to process message[message-id={}]", (*it)->id()); svc->preHandle(**it); + auto await_time = std::chrono::system_clock::now() - (*it)->extension().decode_time; + opencensus::stats::Record( + {{consumer->stats().awaitTime(), MixAll::millisecondsOf(await_time)}}, + {{Tag::topicTag(), (*it)->topic()}, {Tag::clientIdTag(), consumer->config().client_id}}); + + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); auto result = listener(**it); + auto duration = std::chrono::steady_clock::now() - start; + opencensus::stats::Record( + {{consumer->stats().processTime(), MixAll::millisecondsOf(duration)}}, + {{Tag::topicTag(), (*it)->topic()}, {Tag::clientIdTag(), consumer->config().client_id}}); svc->postHandle(**it, result); switch (result) { case ConsumeResult::SUCCESS: { diff --git a/src/main/cpp/rocketmq/ProducerImpl.cpp b/src/main/cpp/rocketmq/ProducerImpl.cpp index 4c203d83e..4b6c3f0f5 100644 --- a/src/main/cpp/rocketmq/ProducerImpl.cpp +++ b/src/main/cpp/rocketmq/ProducerImpl.cpp @@ -34,6 +34,7 @@ #include "SendContext.h" #include "SendMessageContext.h" #include "Signature.h" +#include "Tag.h" #include "TracingUtility.h" #include "TransactionImpl.h" #include "UniqueIdGenerator.h" @@ -287,32 +288,35 @@ void ProducerImpl::sendImpl(std::shared_ptr context) { return; } - // { - // Trace Send RPC - // auto span_context = - // opencensus::trace::propagation::FromTraceParentHeader(context->message_.traceContext().value()); auto span = - // opencensus::trace::Span::BlankSpan(); std::string span_name = resourceNamespace() + "/" + context->message_.topic() - // + " " + - // MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; - // if (span_context.IsValid()) { - // span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {&Samplers::always()}); - // } else { - // span = opencensus::trace::Span::StartSpan(span_name, nullptr, {&Samplers::always()}); - // } - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, - // MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, - // MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); - // TracingUtility::addUniversalSpanAttributes(context->message_, *this, span); - // Note: attempt-time is 0-based - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + callback->attemptTime()); - // if (message.deliveryTimestamp() != absl::ToChronoTime(absl::UnixEpoch())) { - // span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, - // absl::FormatTime(absl::FromChrono(message.deliveryTimestamp()))); - // } - // callback->message().traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); - // callback->span() = span; - // } + { + // Trace Send RPC + if (context->message_->traceContext().has_value()) { + auto span_context = + opencensus::trace::propagation::FromTraceParentHeader(context->message_->traceContext().value()); + auto span = opencensus::trace::Span::BlankSpan(); + std::string span_name = resourceNamespace() + "/" + context->message_->topic() + " " + + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION; + if (span_context.IsValid()) { + span = opencensus::trace::Span::StartSpanWithRemoteParent(span_name, span_context, {traceSampler()}); + } else { + span = opencensus::trace::Span::StartSpan(span_name, nullptr, {traceSampler()}); + } + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_MESSAGING_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_ROCKETMQ_SEND_OPERATION); + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_OPERATION, + MixAll::SPAN_ATTRIBUTE_VALUE_MESSAGING_SEND_OPERATION); + TracingUtility::addUniversalSpanAttributes(*context->message_, config(), span); + // Note: attempt-time is 0-based + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_ATTEMPT, 1 + context->attempt_times_); + if (context->message_->deliveryTimestamp().has_value()) { + span.AddAttribute(MixAll::SPAN_ATTRIBUTE_KEY_ROCKETMQ_DELIVERY_TIMESTAMP, + absl::FormatTime(absl::FromChrono(context->message_->deliveryTimestamp().value()))); + } + auto ptr = const_cast(context->message_.get()); + ptr->traceContext(opencensus::trace::propagation::ToTraceParentHeader(span.context())); + context->span_ = span; + } + } SendMessageRequest request; wrapSendMessageRequest(*context->message_, request, context->messageQueue()); @@ -354,7 +358,8 @@ void ProducerImpl::send0(MessageConstPtr message, SendCallback callback, std::ve bool ProducerImpl::endTransaction0(const Transaction& transaction, TransactionState resolution) { EndTransactionRequest request; - request.mutable_topic()->set_name(transaction.topic()); + const std::string& topic = transaction.topic(); + request.mutable_topic()->set_name(topic); request.mutable_topic()->set_resource_namespace(resourceNamespace()); request.set_message_id(transaction.messageId()); request.set_transaction_id(transaction.messageId()); @@ -396,7 +401,9 @@ bool ProducerImpl::endTransaction0(const Transaction& transaction, TransactionSt auto mtx = std::make_shared(); auto cv = std::make_shared(); const auto& endpoint = transaction.endpoint(); - auto cb = [&, span, endpoint, mtx, cv](const std::error_code& ec, const EndTransactionResponse& response) { + std::weak_ptr publisher(shared_from_this()); + + auto cb = [&, span, endpoint, mtx, cv, topic](const std::error_code& ec, const EndTransactionResponse& response) { if (ec) { { span.SetStatus(opencensus::trace::StatusCode::ABORTED); diff --git a/src/main/cpp/rocketmq/SendContext.cpp b/src/main/cpp/rocketmq/SendContext.cpp index 73db9a1c0..9dee0e87f 100644 --- a/src/main/cpp/rocketmq/SendContext.cpp +++ b/src/main/cpp/rocketmq/SendContext.cpp @@ -18,14 +18,15 @@ #include -#include "opencensus/trace/propagation/trace_context.h" -#include "opencensus/trace/span.h" -#include "spdlog/spdlog.h" - #include "ProducerImpl.h" +#include "PublishStats.h" +#include "Tag.h" #include "TransactionImpl.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" #include "rocketmq/Logger.h" #include "rocketmq/SendReceipt.h" +#include "spdlog/spdlog.h" ROCKETMQ_NAMESPACE_BEGIN @@ -35,6 +36,28 @@ void SendContext::onSuccess(const SendReceipt& send_receipt) noexcept { span_.SetStatus(opencensus::trace::StatusCode::OK); span_.End(); } + + auto publisher = producer_.lock(); + if (!publisher) { + return; + } + + // Collect metrics + { + auto duration = std::chrono::steady_clock::now() - request_time_; + opencensus::stats::Record({{publisher->stats().latency(), MixAll::millisecondsOf(duration)}}, + { + {Tag::topicTag(), message_->topic()}, + {Tag::clientIdTag(), publisher->config().client_id}, + }); + + opencensus::stats::Record({{publisher->stats().success(), 1}}, + { + {Tag::topicTag(), message_->topic()}, + {Tag::clientIdTag(), publisher->config().client_id}, + }); + } + // send_receipt.traceContext(opencensus::trace::propagation::ToTraceParentHeader(span_.context())); std::error_code ec; callback_(ec, send_receipt); @@ -52,6 +75,22 @@ void SendContext::onFailure(const std::error_code& ec) noexcept { return; } + // Collect metrics + { + auto duration = std::chrono::steady_clock::now() - request_time_; + opencensus::stats::Record({{publisher->stats().latency(), MixAll::millisecondsOf(duration)}}, + { + {Tag::topicTag(), message_->topic()}, + {Tag::clientIdTag(), publisher->config().client_id}, + }); + + opencensus::stats::Record({{publisher->stats().failure(), 1}}, + { + {Tag::topicTag(), message_->topic()}, + {Tag::clientIdTag(), publisher->config().client_id}, + }); + } + if (++attempt_times_ >= publisher->maxAttemptTimes()) { SPDLOG_WARN("Retried {} times, which exceeds the limit: {}", attempt_times_, publisher->maxAttemptTimes()); callback_(ec, {}); @@ -72,6 +111,7 @@ void SendContext::onFailure(const std::error_code& ec) noexcept { } auto message_queue = candidates_[attempt_times_ % candidates_.size()]; + request_time_ = std::chrono::steady_clock::now(); producer->sendImpl(shared_from_this()); } diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageService.h b/src/main/cpp/rocketmq/include/ConsumeMessageService.h index 39d9d8ed6..0f8b49a85 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageService.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageService.h @@ -61,6 +61,8 @@ class ConsumeMessageService { virtual void schedule(std::shared_ptr task, std::chrono::milliseconds delay) = 0; virtual std::size_t maxDeliveryAttempt() = 0; + + virtual std::weak_ptr consumer() = 0; }; using ConsumeMessageServiceWeakPtr = std::weak_ptr; diff --git a/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h b/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h index 7d6c34afb..632a388e2 100644 --- a/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h +++ b/src/main/cpp/rocketmq/include/ConsumeMessageServiceImpl.h @@ -72,6 +72,8 @@ class ConsumeMessageServiceImpl : public ConsumeMessageService, std::size_t maxDeliveryAttempt() override; + std::weak_ptr consumer() override; + protected: std::atomic state_; diff --git a/src/main/cpp/rocketmq/include/ProducerImpl.h b/src/main/cpp/rocketmq/include/ProducerImpl.h index b765fa616..a50c6ef82 100644 --- a/src/main/cpp/rocketmq/include/ProducerImpl.h +++ b/src/main/cpp/rocketmq/include/ProducerImpl.h @@ -37,6 +37,7 @@ #include "rocketmq/SendReceipt.h" #include "rocketmq/State.h" #include "rocketmq/TransactionChecker.h" +#include "PublishStats.h" ROCKETMQ_NAMESPACE_BEGIN @@ -105,7 +106,10 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t void buildClientSettings(rmq::Settings& settings) override; - void topicsOfInterest(std::vector topics) LOCKS_EXCLUDED(topics_mtx_); + void topicsOfInterest(std::vector topics) + LOCKS_EXCLUDED(topics_mtx_); + + const PublishStats& stats() const { return stats_; } protected: std::shared_ptr self() override { @@ -126,6 +130,8 @@ class ProducerImpl : virtual public ClientImpl, public std::enable_shared_from_t std::vector topics_ GUARDED_BY(topics_mtx_); absl::Mutex topics_mtx_; + PublishStats stats_; + /** * @brief Acquire PublishInfo for the given topic. * Generally speaking, it first checks presence of the desired info in local cache, aka, topic_publish_table_; diff --git a/src/main/cpp/rocketmq/include/PushConsumerImpl.h b/src/main/cpp/rocketmq/include/PushConsumerImpl.h index cf5fe50ac..0067009a9 100644 --- a/src/main/cpp/rocketmq/include/PushConsumerImpl.h +++ b/src/main/cpp/rocketmq/include/PushConsumerImpl.h @@ -25,6 +25,7 @@ #include "ClientImpl.h" #include "ClientManagerImpl.h" #include "ConsumeMessageService.h" +#include "ConsumeStats.h" #include "ProcessQueue.h" #include "Scheduler.h" #include "TopicAssignmentInfo.h" @@ -162,6 +163,10 @@ class PushConsumerImpl : virtual public ClientImpl, public std::enable_shared_fr return client_config_.subscriber.group.name(); } + const ConsumeStats& stats() const { + return stats_; + } + protected: std::shared_ptr self() override { return shared_from_this(); @@ -206,6 +211,8 @@ class PushConsumerImpl : virtual public ClientImpl, public std::enable_shared_fr int32_t max_delivery_attempts_{MixAll::DEFAULT_MAX_DELIVERY_ATTEMPTS}; + ConsumeStats stats_; + void fetchRoutes() LOCKS_EXCLUDED(topic_filter_expression_table_mtx_); std::chrono::milliseconds invisibleDuration(std::size_t attempt); diff --git a/src/main/cpp/rocketmq/include/SendContext.h b/src/main/cpp/rocketmq/include/SendContext.h index bba83c8fe..4c05cebeb 100644 --- a/src/main/cpp/rocketmq/include/SendContext.h +++ b/src/main/cpp/rocketmq/include/SendContext.h @@ -72,6 +72,8 @@ class SendContext : public std::enable_shared_from_this { * @brief The on-going span. Should be terminated in the callback functions. */ opencensus::trace::Span span_; + + std::chrono::steady_clock::time_point request_time_{std::chrono::steady_clock::now()}; }; ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/BUILD.bazel b/src/main/cpp/stats/BUILD.bazel index f31a08135..fbf6af20d 100644 --- a/src/main/cpp/stats/BUILD.bazel +++ b/src/main/cpp/stats/BUILD.bazel @@ -22,7 +22,9 @@ cc_library( strip_include_prefix = "//src/main/cpp/stats/include", deps = [ "//api:rocketmq_interface", + "//src/main/cpp/client:client_library", "@io_opencensus_cpp//opencensus/stats", + "@opencensus_proto//opencensus/proto/agent/metrics/v1:metrics_service_grpc_cc", ], visibility = ["//visibility:public"], ) \ No newline at end of file diff --git a/src/main/cpp/stats/ConsumeStats.cpp b/src/main/cpp/stats/ConsumeStats.cpp new file mode 100644 index 000000000..e563c8cf3 --- /dev/null +++ b/src/main/cpp/stats/ConsumeStats.cpp @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConsumeStats.h" + +#include "Tag.h" + +ROCKETMQ_NAMESPACE_BEGIN + +ConsumeStats::ConsumeStats() + : process_success_( + opencensus::stats::MeasureInt64::Register("process_success", "Number of messages processed", "1")), + process_failure_(opencensus::stats::MeasureInt64::Register( + "process_failure", "Number of failures when processing messages", "1")), + ack_success_(opencensus::stats::MeasureInt64::Register("ack_success", "Number of messages acknowledged", "1")), + ack_failure_(opencensus::stats::MeasureInt64::Register( + "ack_failure", "Number of failures when acknowledging messages", "1")), + change_invisible_time_success_(opencensus::stats::MeasureInt64::Register( + "change_invisible_time_success", "Number of change-invisible-time performed", "1")), + change_invisible_time_failure_(opencensus::stats::MeasureInt64::Register( + "change_invisible_time_failure", "Number of failures when changing message invisible time", "1")), + cached_message_quantity_(opencensus::stats::MeasureInt64::Register( + "cached_message_quantity", "Number of locally cached messages", "1")), + cached_message_bytes_(opencensus::stats::MeasureInt64::Register( + "cached_message_bytes", "Number of locally cached messages in bytes", "1")), + delivery_latency_(opencensus::stats::MeasureInt64::Register( + "delivery_latency", "Time spent delivering messages from servers to clients", "1")), + await_time_(opencensus::stats::MeasureInt64::Register( + "await_time", "Client side queuing time of messages before getting processed", "1")), + process_time_(opencensus::stats::MeasureInt64::Register("process_time", "Process message time", "1")) { + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_process_success_total") + .set_description("Number of messages processed") + .set_measure("process_success") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_process_failure_total") + .set_description("Number of failures on processing messages") + .set_measure("process_failure") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_ack_success_total") + .set_description("Number of messages acknowledged") + .set_measure("ack_success") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_ack_failure_total") + .set_description("Number of failures on acknowledging messages") + .set_measure("ack_failure") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_change_invisible_time_success_total") + .set_description("Number of change-invisible-time operations") + .set_measure("change_invisible_time_success") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_change_invisible_time_failure_total") + .set_description("Number of failed change-invisible-time operations") + .set_measure("change_invisible_time_failure") + .set_aggregation(opencensus::stats::Aggregation::Sum()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_consumer_cached_messages") + .set_description("Number of messages locally cached") + .set_measure("cached_message_quantity") + .set_aggregation(opencensus::stats::Aggregation::LastValue()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_consumer_cached_bytes") + .set_description("Number of locally cached messages in bytes") + .set_measure("cached_message_bytes") + .set_aggregation(opencensus::stats::Aggregation::LastValue()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_delivery_latency") + .set_description("Message delivery latency") + .set_measure("delivery_latency") + .set_aggregation(opencensus::stats::Aggregation::Distribution( + opencensus::stats::BucketBoundaries::Explicit({5, 10, 20, 50, 500}))) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_await_time") + .set_description("Message await time") + .set_measure("await_time") + .set_aggregation(opencensus::stats::Aggregation::Distribution( + opencensus::stats::BucketBoundaries::Explicit({1, 1000, 60000, 900000}))) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); + + opencensus::stats::ViewDescriptor() + .set_name("rocketmq_process_time") + .set_description("Process time") + .set_measure("process_time") + .set_aggregation(opencensus::stats::Aggregation::Distribution( + opencensus::stats::BucketBoundaries::Explicit({100, 1000, 60000, 900000}))) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) + .RegisterForExport(); +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/MetricBidiReactor.cpp b/src/main/cpp/stats/MetricBidiReactor.cpp new file mode 100644 index 000000000..62983ee4b --- /dev/null +++ b/src/main/cpp/stats/MetricBidiReactor.cpp @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MetricBidiReactor.h" + +#include "LoggerImpl.h" +#include "OpencensusExporter.h" +#include "Signature.h" + +ROCKETMQ_NAMESPACE_BEGIN + +MetricBidiReactor::MetricBidiReactor(std::weak_ptr client, std::weak_ptr exporter) + : client_(client), exporter_(exporter) { + grpc::ClientContext context; + auto ptr = client_.lock(); + + Metadata metadata; + Signature::sign(ptr->config(), metadata); + + for (const auto& entry : metadata) { + context.AddMetadata(entry.first, entry.second); + } + context.set_deadline(std::chrono::system_clock::now() + absl::ToChronoMilliseconds(ptr->config().request_timeout)); + + auto exporter_ptr = exporter_.lock(); + if (!exporter_ptr) { + return; + } + + exporter_ptr->stub()->async()->Export(&context, this); + StartCall(); +} + +void MetricBidiReactor::OnReadDone(bool ok) { + if (!ok) { + SPDLOG_WARN("Failed to read response"); + return; + } + + StartRead(&response_); +} + +void MetricBidiReactor::OnWriteDone(bool ok) { + if (!ok) { + SPDLOG_WARN("Failed to report metrics"); + return; + } + + bool expected = true; + if (inflight_.compare_exchange_strong(expected, false, std::memory_order_relaxed)) { + fireWrite(); + } +} + +void MetricBidiReactor::OnDone(const grpc::Status& s) { + auto client = client_.lock(); + if (!client) { + return; + } + + if (s.ok()) { + SPDLOG_DEBUG("Bi-directional stream ended. status.code={}, status.message={}", s.error_code(), s.error_message()); + } else { + SPDLOG_WARN("Bi-directional stream ended. status.code={}, status.message={}", s.error_code(), s.error_message()); + } +} + +void MetricBidiReactor::write(ExportMetricsServiceRequest request) { + { + absl::MutexLock lk(&requests_mtx_); + requests_.emplace_back(std::move(request)); + } + + fireWrite(); +} + +void MetricBidiReactor::fireWrite() { + { + absl::MutexLock lk(&requests_mtx_); + if (requests_.empty()) { + SPDLOG_DEBUG("No more metric data to write"); + return; + } + } + + bool expected = false; + if (inflight_.compare_exchange_strong(expected, true, std::memory_order_relaxed)) { + absl::MutexLock lk(&requests_mtx_); + request_.CopyFrom(requests_[0]); + requests_.erase(requests_.begin()); + StartWrite(&request_); + fireRead(); + } +} + +void MetricBidiReactor::fireRead() { + bool expected = false; + if (read_.compare_exchange_strong(expected, true, std::memory_order_relaxed)) { + StartRead(&response_); + } +} + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/OpencensusExporter.cpp b/src/main/cpp/stats/OpencensusExporter.cpp new file mode 100644 index 000000000..0bf13a48f --- /dev/null +++ b/src/main/cpp/stats/OpencensusExporter.cpp @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OpencensusExporter.h" + +#include "MetricBidiReactor.h" +#include "google/protobuf/util/time_util.h" + +ROCKETMQ_NAMESPACE_BEGIN + +namespace opencensus_proto = opencensus::proto::metrics::v1; + +OpencensusExporter::OpencensusExporter(std::string endpoints, std::weak_ptr client) : client_(client) { +} + +void OpencensusExporter::wrap(const MetricData& data, ExportMetricsServiceRequest& request) { + auto metrics = request.mutable_metrics(); + + for (const auto& entry : data) { + const auto& view_descriptor = entry.first; + + auto metric = new opencensus::proto::metrics::v1::Metric(); + auto descriptor = metric->mutable_metric_descriptor(); + descriptor->set_name(view_descriptor.name()); + descriptor->set_description(view_descriptor.description()); + descriptor->set_unit(view_descriptor.measure_descriptor().units()); + switch (view_descriptor.aggregation().type()) { + case opencensus::stats::Aggregation::Type::kCount: { + descriptor->set_type(opencensus_proto::MetricDescriptor_Type::MetricDescriptor_Type_CUMULATIVE_INT64); + break; + } + + case opencensus::stats::Aggregation::Type::kSum: { + descriptor->set_type(opencensus_proto::MetricDescriptor_Type::MetricDescriptor_Type_CUMULATIVE_INT64); + break; + } + + case opencensus::stats::Aggregation::Type::kLastValue: { + descriptor->set_type(opencensus_proto::MetricDescriptor_Type::MetricDescriptor_Type_GAUGE_INT64); + break; + } + + case opencensus::stats::Aggregation::Type::kDistribution: { + descriptor->set_type(opencensus_proto::MetricDescriptor_Type::MetricDescriptor_Type_GAUGE_DISTRIBUTION); + break; + } + } + + auto label_keys = descriptor->mutable_label_keys(); + for (const auto& column : view_descriptor.columns()) { + auto label_key = new opencensus::proto::metrics::v1::LabelKey; + label_key->set_key(column.name()); + label_keys->AddAllocated(label_key); + } + + auto time_series = metric->mutable_timeseries(); + const auto& view_data = entry.second; + + auto start_times = view_data.start_times(); + + switch (view_data.type()) { + case opencensus::stats::ViewData::Type::kInt64: { + for (const auto& entry : view_data.int_data()) { + auto time_series_element = new opencensus::proto::metrics::v1::TimeSeries(); + + auto search = start_times.find(entry.first); + if (search != start_times.end()) { + absl::Time time = search->second; + time_series_element->mutable_start_timestamp()->CopyFrom( + google::protobuf::util::TimeUtil::TimeTToTimestamp(absl::ToTimeT(time))); + } + + auto label_values = time_series_element->mutable_label_values(); + for (const auto& value : entry.first) { + auto label_value = new opencensus::proto::metrics::v1::LabelValue; + label_value->set_value(value); + label_value->set_has_value(true); + label_values->AddAllocated(label_value); + } + + auto point = new opencensus::proto::metrics::v1::Point(); + point->set_int64_value(entry.second); + time_series_element->mutable_points()->AddAllocated(point); + + time_series->AddAllocated(time_series_element); + } + break; + } + case opencensus::stats::ViewData::Type::kDouble: { + for (const auto& entry : view_data.double_data()) { + auto time_series_element = new opencensus::proto::metrics::v1::TimeSeries(); + auto search = start_times.find(entry.first); + if (search != start_times.end()) { + absl::Time time = search->second; + time_series_element->mutable_start_timestamp()->CopyFrom( + google::protobuf::util::TimeUtil::TimeTToTimestamp(absl::ToTimeT(time))); + } + + auto label_values = time_series_element->mutable_label_values(); + for (const auto& value : entry.first) { + auto label_value = new opencensus::proto::metrics::v1::LabelValue; + label_value->set_value(value); + label_value->set_has_value(true); + label_values->AddAllocated(label_value); + } + + auto point = new opencensus::proto::metrics::v1::Point(); + point->set_double_value(entry.second); + time_series_element->mutable_points()->AddAllocated(point); + + time_series->AddAllocated(time_series_element); + } + break; + } + case opencensus::stats::ViewData::Type::kDistribution: { + for (const auto& entry : view_data.distribution_data()) { + auto time_series_element = new opencensus::proto::metrics::v1::TimeSeries(); + auto search = start_times.find(entry.first); + if (search != start_times.end()) { + absl::Time time = search->second; + time_series_element->mutable_start_timestamp()->CopyFrom( + google::protobuf::util::TimeUtil::TimeTToTimestamp(absl::ToTimeT(time))); + } + + auto label_values = time_series_element->mutable_label_values(); + for (const auto& value : entry.first) { + auto label_value = new opencensus::proto::metrics::v1::LabelValue; + label_value->set_value(value); + label_value->set_has_value(true); + label_values->AddAllocated(label_value); + } + auto point = new opencensus::proto::metrics::v1::Point(); + auto distribution_value = new opencensus::proto::metrics::v1::DistributionValue; + point->set_allocated_distribution_value(distribution_value); + time_series_element->mutable_points()->AddAllocated(point); + distribution_value->set_count(entry.second.count()); + distribution_value->set_sum_of_squared_deviation(entry.second.sum_of_squared_deviation()); + for (const auto& cnt : entry.second.bucket_counts()) { + auto bucket = new opencensus::proto::metrics::v1::DistributionValue::Bucket(); + bucket->set_count(cnt); + distribution_value->mutable_buckets()->AddAllocated(bucket); + } + + auto bucket_options = distribution_value->mutable_bucket_options(); + + for (const auto& boundary : entry.second.bucket_boundaries().lower_boundaries()) { + bucket_options->mutable_explicit_()->mutable_bounds()->Add(boundary); + } + + time_series->AddAllocated(time_series_element); + } + break; + } + } + + metrics->AddAllocated(metric); + } +} + +void OpencensusExporter::exportMetrics( + const std::vector>& data) { + opencensus::proto::agent::metrics::v1::ExportMetricsServiceRequest request; + wrap(data, request); + std::weak_ptr exporter{shared_from_this()}; + if (!bidi_reactor_) { + bidi_reactor_ = absl::make_unique(client_, exporter); + } + bidi_reactor_->write(request); +} + +void OpencensusExporter::resetStream() { + std::weak_ptr exporter{shared_from_this()}; + bidi_reactor_.reset(new MetricBidiReactor(client_, exporter)); +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/stats/PublishStats.cpp b/src/main/cpp/stats/PublishStats.cpp index 9356f980d..37ce613ad 100644 --- a/src/main/cpp/stats/PublishStats.cpp +++ b/src/main/cpp/stats/PublishStats.cpp @@ -16,6 +16,8 @@ */ #include "PublishStats.h" +#include "Tag.h" + ROCKETMQ_NAMESPACE_BEGIN PublishStats::PublishStats() @@ -27,15 +29,17 @@ PublishStats::PublishStats() .set_description("Number of messages published") .set_measure("publish_success") .set_aggregation(opencensus::stats::Aggregation::Sum()) - .add_column(topicTag()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) .RegisterForExport(); opencensus::stats::ViewDescriptor() .set_name("rocketmq_send_failure_total") .set_description("Number of publish failures") - .set_measure("pubish_failure") + .set_measure("publish_failure") .set_aggregation(opencensus::stats::Aggregation::Sum()) - .add_column(topicTag()) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) .RegisterForExport(); opencensus::stats::ViewDescriptor() @@ -43,14 +47,10 @@ PublishStats::PublishStats() .set_description("Publish latency") .set_measure("publish_latency") .set_aggregation(opencensus::stats::Aggregation::Distribution( - opencensus::stats::BucketBoundaries::Explicit({1, 10, 100, 1000}))) - .add_column(topicTag()) + opencensus::stats::BucketBoundaries::Explicit({5, 10, 20, 50, 500}))) + .add_column(Tag::topicTag()) + .add_column(Tag::clientIdTag()) .RegisterForExport(); } -opencensus::tags::TagKey& PublishStats::topicTag() { - static opencensus::tags::TagKey topic_tag = opencensus::tags::TagKey::Register("topic"); - return topic_tag; -} - ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/Tag.cpp b/src/main/cpp/stats/Tag.cpp new file mode 100644 index 000000000..959923346 --- /dev/null +++ b/src/main/cpp/stats/Tag.cpp @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Tag.h" + +ROCKETMQ_NAMESPACE_BEGIN + +opencensus::tags::TagKey& Tag::topicTag() { + static opencensus::tags::TagKey topic_tag = opencensus::tags::TagKey::Register("topic"); + return topic_tag; +} + +opencensus::tags::TagKey& Tag::clientIdTag() { + static opencensus::tags::TagKey client_id_tag = opencensus::tags::TagKey::Register("client_id"); + return client_id_tag; +} + +opencensus::tags::TagKey& Tag::userIdTag() { + static opencensus::tags::TagKey uid_tag = opencensus::tags::TagKey::Register("uid"); + return uid_tag; +} + +opencensus::tags::TagKey& Tag::deploymentTag() { + static opencensus::tags::TagKey deployment_tag = opencensus::tags::TagKey::Register("deployment"); + return deployment_tag; +} + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/stats/include/ConsumeStats.h b/src/main/cpp/stats/include/ConsumeStats.h new file mode 100644 index 000000000..a70f32467 --- /dev/null +++ b/src/main/cpp/stats/include/ConsumeStats.h @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/stats/stats.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class ConsumeStats { +public: + ConsumeStats(); + + const opencensus::stats::MeasureInt64& processSuccess() const { + return process_success_; + } + + const opencensus::stats::MeasureInt64& processFailure() const { + return process_failure_; + } + + const opencensus::stats::MeasureInt64& ackSuccess() const { + return ack_success_; + } + + const opencensus::stats::MeasureInt64& ackFailure() const { + return ack_failure_; + } + + const opencensus::stats::MeasureInt64& changeInvisibleTimeSuccess() const { + return change_invisible_time_success_; + } + + const opencensus::stats::MeasureInt64& changeInvisibleTimeFailure() const { + return change_invisible_time_failure_; + } + + const opencensus::stats::MeasureInt64& cachedMessageQuantity() const { + return cached_message_quantity_; + } + + const opencensus::stats::MeasureInt64& cachedMessageBytes() const { + return cached_message_bytes_; + } + + const opencensus::stats::MeasureInt64& deliveryLatency() const { + return delivery_latency_; + } + + const opencensus::stats::MeasureInt64& awaitTime() const { + return await_time_; + } + + const opencensus::stats::MeasureInt64& processTime() const { + return process_time_; + } + +private: + opencensus::stats::MeasureInt64 process_success_; + opencensus::stats::MeasureInt64 process_failure_; + opencensus::stats::MeasureInt64 ack_success_; + opencensus::stats::MeasureInt64 ack_failure_; + opencensus::stats::MeasureInt64 change_invisible_time_success_; + opencensus::stats::MeasureInt64 change_invisible_time_failure_; + opencensus::stats::MeasureInt64 cached_message_quantity_; + opencensus::stats::MeasureInt64 cached_message_bytes_; + + opencensus::stats::MeasureInt64 delivery_latency_; + opencensus::stats::MeasureInt64 await_time_; + opencensus::stats::MeasureInt64 process_time_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/include/Exporter.h b/src/main/cpp/stats/include/Exporter.h new file mode 100644 index 000000000..7f2f24a38 --- /dev/null +++ b/src/main/cpp/stats/include/Exporter.h @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/stats/stats.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Exporter { +public: + virtual void exportMetrics( + const std::vector>& data) = 0; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/include/MetricBidiReactor.h b/src/main/cpp/stats/include/MetricBidiReactor.h new file mode 100644 index 000000000..cc08c1d01 --- /dev/null +++ b/src/main/cpp/stats/include/MetricBidiReactor.h @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "Client.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/impl/codegen/client_callback.h" +#include "opencensus/proto/agent/metrics/v1/metrics_service.grpc.pb.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class OpencensusExporter; + +using ExportMetricsServiceRequest = opencensus::proto::agent::metrics::v1::ExportMetricsServiceRequest; +using ExportMetricsServiceResponse = opencensus::proto::agent::metrics::v1::ExportMetricsServiceResponse; + +class MetricBidiReactor : public grpc::ClientBidiReactor { +public: + MetricBidiReactor(std::weak_ptr client, std::weak_ptr exporter); + + /// Notifies the application that a StartRead operation completed. + /// + /// \param[in] ok Was it successful? If false, no new read/write operation + /// will succeed, and any further Start* should not be called. + void OnReadDone(bool /*ok*/) override; + + /// Notifies the application that a StartWrite or StartWriteLast operation + /// completed. + /// + /// \param[in] ok Was it successful? If false, no new read/write operation + /// will succeed, and any further Start* should not be called. + void OnWriteDone(bool /*ok*/) override; + + /// Notifies the application that all operations associated with this RPC + /// have completed and all Holds have been removed. OnDone provides the RPC + /// status outcome for both successful and failed RPCs and will be called in + /// all cases. If it is not called, it indicates an application-level problem + /// (like failure to remove a hold). + /// + /// \param[in] s The status outcome of this RPC + void OnDone(const grpc::Status& /*s*/) override; + + void write(ExportMetricsServiceRequest request) LOCKS_EXCLUDED(requests_mtx_); + +private: + std::weak_ptr client_; + std::weak_ptr exporter_; + + ExportMetricsServiceRequest request_; + + std::vector requests_ GUARDED_BY(requests_mtx_); + absl::Mutex requests_mtx_; + + std::atomic_bool inflight_{false}; + std::atomic_bool read_{false}; + + ExportMetricsServiceResponse response_; + + void fireWrite(); + + void fireRead(); +}; + +ROCKETMQ_NAMESPACE_END diff --git a/src/main/cpp/stats/include/OpencensusExporter.h b/src/main/cpp/stats/include/OpencensusExporter.h new file mode 100644 index 000000000..7920ff5c1 --- /dev/null +++ b/src/main/cpp/stats/include/OpencensusExporter.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "Client.h" +#include "Exporter.h" +#include "grpcpp/grpcpp.h" +#include "opencensus/proto/agent/metrics/v1/metrics_service.grpc.pb.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class MetricBidiReactor; + +using Stub = opencensus::proto::agent::metrics::v1::MetricsService::Stub; +using StubPtr = std::unique_ptr; +using MetricData = std::vector>; +using ExportMetricsServiceRequest = opencensus::proto::agent::metrics::v1::ExportMetricsServiceRequest; + +class OpencensusExporter : public Exporter, public std::enable_shared_from_this { +public: + OpencensusExporter(std::string endpoints, std::weak_ptr client); + + void exportMetrics(const MetricData& data) override; + + static void wrap(const MetricData& data, ExportMetricsServiceRequest& request); + + void resetStream(); + + Stub* stub() { + return stub_.get(); + } + +private: + std::weak_ptr client_; + StubPtr stub_; + std::unique_ptr bidi_reactor_; +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/include/PublishStats.h b/src/main/cpp/stats/include/PublishStats.h index 46305d0bf..d9d750afd 100644 --- a/src/main/cpp/stats/include/PublishStats.h +++ b/src/main/cpp/stats/include/PublishStats.h @@ -18,6 +18,7 @@ #include +#include "Tag.h" #include "opencensus/stats/stats.h" #include "rocketmq/RocketMQ.h" @@ -27,20 +28,18 @@ class PublishStats { public: PublishStats(); - opencensus::stats::MeasureInt64& success() { + const opencensus::stats::MeasureInt64& success() const { return success_; } - opencensus::stats::MeasureInt64& failure() { + const opencensus::stats::MeasureInt64& failure() const { return failure_; } - opencensus::stats::MeasureInt64& latency() { + const opencensus::stats::MeasureInt64& latency() const { return latency_; } - static opencensus::tags::TagKey& topicTag(); - private: opencensus::stats::MeasureInt64 success_; opencensus::stats::MeasureInt64 failure_; diff --git a/src/main/cpp/stats/include/Tag.h b/src/main/cpp/stats/include/Tag.h new file mode 100644 index 000000000..ae69fcb82 --- /dev/null +++ b/src/main/cpp/stats/include/Tag.h @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "opencensus/stats/stats.h" +#include "rocketmq/RocketMQ.h" + +ROCKETMQ_NAMESPACE_BEGIN + +class Tag { +public: + static opencensus::tags::TagKey& topicTag(); + + static opencensus::tags::TagKey& clientIdTag(); + + static opencensus::tags::TagKey& userIdTag(); + + static opencensus::tags::TagKey& deploymentTag(); +}; + +ROCKETMQ_NAMESPACE_END \ No newline at end of file diff --git a/src/main/cpp/stats/tests/PublishStatsTest.cpp b/src/main/cpp/stats/tests/PublishStatsTest.cpp index 609e63424..b6fb7d3af 100644 --- a/src/main/cpp/stats/tests/PublishStatsTest.cpp +++ b/src/main/cpp/stats/tests/PublishStatsTest.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "PublishStats.h" @@ -28,11 +29,33 @@ class Handler : public opencensus::stats::StatsExporter::Handler { public: void ExportViewData( const std::vector>& data) override { + std::cout << "================================================================================" << std::endl; for (const auto& datum : data) { const auto& view_data = datum.second; + const auto& descriptor = datum.first; + auto start_times = view_data.start_times(); + auto columns = descriptor.columns(); + switch (view_data.type()) { case opencensus::stats::ViewData::Type::kInt64: { - exportDatum(datum.first, view_data.start_time(), view_data.end_time(), view_data.int_data()); + auto data_map = view_data.int_data(); + for (const auto& entry : data_map) { + absl::Time time = start_times[entry.first]; + std::string line; + line.append(absl::FormatTime(time)).append(" "); + line.append(descriptor.name()); + line.append("{"); + for (std::size_t i = 0; i < columns.size(); i++) { + line.append(columns[i].name()).append("=").append(entry.first[i]); + if (i < columns.size() - 1) { + line.append(", "); + } else { + line.append("} ==> "); + } + } + line.append(std::to_string(entry.second)); + println(line); + } break; } case opencensus::stats::ViewData::Type::kDouble: { @@ -40,7 +63,22 @@ class Handler : public opencensus::stats::StatsExporter::Handler { break; } case opencensus::stats::ViewData::Type::kDistribution: { - exportDatum(datum.first, view_data.start_time(), view_data.end_time(), view_data.distribution_data()); + for (const auto& entry : view_data.distribution_data()) { + std::string line(descriptor.name()); + line.append("{"); + for (std::size_t i = 0; i < columns.size(); i++) { + line.append(columns[i].name()).append("=").append(entry.first[i]); + if (i < columns.size() - 1) { + line.append(", "); + } else { + line.append("} ==> "); + } + } + line.append(entry.second.DebugString()); + println(line); + + println(absl::StrJoin(entry.second.bucket_boundaries().lower_boundaries(), ",")); + } break; } } @@ -53,7 +91,7 @@ class Handler : public opencensus::stats::StatsExporter::Handler { absl::Time end_time, const opencensus::stats::ViewData::DataMap& data) { if (data.empty()) { - std::cout << "No data for " << descriptor.name() << std::endl; + // std::cout << "No data for " << descriptor.name() << std::endl; return; } @@ -65,6 +103,12 @@ class Handler : public opencensus::stats::StatsExporter::Handler { } } + std::mutex console_mtx; + void println(const std::string& line) { + std::lock_guard lk(console_mtx); + std::cout << line << std::endl; + } + // Functions to format data for different aggregation types. std::string DataToString(double data) { return absl::StrCat(": ", data, "\n"); @@ -87,12 +131,27 @@ TEST(StatsTest, testBasics) { std::string t1("T1"); std::string t2("T2"); PublishStats metrics; + opencensus::stats::StatsExporter::SetInterval(absl::Seconds(5)); opencensus::stats::StatsExporter::RegisterPushHandler(absl::make_unique()); - opencensus::stats::Record({{metrics.success(), 1}}, {{PublishStats::topicTag(), t1}}); - opencensus::stats::Record({{metrics.success(), 100}}, {{PublishStats::topicTag(), t2}}); - opencensus::stats::Record({{metrics.latency(), 100}}, {{PublishStats::topicTag(), t1}}); + std::atomic_bool stopped{false}; + auto generator = [&]() { + while (!stopped) { + opencensus::stats::Record({{metrics.success(), 1}}, {{Tag::topicTag(), t1}, {Tag::clientIdTag(), "client-0"}}); + opencensus::stats::Record({{metrics.success(), 100}}, {{Tag::topicTag(), t2}, {Tag::clientIdTag(), "client-0"}}); + for (std::size_t i = 0; i < 10; i++) { + opencensus::stats::Record({{metrics.latency(), i * 10}}, + {{Tag::topicTag(), t1}, {Tag::clientIdTag(), "client-0"}}); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + }; + std::thread feeder(generator); std::this_thread::sleep_for(std::chrono::seconds(10)); + stopped.store(true); + if (feeder.joinable()) { + feeder.join(); + } } ROCKETMQ_NAMESPACE_END From 7fc8d4f5380c9fedb106fa2b6a26edf6c4ea3c07 Mon Sep 17 00:00:00 2001 From: Li Zhanhui Date: Mon, 30 May 2022 15:07:32 +0800 Subject: [PATCH 133/133] Remove stages of running unit tests --- .github/workflows/main.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 72c1725fd..26c6d3b36 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,4 @@ jobs: if: matrix.os != 'windows' run: bazel -h - name: Compile All Targets - run: bazel build //... - - name: Run Unit Tests - run: bazel test --test_output=errors //src/test/cpp/ut/... + run: bazel build //... \ No newline at end of file