rocketmq-client-nodejs
Version:
RocketMQ Node.js Client
238 lines • 18.3 kB
JavaScript
"use strict";
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LiteSubscriptionManager = void 0;
const definition_pb_1 = require("../../proto/apache/rocketmq/v2/definition_pb");
const service_pb_1 = require("../../proto/apache/rocketmq/v2/service_pb");
const exception_1 = require("../exception");
const SYNC_LITE_SUBSCRIPTION_INTERVAL = 30000; // 30 seconds
/**
* Manages lite topic subscriptions for LitePushConsumer.
*
* <p>LiteSubscriptionManager handles:
* - Maintaining the set of subscribed lite topics
* - Periodic synchronization of lite subscriptions with server
* - Quota management and verification
* - Handling unsubscribe commands from server</p>
*/
class LiteSubscriptionManager {
consumerImpl;
bindTopic;
group;
liteTopicSet = new Set();
liteSubscriptionQuota;
maxLiteTopicSize;
syncTimer;
constructor(consumerImpl, bindTopic, group) {
this.consumerImpl = consumerImpl;
this.bindTopic = bindTopic;
this.group = group;
this.liteSubscriptionQuota = 100; // Default quota
this.maxLiteTopicSize = 64; // Default max length
}
/**
* Start periodic sync of lite subscriptions.
*/
startUp() {
// syncAll after startup for subscribeLite("*")
this.syncAllLiteSubscription();
this.syncTimer = setInterval(() => this.syncAllLiteSubscription(), SYNC_LITE_SUBSCRIPTION_INTERVAL);
}
/**
* Stop the sync timer.
*/
shutdown() {
// Clear sync timer
if (this.syncTimer) {
clearInterval(this.syncTimer);
this.syncTimer = undefined;
}
// Clear lite topic set to free memory
this.liteTopicSet.clear();
}
/**
* Get the bind topic name.
*/
getBindTopicName() {
return this.bindTopic.getName();
}
/**
* Get the consumer group name.
*/
getConsumerGroupName() {
return this.group.getName();
}
/**
* Get a copy of the lite topic set.
*/
getLiteTopicSet() {
return new Set(this.liteTopicSet);
}
/**
* Sync settings from server.
*/
sync(settings) {
if (!settings?.hasSubscription?.()) {
return;
}
const subscription = settings.getSubscription();
if (subscription.hasLiteSubscriptionQuota?.()) {
this.liteSubscriptionQuota = subscription.getLiteSubscriptionQuota();
}
if (subscription.hasMaxLiteTopicSize?.()) {
this.maxLiteTopicSize = subscription.getMaxLiteTopicSize();
}
}
/**
* Subscribe to a lite topic.
*/
async subscribeLite(liteTopic, offsetOption) {
// Check if consumer is running
if (!this.consumerImpl.isRunning()) {
throw new exception_1.ClientException(500, 'Consumer is not running');
}
// Already subscribed
if (this.liteTopicSet.has(liteTopic)) {
return;
}
// Validate lite topic
this.validateLiteTopic(liteTopic);
// Check quota
this.checkLiteSubscriptionQuota(1);
try {
await this.syncLiteSubscription(definition_pb_1.LiteSubscriptionAction.PARTIAL_ADD, [liteTopic], offsetOption ?? null);
this.liteTopicSet.add(liteTopic);
this.consumerImpl.getLogger().info('SubscribeLite %s, topic=%s, group=%s, clientId=%s', liteTopic, this.getBindTopicName(), this.getConsumerGroupName(), this.consumerImpl.clientId);
}
catch (error) {
this.consumerImpl.getLogger().error('Failed to subscribeLite %s, error=%s', liteTopic, error);
throw error;
}
}
/**
* Unsubscribe from a lite topic.
*/
async unsubscribeLite(liteTopic) {
// Check if consumer is running
if (!this.consumerImpl.isRunning()) {
throw new exception_1.ClientException(500, 'Consumer is not running');
}
// Not subscribed
if (!this.liteTopicSet.has(liteTopic)) {
return;
}
try {
await this.syncLiteSubscription(definition_pb_1.LiteSubscriptionAction.PARTIAL_REMOVE, [liteTopic], null);
this.liteTopicSet.delete(liteTopic);
this.consumerImpl.getLogger().info('UnsubscribeLite %s, topic=%s, group=%s, clientId=%s', liteTopic, this.getBindTopicName(), this.getConsumerGroupName(), this.consumerImpl.clientId);
}
catch (error) {
this.consumerImpl.getLogger().error('Failed to unsubscribeLite %s, error=%s', liteTopic, error);
throw error;
}
}
/**
* Periodic sync of all lite subscriptions.
*/
async syncAllLiteSubscription() {
try {
this.checkLiteSubscriptionQuota(0);
await this.syncLiteSubscription(definition_pb_1.LiteSubscriptionAction.COMPLETE_ADD, Array.from(this.liteTopicSet), null);
}
catch (error) {
this.consumerImpl.getLogger().error('Schedule syncAllLiteSubscription error, clientId=%s, error=%s', this.consumerImpl.clientId, error);
}
}
/**
* Sync lite subscription with server.
*/
async syncLiteSubscription(action, liteTopics, offsetOption) {
const logger = this.consumerImpl.getLogger();
if (logger.debug) {
logger.debug('SyncLiteSubscription: action=%s, liteTopics=[%s], offsetOption=%s', definition_pb_1.LiteSubscriptionAction[action], liteTopics.join(', '), offsetOption?.toString() ?? 'null');
}
// Build the request
const request = new service_pb_1.SyncLiteSubscriptionRequest()
.setAction(action)
.setTopic(this.bindTopic.toProtobuf())
.setGroup(this.group.toProtobuf())
.setLiteTopicSetList(liteTopics);
// Set offset option if provided
if (offsetOption) {
request.setOffsetOption(offsetOption.toProtobuf());
}
try {
// Call RPC client using public methods from LitePushConsumerImpl
const response = await this.consumerImpl.getRpcClientManager().syncLiteSubscription(this.consumerImpl.getEndpoints(), request, this.consumerImpl.getRequestTimeout());
// Handle response status
const status = response.getStatus();
if (status && status.getCode() !== definition_pb_1.Code.OK) {
throw new exception_1.ClientException(status.getCode(), `Failed to sync lite subscription: ${status.getMessage()}`);
}
if (logger.info) {
logger.info('SyncLiteSubscription success: action=%s, liteTopics=[%s], clientId=%s', definition_pb_1.LiteSubscriptionAction[action], liteTopics.join(', '), this.consumerImpl.clientId);
}
}
catch (error) {
if (logger.error) {
logger.error('SyncLiteSubscription failed: action=%s, liteTopics=[%s], clientId=%s, error=%s', definition_pb_1.LiteSubscriptionAction[action], liteTopics.join(', '), this.consumerImpl.clientId, error);
}
throw error;
}
}
/**
* Handle notify unsubscribe lite command from server.
*/
onNotifyUnsubscribeLiteCommand(command) {
const liteTopic = command.getLiteTopic();
this.consumerImpl.getLogger().info('Notify unsubscribe lite: liteTopic=%s, group=%s, bindTopic=%s', liteTopic, this.getConsumerGroupName(), this.getBindTopicName());
if (liteTopic && liteTopic.trim().length > 0) {
this.liteTopicSet.delete(liteTopic);
}
}
/**
* Validate lite topic name.
*/
validateLiteTopic(liteTopic, maxLength) {
const maxLen = maxLength ?? this.maxLiteTopicSize;
if (!liteTopic || liteTopic.trim().length === 0) {
throw new Error('liteTopic is blank');
}
if (liteTopic.length > maxLen) {
throw new Error(`liteTopic length exceeded max length ${maxLen}, liteTopic: ${liteTopic}`);
}
}
/**
* Check lite subscription quota.
*/
checkLiteSubscriptionQuota(delta) {
if (this.liteTopicSet.size + delta > this.liteSubscriptionQuota) {
throw new exception_1.ClientException(429, `Lite subscription quota exceeded ${this.liteSubscriptionQuota}`);
}
}
/**
* Returns a string representation of this LiteSubscriptionManager.
*
* @return String representation
*/
toString() {
return `LiteSubscriptionManager{bindTopic=${this.bindTopic.getName()}, group=${this.group.getName()}, liteTopicSet=[${Array.from(this.liteTopicSet).join(', ')}], liteSubscriptionQuota=${this.liteSubscriptionQuota}, maxLiteTopicSize=${this.maxLiteTopicSize}}`;
}
}
exports.LiteSubscriptionManager = LiteSubscriptionManager;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTGl0ZVN1YnNjcmlwdGlvbk1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uc3VtZXIvTGl0ZVN1YnNjcmlwdGlvbk1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRzs7O0FBRUgsZ0ZBQTRGO0FBQzVGLDBFQUltRDtBQUNuRCw0Q0FBK0M7QUFLL0MsTUFBTSwrQkFBK0IsR0FBRyxLQUFLLENBQUMsQ0FBQyxhQUFhO0FBRTVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBYSx1QkFBdUI7SUFDakIsWUFBWSxDQUF1QjtJQUNuQyxTQUFTLENBQVc7SUFDcEIsS0FBSyxDQUFXO0lBQ2hCLFlBQVksR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBQzFDLHFCQUFxQixDQUFTO0lBQzlCLGdCQUFnQixDQUFTO0lBQ3pCLFNBQVMsQ0FBa0I7SUFFbkMsWUFDRSxZQUFrQyxFQUNsQyxTQUFtQixFQUNuQixLQUFlO1FBRWYsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDM0IsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLHFCQUFxQixHQUFHLEdBQUcsQ0FBQyxDQUFDLGdCQUFnQjtRQUNsRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsRUFBRSxDQUFDLENBQUMscUJBQXFCO0lBQ25ELENBQUM7SUFFRDs7T0FFRztJQUNJLE9BQU87UUFDWiwrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7UUFDL0IsSUFBSSxDQUFDLFNBQVMsR0FBRyxXQUFXLENBQzFCLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxFQUNwQywrQkFBK0IsQ0FDaEMsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNJLFFBQVE7UUFDYixtQkFBbUI7UUFDbkIs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