public Map<String, ConsumerLag> getSubjectConsumerLag(final String subject) { final Map<String, ConsumeQueue> consumeQueues = storage.locateSubjectConsumeQueues(subject); final Map<String, ConsumerLag> lags = new HashMap<>(); for (final Map.Entry<String, ConsumeQueue> entry : consumeQueues.entrySet()) { final String group = entry.getKey(); final ConsumeQueue consumeQueue = entry.getValue(); final ConsumerGroupProgress progress = storage.getConsumerGroupProgress(subject, group); final long pullLag = consumeQueue.getQueueCount(); final long ackLag = computeAckLag(progress); lags.put(group, new ConsumerLag(pullLag, ackLag)); } return lags; }
private void waitUntilSyncDone() { final CheckpointManager checkpointManager = storage.getCheckpointManager(); final long messageCheckpointOffset = checkpointManager.getMessageCheckpointOffset(); final long actionCheckpointOffset = checkpointManager.getActionCheckpointOffset(); while (true) { final long maxMessageOffset = storage.getMaxMessageOffset(); final long maxActionLogOffset = storage.getMaxActionLogOffset(); if (maxMessageOffset >= messageCheckpointOffset && maxActionLogOffset >= actionCheckpointOffset) { return; } LOG.info("waiting log sync done ..."); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException ignore) { LOG.debug("sleep interrupted"); } } }
private void loadFromConsumerGroupProgresses(final ConcurrentMap<String, ConcurrentMap<ConsumerGroup, ConsumerSequence>> result) { final Collection<ConsumerGroupProgress> progresses = storage.allConsumerGroupProgresses(); progresses.forEach(progress -> { final Map<String, ConsumerProgress> consumers = progress.getConsumers(); if (consumers == null || consumers.isEmpty()) { return; } consumers.values().forEach(consumer -> putConsumer(result, consumer)); }); }
@Override public SyncRequest getRequest() { long messageLogMaxOffset = storage.getMaxMessageOffset(); long actionLogMaxOffset = storage.getMaxActionLogOffset(); return new SyncRequest(SyncType.heartbeat.getCode(), messageLogMaxOffset, actionLogMaxOffset); } }
private void cleanPullLogAndCheckpoint() { final Table<String, String, PullLog> pullLogs = storage.allPullLogs(); if (pullLogs == null || pullLogs.size() == 0) return; // delete all pull log without max pulled message sequence for (final String groupAndSubject : pullLogs.columnKeySet()) { final GroupAndSubject gs = GroupAndSubject.parse(groupAndSubject); final long maxPulledMessageSequence = storage.getMaxPulledMessageSequence(gs.getSubject(), gs.getGroup()); if (maxPulledMessageSequence == -1) { for (final Map.Entry<String, PullLog> entry : pullLogs.column(groupAndSubject).entrySet()) { final String consumerId = entry.getKey(); LOG.info("remove pull log. subject: {}, group: {}, consumerId: {}", gs.getSubject(), gs.getGroup(), consumerId); storage.destroyPullLog(gs.getSubject(), gs.getGroup(), consumerId); } } } }
private long getLastMaxSequence(final String subject, final String group) { final ConsumerGroupProgress progress = storage.getConsumerGroupProgress(subject, group); if (progress == null) { return -1; } else { return progress.getPull(); } }
@Override public SyncRequest getRequest() { final long actionLogMaxOffset = storage.getMaxActionLogOffset(); return new SyncRequest(SyncType.action.getCode(), 0, actionLogMaxOffset); } }
@Override public SyncRequest getRequest() { final long messageLogMaxOffset = storage.getMaxMessageOffset(); return new SyncRequest(SyncType.message.getCode(), messageLogMaxOffset, 0L); } }
private void putNeedRetryMessages(String subject, String group, String consumerId, List<SegmentBuffer> needRetryMessages) { try { for (SegmentBuffer buffer : needRetryMessages) { final ByteBuf message = Unpooled.wrappedBuffer(buffer.getBuffer()); final RawMessage rawMessage = QMQSerializer.deserializeRawMessage(message); if (!RetrySubjectUtils.isRetrySubject(subject)) { final String retrySubject = RetrySubjectUtils.buildRetrySubject(subject, group); rawMessage.setSubject(retrySubject); } final PutMessageResult putMessageResult = storage.appendMessage(rawMessage); if (putMessageResult.getStatus() != PutMessageStatus.SUCCESS) { LOG.error("put message error, consumer:{} {} {}, status:{}", subject, group, consumerId, putMessageResult.getStatus()); throw new RuntimeException("put retry message error"); } } } finally { needRetryMessages.forEach(SegmentBuffer::release); } QMon.putNeedRetryMessagesCountInc(subject, group, needRetryMessages.size()); }
@Override public void appendLogs(long startOffset, ByteBuf body) { storage.appendMessageLogData(startOffset, body.nioBuffer()); }
@Override public void appendLogs(long startOffset, ByteBuf body) { storage.appendActionLogData(startOffset, body.nioBuffer()); }
private boolean noPullLog(String subject, String group, String consumerId) { Table<String, String, PullLog> pullLogs = storage.allPullLogs(); Map<String, PullLog> subscribers = pullLogs.row(consumerId); if (subscribers == null || subscribers.isEmpty()) return true; return subscribers.get(GroupAndSubject.groupAndSubject(subject, group)) == null; }
private void handleGroupOffline(final Subscriber lastSubscriber) { try { storage.disableLagMonitor(lastSubscriber.getSubject(), lastSubscriber.getGroup()); } catch (Throwable e) { LOG.error("disable monitor error", e); } // TODO(keli.wang): how to detect group offline if master and slave's subscriber list is different }
private void foreverOffline(final Action action) { final String subject = action.subject(); final String group = action.group(); final String consumerId = action.consumerId(); LOG.info("execute offline task, will remove pull log and checkpoint entry for {}/{}/{}", subject, group, consumerId); storage.destroyPullLog(subject, group, consumerId); } }
@Override public void process(SyncRequestEntry requestEntry) { final ChannelHandlerContext ctx = requestEntry.getCtx(); final long messageLogMaxOffset = storage.getMaxMessageOffset(); final long actionLogMaxOffset = storage.getMaxActionLogOffset(); slaveMessageLogLag = messageLogMaxOffset - requestEntry.getSyncRequest().getMessageLogOffset(); slaveActionLogLag = actionLogMaxOffset - requestEntry.getSyncRequest().getActionLogOffset(); final HeartbeatPayloadHolder payloadHolder = new HeartbeatPayloadHolder(messageLogMaxOffset, actionLogMaxOffset); final Datagram datagram = RemotingBuilder.buildResponseDatagram(CommandCode.SUCCESS, requestEntry.getRequestHeader(), payloadHolder); ctx.writeAndFlush(datagram); }
public ReceiveResult putMessage(final ReceivingMessage message) { final RawMessage rawMessage = message.getMessage(); final MessageHeader header = rawMessage.getHeader(); final String msgId = header.getMessageId(); final long start = System.currentTimeMillis(); try { final PutMessageResult putMessageResult = storage.appendMessage(rawMessage); final PutMessageStatus status = putMessageResult.getStatus(); if (status != PutMessageStatus.SUCCESS) { LOG.error("put message error, message:{} {}, status:{}", header.getSubject(), msgId, status.name()); QMon.storeMessageErrorCountInc(header.getSubject()); return new ReceiveResult(msgId, MessageProducerCode.STORE_ERROR, status.name(), -1); } AppendMessageResult<MessageSequence> result = putMessageResult.getResult(); final long endOffsetOfMessage = result.getWroteOffset() + result.getWroteBytes(); return new ReceiveResult(msgId, MessageProducerCode.SUCCESS, "", endOffsetOfMessage); } catch (Throwable e) { LOG.error("put message error, message:{} {}", header.getSubject(), header.getMessageId(), e); QMon.storeMessageErrorCountInc(header.getSubject()); return new ReceiveResult(msgId, MessageProducerCode.STORE_ERROR, "", -1); } finally { QMon.putMessageTime(header.getSubject(), System.currentTimeMillis() - start); } }
private void initSubscribers() { final Collection<ConsumerGroupProgress> progresses = storage.allConsumerGroupProgresses(); progresses.forEach(progress -> { if (progress.isBroadcast()) { return; } progress.getConsumers().values().forEach(consumer -> { final String groupAndSubject = GroupAndSubject.groupAndSubject(consumer.getSubject(), consumer.getGroup()); addSubscriber(groupAndSubject, consumer.getConsumerId()); }); }); }