-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue 2912][pulsar-admin] add get-message-by-id cmd into pulsar-admin #6331
Changes from 4 commits
25df99e
532a76d
feced50
a7ff070
ec12604
7f6d4b1
5ebd9c9
9d1c1e6
882db05
b475e8f
f772687
b7e89a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -20,6 +20,13 @@ | |||
|
||||
import static com.google.common.base.Preconditions.checkNotNull; | ||||
import static org.apache.pulsar.broker.cache.ConfigurationCacheService.POLICIES; | ||||
import org.apache.bookkeeper.mledger.AsyncCallbacks; | ||||
import org.apache.bookkeeper.mledger.Entry; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerConfig; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerException; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerInfo; | ||||
import org.apache.bookkeeper.mledger.Position; | ||||
import org.apache.bookkeeper.mledger.impl.ManagedLedgerImpl; | ||||
import org.apache.pulsar.common.api.proto.PulsarApi; | ||||
import static org.apache.pulsar.common.util.Codec.decode; | ||||
|
||||
|
@@ -31,7 +38,6 @@ | |||
import io.netty.buffer.ByteBuf; | ||||
|
||||
import java.io.IOException; | ||||
import java.io.OutputStream; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import java.util.Map; | ||||
|
@@ -52,11 +58,6 @@ | |||
import javax.ws.rs.core.StreamingOutput; | ||||
|
||||
import org.apache.bookkeeper.mledger.AsyncCallbacks.ManagedLedgerInfoCallback; | ||||
import org.apache.bookkeeper.mledger.Entry; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerConfig; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerException; | ||||
import org.apache.bookkeeper.mledger.ManagedLedgerInfo; | ||||
import org.apache.bookkeeper.mledger.Position; | ||||
import org.apache.bookkeeper.mledger.impl.ManagedLedgerFactoryImpl; | ||||
import org.apache.bookkeeper.mledger.impl.ManagedLedgerOfflineBacklog; | ||||
import org.apache.bookkeeper.mledger.impl.PositionImpl; | ||||
|
@@ -1403,21 +1404,51 @@ protected void internalResetCursorOnPosition(String subName, boolean authoritati | |||
} | ||||
} | ||||
|
||||
protected Response internalPeekNthMessage(String subName, int messagePosition, boolean authoritative) { | ||||
if (topicName.isGlobal()) { | ||||
validateGlobalNamespaceOwnership(namespaceName); | ||||
} | ||||
PartitionedTopicMetadata partitionMetadata = getPartitionedTopicMetadata(topicName, authoritative, false); | ||||
if (partitionMetadata.partitions > 0) { | ||||
throw new RestException(Status.METHOD_NOT_ALLOWED, "Peek messages on a partitioned topic is not allowed"); | ||||
protected Response internalGetMessageById(long ledgerId, long entryId, boolean authoritative) { | ||||
verifyReadOperation(authoritative); | ||||
|
||||
PersistentTopic topic = (PersistentTopic) getTopicReference(topicName); | ||||
ManagedLedgerImpl ledger = (ManagedLedgerImpl) topic.getManagedLedger(); | ||||
Entry entry = null; | ||||
try { | ||||
CompletableFuture<Entry> future = new CompletableFuture<>(); | ||||
ledger.asyncReadEntry(new PositionImpl(ledgerId, entryId), new AsyncCallbacks.ReadEntryCallback() { | ||||
@Override | ||||
public void readEntryFailed(ManagedLedgerException exception, Object ctx) { | ||||
future.completeExceptionally(exception); | ||||
} | ||||
|
||||
@Override | ||||
public void readEntryComplete(Entry entry, Object ctx) { | ||||
future.complete(entry); | ||||
} | ||||
}, null); | ||||
|
||||
entry = future.get(1000, TimeUnit.MILLISECONDS); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for being late in reviewing this pull request. I would suggest implementing this using AsyncResponse. We have tried to move away from using sync methods. You can check pulsar/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java Line 1305 in df15210
|
||||
return generateResponseWithEntry(entry); | ||||
} catch (NullPointerException npe) { | ||||
throw new RestException(Status.NOT_FOUND, "Message not found"); | ||||
} catch (Exception exception) { | ||||
log.error("[{}] Failed to get message with ledgerId {} entryId {} from {}", | ||||
clientAppId(), ledgerId, entryId, topicName, exception); | ||||
throw new RestException(exception); | ||||
} finally { | ||||
if (entry != null) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move this |
||||
entry.release(); | ||||
} | ||||
} | ||||
} | ||||
|
||||
protected Response internalPeekNthMessage(String subName, int messagePosition, boolean authoritative) { | ||||
verifyReadOperation(authoritative); | ||||
validateAdminAccessForSubscriber(subName, authoritative); | ||||
if (!(getTopicReference(topicName) instanceof PersistentTopic)) { | ||||
log.error("[{}] Not supported operation of non-persistent topic {} {}", clientAppId(), topicName, | ||||
subName); | ||||
throw new RestException(Status.METHOD_NOT_ALLOWED, | ||||
"Skip messages on a non-persistent topic is not allowed"); | ||||
} | ||||
|
||||
PersistentTopic topic = (PersistentTopic) getTopicReference(topicName); | ||||
PersistentReplicator repl = null; | ||||
PersistentSubscription sub = null; | ||||
|
@@ -1433,48 +1464,7 @@ protected Response internalPeekNthMessage(String subName, int messagePosition, b | |||
} else { | ||||
entry = sub.peekNthMessage(messagePosition).get(); | ||||
} | ||||
checkNotNull(entry); | ||||
PositionImpl pos = (PositionImpl) entry.getPosition(); | ||||
ByteBuf metadataAndPayload = entry.getDataBuffer(); | ||||
|
||||
// moves the readerIndex to the payload | ||||
MessageMetadata metadata = Commands.parseMessageMetadata(metadataAndPayload); | ||||
|
||||
ResponseBuilder responseBuilder = Response.ok(); | ||||
responseBuilder.header("X-Pulsar-Message-ID", pos.toString()); | ||||
for (KeyValue keyValue : metadata.getPropertiesList()) { | ||||
responseBuilder.header("X-Pulsar-PROPERTY-" + keyValue.getKey(), keyValue.getValue()); | ||||
} | ||||
if (metadata.hasPublishTime()) { | ||||
responseBuilder.header("X-Pulsar-publish-time", DateFormatter.format(metadata.getPublishTime())); | ||||
} | ||||
if (metadata.hasEventTime()) { | ||||
responseBuilder.header("X-Pulsar-event-time", DateFormatter.format(metadata.getEventTime())); | ||||
} | ||||
if (metadata.hasNumMessagesInBatch()) { | ||||
responseBuilder.header("X-Pulsar-num-batch-message", metadata.getNumMessagesInBatch()); | ||||
} | ||||
|
||||
// Decode if needed | ||||
CompressionCodec codec = CompressionCodecProvider.getCompressionCodec(metadata.getCompression()); | ||||
ByteBuf uncompressedPayload = codec.decode(metadataAndPayload, metadata.getUncompressedSize()); | ||||
|
||||
// Copy into a heap buffer for output stream compatibility | ||||
ByteBuf data = PulsarByteBufAllocator.DEFAULT.heapBuffer(uncompressedPayload.readableBytes(), | ||||
uncompressedPayload.readableBytes()); | ||||
data.writeBytes(uncompressedPayload); | ||||
uncompressedPayload.release(); | ||||
|
||||
StreamingOutput stream = new StreamingOutput() { | ||||
|
||||
@Override | ||||
public void write(OutputStream output) throws IOException, WebApplicationException { | ||||
output.write(data.array(), data.arrayOffset(), data.readableBytes()); | ||||
data.release(); | ||||
} | ||||
}; | ||||
|
||||
return responseBuilder.entity(stream).build(); | ||||
return generateResponseWithEntry(entry); | ||||
} catch (NullPointerException npe) { | ||||
throw new RestException(Status.NOT_FOUND, "Message not found"); | ||||
} catch (Exception exception) { | ||||
|
@@ -1488,6 +1478,57 @@ public void write(OutputStream output) throws IOException, WebApplicationExcepti | |||
} | ||||
} | ||||
|
||||
private void verifyReadOperation(boolean authoritative) { | ||||
if (topicName.isGlobal()) { | ||||
validateGlobalNamespaceOwnership(namespaceName); | ||||
} | ||||
PartitionedTopicMetadata partitionMetadata = getPartitionedTopicMetadata(topicName, authoritative, false); | ||||
if (partitionMetadata.partitions > 0) { | ||||
throw new RestException(Status.METHOD_NOT_ALLOWED, "Peek messages on a partitioned topic is not allowed"); | ||||
} | ||||
} | ||||
|
||||
private Response generateResponseWithEntry(Entry entry) throws IOException { | ||||
checkNotNull(entry); | ||||
PositionImpl pos = (PositionImpl) entry.getPosition(); | ||||
ByteBuf metadataAndPayload = entry.getDataBuffer(); | ||||
|
||||
// moves the readerIndex to the payload | ||||
MessageMetadata metadata = Commands.parseMessageMetadata(metadataAndPayload); | ||||
|
||||
ResponseBuilder responseBuilder = Response.ok(); | ||||
responseBuilder.header("X-Pulsar-Message-ID", pos.toString()); | ||||
for (KeyValue keyValue : metadata.getPropertiesList()) { | ||||
responseBuilder.header("X-Pulsar-PROPERTY-" + keyValue.getKey(), keyValue.getValue()); | ||||
} | ||||
if (metadata.hasPublishTime()) { | ||||
responseBuilder.header("X-Pulsar-publish-time", DateFormatter.format(metadata.getPublishTime())); | ||||
} | ||||
if (metadata.hasEventTime()) { | ||||
responseBuilder.header("X-Pulsar-event-time", DateFormatter.format(metadata.getEventTime())); | ||||
} | ||||
if (metadata.hasNumMessagesInBatch()) { | ||||
responseBuilder.header("X-Pulsar-num-batch-message", metadata.getNumMessagesInBatch()); | ||||
} | ||||
|
||||
// Decode if needed | ||||
CompressionCodec codec = CompressionCodecProvider.getCompressionCodec(metadata.getCompression()); | ||||
ByteBuf uncompressedPayload = codec.decode(metadataAndPayload, metadata.getUncompressedSize()); | ||||
|
||||
// Copy into a heap buffer for output stream compatibility | ||||
ByteBuf data = PulsarByteBufAllocator.DEFAULT.heapBuffer(uncompressedPayload.readableBytes(), | ||||
uncompressedPayload.readableBytes()); | ||||
data.writeBytes(uncompressedPayload); | ||||
uncompressedPayload.release(); | ||||
|
||||
StreamingOutput stream = output -> { | ||||
output.write(data.array(), data.arrayOffset(), data.readableBytes()); | ||||
data.release(); | ||||
}; | ||||
|
||||
return responseBuilder.entity(stream).build(); | ||||
} | ||||
|
||||
protected PersistentOfflineTopicStats internalGetBacklog(boolean authoritative) { | ||||
if (topicName.isGlobal()) { | ||||
validateGlobalNamespaceOwnership(namespaceName); | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed any more.