[jboss-jira] [JBoss JIRA] (JGRP-2218) New payload interface
Dan Berindei (JIRA)
issues at jboss.org
Wed Sep 13 11:59:00 EDT 2017
[ https://issues.jboss.org/browse/JGRP-2218?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13463582#comment-13463582 ]
Dan Berindei edited comment on JGRP-2218 at 9/13/17 11:58 AM:
--------------------------------------------------------------
{quote}
Yes, as Payload.getInput() returns an input stream, you have to produce the ref to the stream anyway and probably already store it in the ObjectPayload instance.
{quote}
What's the use case for {{Payload.getInput()}}?
My concern is that when {{Payload.readFrom(DataInput)}} is called, the {{DataInput}} could be a wrapper on a TCP socket's {{InputStream}}. In that case it's definitely not safe to keep a reference to the {{DataInput}} around and to read from it after {{readFrom()}} finishes.
{quote}
I was thinking of using Bits.sizeUTF() which I copied from somewhere...
{quote}
{{Bits.sizeUTF()}} touches all the characters in the string, so it's O\(n\). That's why in our marshaller implementation we write the UTF8-encoded bytes first and then [go back to write the number of bytes at the beginning|https://github.com/infinispan/infinispan/blob/f288c948c3ca33e7a2ef47f3e8137d641bd95c07/core/src/main/java/org/infinispan/marshall/core/BytesObjectOutput.java#L209]. Although in this particular case I think we could have written the number of UTF16 code units instead of the number of bytes.
was (Author: dan.berindei):
{quote}
Yes, as Payload.getInput() returns an input stream, you have to produce the ref to the stream anyway and probably already store it in the ObjectPayload instance.
{quote}
What's the use case for {{Payload.getInput()}}?
My concern is that when {{Payload.readFrom(DataInput)}} is called, the {{DataInput}} could be a wrapper on a TCP socket's {{InputStream}}. In that case it's definitely not safe to keep a reference to the {{DataInput}} around and to read from it after {{readFrom()}} finishes.
{quote}
I was thinking of using Bits.sizeUTF() which I copied from somewhere...
{quote}
{{Bits.sizeUTF()}} touches all the characters in the string, so it's O(n). That's why in our marshaller implementation we write the UTF8-encoded bytes first and then [go back to write the number of bytes at the beginning|https://github.com/infinispan/infinispan/blob/f288c948c3ca33e7a2ef47f3e8137d641bd95c07/core/src/main/java/org/infinispan/marshall/core/BytesObjectOutput.java#L209]. Although in this particular case I think we could have written the number of UTF16 code units instead of the number of bytes.
> New payload interface
> ---------------------
>
> Key: JGRP-2218
> URL: https://issues.jboss.org/browse/JGRP-2218
> Project: JGroups
> Issue Type: Feature Request
> Reporter: Bela Ban
> Assignee: Bela Ban
> Fix For: 5.0
>
>
> h3. Goal
> Change payload in {{Message}} from byte[] arrays to a {{Payload}} interface which can have multiple implementations.
> h3. Reason
> Currently, having to pass a byte[] array to a message leads to unnecessary copying:
> * When an application has a ref to an NIO (direct) {{ByteBuffer}}, the bytes in the byte buffer have to be copied into a byte[] array and then set in the message
> * When the application sends around byte[] arrays, but also wants to add some additional metadata, e.g. type (1000-byte requests/responses), it needs to create a new byte[] array of (say) 1001 bytes and copy the data (1000 bytes) plus the request type (1 byte) into the new copy. Example: {{MPerf}} and {{UPerf}}
> * When an object has to be sent (e.g. in Infinispan), the object has to be marshalled into a byte[] array (first allocation) and then added to the message. With the suggested {{ObjectPayload}} (below), marshalling of the object would occur late, and it would be marshalled directly into the output stream of the bundler, eliminating the byte[] array allocation made by the application.
> h3. Design
> Instead of copying, the application creates an instance of {{Payload}} and sets the payload in {{Message}}. The {{Payload}} is then passed all the way down into the transport where it is marshalled and sent. There can be a number of payload implementations, e.g.
> * {{ArrayPayload}}: wraps a byte[] array with an offset and length
> * {{NioDirectPayload}}: wraps an NIO direct {{ByteBuffer}}
> * {{NioHeapPayload}}: wraps an NIO heap-based {{ByteBuffer}}
> * {{CompositePayload}}: wraps multiple Buffers. E.g. type (1 byte) and data (1000 bytes) as described above
> * {{IntPayload}}: a single integer
> * {{ObjectPayload}}: has an Object and a ClassLoader (for reading), plus a Marshaller which know how to marshal the object, this allows for objects to be passed in payloads and they're only marshalled at the end (transport).
> * {{PartialPayload}}: a ref to a {{Payload}}, with an offset and length
> The {{Payload}} interface has methods:
> * {{size()}}
> * {{writeTo(DataOutput)}}
> * {{readFrom(DataInput)}}
> * {{getInput()}}: this provides a {{DataInput}} stream for reading from the underlying payload
> and possibly also
> * {{acquire()}} and
> * {{release()}} (for ref-counting)
> * {{copy()}}
> Each payload impl has an ID and it should be possible to register new impls. A {{PayloadFactory}} maintains a mapping between IDs and impl classes.
> When marshalling a {{Payload}}, the ID is written first, followed by the payload's {{writeTo()}} method. When reading payloads, the {{PayloadFactory}} is used to create instances from IDs.
> h4. Fragmentation
> When fragmenting a buffer, the fragments are instances of {{PartialPayload}} which maintains an offset and length over an underlying payload. When marshalling a {{PartialPayload}}, only the part between offset and offset+length is written to the output stream.
> h4. Reference counting
> If we implement ref-counting, then payloads can be reused as soon as the ref-count is 0. For example, when sending a message, the payload's ref-count could be incremented by the app calling {{acquire()}}. (Assuming the message is a unicast message), {{UNICAST3}} would increment the count to 2. This is needed because {{UNICAST3}} might have to retransmit the message if it was lost on the network, and meanwhile the payload cannot be reused (changed). The app calls {{release()}} when the {{JChannel.send()}} call returns, but the payload cannot be reused until {{UNICAST3}} calls {{release()}} as well. This will happen when an {{ACK}} for the given message has been received.
> h4. Payload management
> When a request is received, the buffer is created from the bytes received on the network, based on the ID. This should be done by asking a {{PayloadManagement}} (or {{PayloadPool}} component for a new buffer. A naive implementation might create a new buffer every time, and more sophisticated one might use a pool of payloads.
> The {{PayloadManagement}} instance could be replaced by one's own implementation; this allows for an application to control the lifecycle of payloads: thus the creation of buffers by the application and of payloads received over the network can be controlled by the same payload management impl.
> h4. Misc
> * Since this issue includes API changes, the version will be 5.0
--
This message was sent by Atlassian JIRA
(v7.2.3#72005)
More information about the jboss-jira
mailing list