Discussion:
Storing message off heap, message compression, and storing the whole message as headers.
Kevin Burton
2015-04-20 00:05:54 UTC
Permalink
I’ve been thinking about how messages are stored in the broker and ways to
improve the storage in memory.

First, right now, messages are stored in the same heap, and if you’re using
the memory store, like, that’s going to add up. This will increase GC
latency , and you actually need 2x more memory because you have to have
temp memory set aside for GCs.

I was thinking about using Chronicle to store the messages off heap using
direct buffers. The downside to this is that the messages need to be
serialized/deserialized with each access. But realistically that’s probably
acceptable because you can do something like 1M message deserializations
per second. Which is normally more than the throughput of the broker.

Additionally, chronicle supports zlib or snappy compression on the message
bodies. So, while the broker supports message compression now, it doesn’t
support this feature on headers.

This would give us header compression!

The broker would transparently decompress the headers when reading the
message.

This then begs the question, why use message bodies at all? Why not just
store an entire message as a set of headers?

If you need hierarchy you can do foo.bar.cat.dog style header names.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Tim Bain
2015-04-20 13:24:44 UTC
Permalink
I'm confused about what would drive the need for this.

Is it the ability to hold more messages than your JVM size allows? If so,
we already have both KahaDB and LevelDB; what does Chronicle offer that
those other two don't?

Is it because you see some kind of inefficiency in how ActiveMQ uses memory
or how the JVM's GC strategies work? If so, can you elaborate on what
you're concerned about? (You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing", which doesn't match my experience at all. I've observed G1GC
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC. Please either correct my
interpretation of what your statement or provide the data you're basing it
on.)

One difference in GC behavior with what you're proposing is that under your
algorithm you'd GC each message at least twice (once when it's received and
put into Chronicle, and once when it's pulled from Chronicle and sent
onward, plus any additional reads needed to operate on the message such as
if a new subscriber with a non-matching selector connected to the broker)
instead of just once under the current algorithm. On the other hand, your
GCs should all be from Young Gen (and cheap) whereas the current algorithm
would likely push many of its messages to Old Gen. Old Gen GCs are more
expensive under ParallelGC, though they're no worse under G1GC and CMS. So
it's a trade-off under ParallelGC (maybe better, maybe worse) and a loss
under the other two.

One other thing: this would give compression at rest, but not in motion,
and it comes at the expense of two serialization/deserialization and
compression/decompression operations per broker traversed. Maybe being
able to store more messages in a given amount of memory is worth it to you
(your volumes seem a lot higher than ours, and than most installations'),
but latency and throughput matter more to us than memory usage so we'd live
with using more memory to avoid the extra operations.

The question about why to use message bodies at all is an interesting one,
though the ability to compress the body once and have it stay compressed
through multiple network writes is a compelling reason in the near term.

Tim
Post by Kevin Burton
I’ve been thinking about how messages are stored in the broker and ways to
improve the storage in memory.
First, right now, messages are stored in the same heap, and if you’re using
the memory store, like, that’s going to add up. This will increase GC
latency , and you actually need 2x more memory because you have to have
temp memory set aside for GCs.
I was thinking about using Chronicle to store the messages off heap using
direct buffers. The downside to this is that the messages need to be
serialized/deserialized with each access. But realistically that’s probably
acceptable because you can do something like 1M message deserializations
per second. Which is normally more than the throughput of the broker.
Additionally, chronicle supports zlib or snappy compression on the message
bodies. So, while the broker supports message compression now, it doesn’t
support this feature on headers.
This would give us header compression!
The broker would transparently decompress the headers when reading the
message.
This then begs the question, why use message bodies at all? Why not just
store an entire message as a set of headers?
If you need hierarchy you can do foo.bar.cat.dog style header names.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Justin Reock
2015-04-20 13:34:23 UTC
Permalink
"You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing", which doesn't match my experience at all. I've observed G1GC
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC.²

In general, because ActiveMQ makes use of so many tiny objects in memory,
I recommend people set aside around twice the necessary heap to allow
compaction to occur efficiently, even with G1GC. The compaction algorithm
works by copying fragmented data into a free contiguous area of heap, so,
the more free heap you have, the higher the guarantee that lots of objects
can be compacted without requiring a GC.

-Justin
Post by Tim Bain
I'm confused about what would drive the need for this.
Is it the ability to hold more messages than your JVM size allows? If so,
we already have both KahaDB and LevelDB; what does Chronicle offer that
those other two don't?
Is it because you see some kind of inefficiency in how ActiveMQ uses memory
or how the JVM's GC strategies work? If so, can you elaborate on what
you're concerned about? (You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing", which doesn't match my experience at all. I've observed G1GC
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC. Please either correct my
interpretation of what your statement or provide the data you're basing it
on.)
One difference in GC behavior with what you're proposing is that under your
algorithm you'd GC each message at least twice (once when it's received and
put into Chronicle, and once when it's pulled from Chronicle and sent
onward, plus any additional reads needed to operate on the message such as
if a new subscriber with a non-matching selector connected to the broker)
instead of just once under the current algorithm. On the other hand, your
GCs should all be from Young Gen (and cheap) whereas the current algorithm
would likely push many of its messages to Old Gen. Old Gen GCs are more
expensive under ParallelGC, though they're no worse under G1GC and CMS.
So
it's a trade-off under ParallelGC (maybe better, maybe worse) and a loss
under the other two.
One other thing: this would give compression at rest, but not in motion,
and it comes at the expense of two serialization/deserialization and
compression/decompression operations per broker traversed. Maybe being
able to store more messages in a given amount of memory is worth it to you
(your volumes seem a lot higher than ours, and than most installations'),
but latency and throughput matter more to us than memory usage so we'd live
with using more memory to avoid the extra operations.
The question about why to use message bodies at all is an interesting one,
though the ability to compress the body once and have it stay compressed
through multiple network writes is a compelling reason in the near term.
Tim
I¹ve been thinking about how messages are stored in the broker and ways
to
improve the storage in memory.
First, right now, messages are stored in the same heap, and if you¹re
using
the memory store, like, that¹s going to add up. This will increase GC
latency , and you actually need 2x more memory because you have to have
temp memory set aside for GCs.
I was thinking about using Chronicle to store the messages off heap using
direct buffers. The downside to this is that the messages need to be
serialized/deserialized with each access. But realistically that¹s
probably
acceptable because you can do something like 1M message deserializations
per second. Which is normally more than the throughput of the broker.
Additionally, chronicle supports zlib or snappy compression on the message
bodies. So, while the broker supports message compression now, it
doesn¹t
support this feature on headers.
This would give us header compression!
The broker would transparently decompress the headers when reading the
message.
This then begs the question, why use message bodies at all? Why not just
store an entire message as a set of headers?
If you need hierarchy you can do foo.bar.cat.dog style header names.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com
Š or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Tim Bain
2015-04-20 15:37:11 UTC
Permalink
With G1GC, you need exactly 1 free bucket per GC thread to be able to
perform garbage collection. By the time you need the next bucket on any
given thread, you've freed up at least one new one (often more), so you've
always got enough space for that next bucket. And given that G1GC shoots
(by default) for 2000 buckets, you only need 0.05% of your heap free per
bucket in order to perform GC; even if you're using 8 threads, that's still
less than one half of one percent of your total heap, not 50%.

CMS doesn't perform compaction (till heap fragmentation forces a
ParallelGC-based full GC, anyway), and I believe that ParallelGC compacts
by copying live objects into the space previously used by dead objects (at
earlier array indices, to use that mental model), not a from-space/to-space
algorithm.

The bigger reason to have some overhead with G1GC is to allow the garbage
collector to successfully harvest objects from Old Gen fast enough to
ensure that you don't run out of memory (which would force a stop-the-world
full garbage collection); you want to make sure that there's enough
overhead when you start the first Old Gen GC to hold any objects that might
be created in New Gen before the GC finishes. But I wouldn't describe that
as related to efficient compaction, and I'd be astounded (and alarmed) if
an application could generate 50% of the heap in new objects during a G1GC
pause (by default, that's 200ms).

Tim
Post by Justin Reock
"You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing", which doesn't match my experience at all. I've observed G1GC
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC.²
In general, because ActiveMQ makes use of so many tiny objects in memory,
I recommend people set aside around twice the necessary heap to allow
compaction to occur efficiently, even with G1GC. The compaction algorithm
works by copying fragmented data into a free contiguous area of heap, so,
the more free heap you have, the higher the guarantee that lots of objects
can be compacted without requiring a GC.
-Justin
Post by Tim Bain
I'm confused about what would drive the need for this.
Is it the ability to hold more messages than your JVM size allows? If so,
we already have both KahaDB and LevelDB; what does Chronicle offer that
those other two don't?
Is it because you see some kind of inefficiency in how ActiveMQ uses memory
or how the JVM's GC strategies work? If so, can you elaborate on what
you're concerned about? (You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing", which doesn't match my experience at all. I've observed G1GC
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC. Please either correct my
interpretation of what your statement or provide the data you're basing it
on.)
One difference in GC behavior with what you're proposing is that under your
algorithm you'd GC each message at least twice (once when it's received and
put into Chronicle, and once when it's pulled from Chronicle and sent
onward, plus any additional reads needed to operate on the message such as
if a new subscriber with a non-matching selector connected to the broker)
instead of just once under the current algorithm. On the other hand, your
GCs should all be from Young Gen (and cheap) whereas the current algorithm
would likely push many of its messages to Old Gen. Old Gen GCs are more
expensive under ParallelGC, though they're no worse under G1GC and CMS.
So
it's a trade-off under ParallelGC (maybe better, maybe worse) and a loss
under the other two.
One other thing: this would give compression at rest, but not in motion,
and it comes at the expense of two serialization/deserialization and
compression/decompression operations per broker traversed. Maybe being
able to store more messages in a given amount of memory is worth it to you
(your volumes seem a lot higher than ours, and than most installations'),
but latency and throughput matter more to us than memory usage so we'd live
with using more memory to avoid the extra operations.
The question about why to use message bodies at all is an interesting one,
though the ability to compress the body once and have it stay compressed
through multiple network writes is a compelling reason in the near term.
Tim
I¹ve been thinking about how messages are stored in the broker and ways
to
improve the storage in memory.
First, right now, messages are stored in the same heap, and if you¹re
using
the memory store, like, that¹s going to add up. This will increase GC
latency , and you actually need 2x more memory because you have to have
temp memory set aside for GCs.
I was thinking about using Chronicle to store the messages off heap using
direct buffers. The downside to this is that the messages need to be
serialized/deserialized with each access. But realistically that¹s
probably
acceptable because you can do something like 1M message deserializations
per second. Which is normally more than the throughput of the broker.
Additionally, chronicle supports zlib or snappy compression on the message
bodies. So, while the broker supports message compression now, it
doesn¹t
support this feature on headers.
This would give us header compression!
The broker would transparently decompress the headers when reading the
message.
This then begs the question, why use message bodies at all? Why not just
store an entire message as a set of headers?
If you need hierarchy you can do foo.bar.cat.dog style header names.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com
Å  or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Kevin Burton
2015-04-20 16:16:04 UTC
Permalink
Post by Tim Bain
I'm confused about what would drive the need for this.
Is it the ability to hold more messages than your JVM size allows? If so,
we already have both KahaDB and LevelDB; what does Chronicle offer that
those other two don't?
The ability to pack more messages into the JVM but keep it in memory which
is MUCH faster than disk, even with message serialization.

Also, the ability to avoid GC lock pauses during compaction of large heaps.


This is already a technique used in other databases. For example,
Cassandra does slab allocation where memtables are stored off heap for the
same reasons.
Post by Tim Bain
Is it because you see some kind of inefficiency in how ActiveMQ uses memory
or how the JVM's GC strategies work?
Both, and how hash table re-allocation works.
Post by Tim Bain
If so, can you elaborate on what
you're concerned about? (You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing",
I misspoke. I meant to say hash re-allocation.

The three things things this solves are:

1. MUCH tighter storage of objects in memory. a 15-20x memory saving is
possible because Java is very bad at representing objects in memory.

2. Lower full GC pauses since the JVM doesn’t have to copy 10GB of RAM
each full GC.

3. Additional free memory because less is needed during hashtable
re-allocation. (though I haven’t been able to duplicate this in practice
with LinkedHashMap).

which doesn't match my experience at all. I've observed G1GC
Post by Tim Bain
to successfully GC when the heap was nearly 100% full, I'm certain it's not
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though I
haven't directly observed it during a GC. Please either correct my
interpretation of what your statement or provide the data you're basing it
on.)
#1 above is my BIG motivating factor. I haven’t seen #2 or #3 in
production though I may have seen #3 with parallel GC. I resolved it by
allocating more memory.
Post by Tim Bain
One difference in GC behavior with what you're proposing is that under your
algorithm you'd GC each message at least twice (once when it's received and
put into Chronicle, and once when it's pulled from Chronicle and sent
onward,
Oh yes.. but it’s in the young generation. It’s just normal garbage.
Post by Tim Bain
plus any additional reads needed to operate on the message such as
if a new subscriber with a non-matching selector connected to the broker)
instead of just once under the current algorithm.
This is a traditional space / time tradeoff.

I’m trading a bit more CPU time for a LOT more memory. So if it’s 5% more
CPU time for 15x more memory, that’s a big $ savings.

It can still do something like 180M messages per second encode/decode.
Which is more than fine. If you’re doing THAT many messages on your broker
you probably have other performance issues.

We’re doing about 1000 transactions per second on our broker.
Post by Tim Bain
One other thing: this would give compression at rest, but not in motion,
and it comes at the expense of two serialization/deserialization and
compression/decompression operations per broker traversed.
yes. not in motion. When you read a message, it’s temporarily decompressed,
then the messages in in Java as a real messages, then just discarded as a
young generation GC.

But it’s part of normal GC at this point, and won’t waste any more memory.

Also, there’s going to be a memory savings here WITHOUT compression.
Strings can be stored as UTF8 internally and will be MUCH more efficient.
Plus, you don’t have JVM memory overhead of non-packed objects. The
chronicle objects would be packed so you would get REALLY good storage
efficiency.

you could use snappy compression if you wanted too but it would depend on
your benchmarks. Maybe it’s not needed. Snappy is VERy fast though. Like
150MB/s on a single core.
Post by Tim Bain
Maybe being
able to store more messages in a given amount of memory is worth it to you
(your volumes seem a lot higher than ours, and than most installations'),
but latency and throughput matter more to us than memory usage so we'd live
with using more memory to avoid the extra operations.
Yes. Latency and throughput matter MUCH more to us too.. which is why
we’re using memory.

And the latencies and throughput in this situation would be MUCH MUCH
higher than what you can get from KahaDB and LevelDB.
Post by Tim Bain
The question about why to use message bodies at all is an interesting one,
though the ability to compress the body once and have it stay compressed
through multiple network writes is a compelling reason in the near term.
Ah. yes. That’s a good point. The network stack would need to be
rewritten to avoid a decompression from memory, then a compression again
while sent over the wire.

Kevin
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Tim Bain
2015-04-26 05:00:41 UTC
Permalink
Post by Kevin Burton
Post by Tim Bain
I'm confused about what would drive the need for this.
Is it the ability to hold more messages than your JVM size allows? If
so,
Post by Tim Bain
we already have both KahaDB and LevelDB; what does Chronicle offer that
those other two don't?
The ability to pack more messages into the JVM but keep it in memory which
is MUCH faster than disk, even with message serialization.
Also, the ability to avoid GC lock pauses during compaction of large heaps.
This is already a technique used in other databases. For example,
Cassandra does slab allocation where memtables are stored off heap for the
same reasons.
Post by Tim Bain
Is it because you see some kind of inefficiency in how ActiveMQ uses
memory
Post by Tim Bain
or how the JVM's GC strategies work?
Both, and how hash table re-allocation works.
Post by Tim Bain
If so, can you elaborate on what
you're concerned about? (You made a statement that sounds like "the JVM
can only use half its memory, because the other half has to be kept free
for GCing",
I misspoke. I meant to say hash re-allocation.
1. MUCH tighter storage of objects in memory. a 15-20x memory saving is
possible because Java is very bad at representing objects in memory.
I hadn't realized it could drive that large of a reduction in memory usage;
that makes sense for why it would be valuable for people who are keeping
lots of messages in memory. I'm not sure we'd make the same tradeoff
(though we might to save money on RAM), so as usual my response is "great
idea, but make it a configurable option rather than the only way to do it",
but now I can certainly see why you'd want it.
Post by Kevin Burton
2. Lower full GC pauses since the JVM doesn’t have to copy 10GB of RAM
each full GC.
3. Additional free memory because less is needed during hashtable
re-allocation. (though I haven’t been able to duplicate this in practice
with LinkedHashMap).
which doesn't match my experience at all. I've observed G1GC
Post by Tim Bain
to successfully GC when the heap was nearly 100% full, I'm certain it's
not
Post by Tim Bain
a problem for CMS because CMS is a non-compacting Old Gen GC strategy -
that's why it's subject to fragmentation - and I believe that ParallelGC
does in-place compaction so it wouldn't require additional memory though
I
Post by Tim Bain
haven't directly observed it during a GC. Please either correct my
interpretation of what your statement or provide the data you're basing
it
Post by Tim Bain
on.)
#1 above is my BIG motivating factor. I haven’t seen #2 or #3 in
production though I may have seen #3 with parallel GC. I resolved it by
allocating more memory.
If you're not using G1GC, you really should consider it. Pure throughput
is a bit lower (G1 comes with more overhead than Parallel GC), but the
frequency of full GCs is way lower because it's able to do incremental
collection of most Old Gen objects. Some Old Gen objects fail to collect
during the incremental phase but successfully collect during the full GC
(and we haven't spent the time to see if tuning the G1GC parameters allows
all of them to collect), but it's still a huge improvement over how often
full GCs happened under Parallel GC.
Post by Kevin Burton
Post by Tim Bain
One difference in GC behavior with what you're proposing is that under
your
Post by Tim Bain
algorithm you'd GC each message at least twice (once when it's received
and
Post by Tim Bain
put into Chronicle, and once when it's pulled from Chronicle and sent
onward,
Oh yes.. but it’s in the young generation. It’s just normal garbage.
If you're using G1GC, even messages that make it to Old Gen are just normal
garbage...
Post by Kevin Burton
plus any additional reads needed to operate on the message such as
Post by Tim Bain
if a new subscriber with a non-matching selector connected to the broker)
instead of just once under the current algorithm.
This is a traditional space / time tradeoff.
I’m trading a bit more CPU time for a LOT more memory. So if it’s 5% more
CPU time for 15x more memory, that’s a big $ savings.
It can still do something like 180M messages per second encode/decode.
Which is more than fine. If you’re doing THAT many messages on your broker
you probably have other performance issues.
We’re doing about 1000 transactions per second on our broker.
Post by Tim Bain
One other thing: this would give compression at rest, but not in motion,
and it comes at the expense of two serialization/deserialization and
compression/decompression operations per broker traversed.
yes. not in motion. When you read a message, it’s temporarily decompressed,
then the messages in in Java as a real messages, then just discarded as a
young generation GC.
But it’s part of normal GC at this point, and won’t waste any more memory.
My point was simply that this doesn't work instead of compression; it's a
complementary feature, not an improved replacement. But as I thought about
it I realized that when you read the message body to serialize it to
Chronicle, you're likely to invoke the decompression code and end up
undoing the compression of the message. (Maybe you realized the same thing
in your last line; I wasn't sure.) So you either need a flag that'll tell
you to recompress it when you read it back out of Chronicle (which then
means you're doing wasteful decompress/compress operations) or you'll need
to access the compressed bytes without running through the decompression
code (e.g. using reflection to grab the field directly). Either way, make
sure the message is still compressed when you send it onwards if it was
compressed when you received it...
Post by Kevin Burton
Also, there’s going to be a memory savings here WITHOUT compression.
Strings can be stored as UTF8 internally and will be MUCH more efficient.
Plus, you don’t have JVM memory overhead of non-packed objects. The
chronicle objects would be packed so you would get REALLY good storage
efficiency.
you could use snappy compression if you wanted too but it would depend on
your benchmarks. Maybe it’s not needed. Snappy is VERy fast though. Like
150MB/s on a single core.
Post by Tim Bain
Maybe being
able to store more messages in a given amount of memory is worth it to
you
Post by Tim Bain
(your volumes seem a lot higher than ours, and than most installations'),
but latency and throughput matter more to us than memory usage so we'd
live
Post by Tim Bain
with using more memory to avoid the extra operations.
Yes. Latency and throughput matter MUCH more to us too.. which is why
we’re using memory.
And the latencies and throughput in this situation would be MUCH MUCH
higher than what you can get from KahaDB and LevelDB.
Absolutely. And lower than what you could get from using Chronicle-less
memory. So it could be a great additional option, for when the tradeoff
was worth it to people.
Post by Kevin Burton
Post by Tim Bain
The question about why to use message bodies at all is an interesting
one,
Post by Tim Bain
though the ability to compress the body once and have it stay compressed
through multiple network writes is a compelling reason in the near term.
Ah. yes. That’s a good point. The network stack would need to be
rewritten to avoid a decompression from memory, then a compression again
while sent over the wire.
I don't think it's the network stack where that code works; I'm pretty sure
the message itself does decompression when the body is accessed via the
getter. But when you read the message body to serialize it to Chronicle,
you're likely to invoke that decompression code and end up undoing the
compression of the message. So you could use a flag that'll tell you to
recompress it when you read it back out of Chronicle, but then you're doing
wasteful decompress/compress operations. It would be better to access the
compressed bytes without running through the decompression code (e.g. using
reflection to grab the field directly) to avoid those wasteful operations.
Either way, make sure the message is still compressed when you send it
onwards if it was compressed when you received it...
Post by Kevin Burton
Kevin
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Kevin Burton
2015-04-26 18:03:44 UTC
Permalink
Post by Tim Bain
I don't think it's the network stack where that code works; I'm pretty sure
the message itself does decompression when the body is accessed via the
getter. But when you read the message body to serialize it to Chronicle,
you're likely to invoke that decompression code and end up undoing the
compression of the message. So you could use a flag that'll tell you to
recompress it when you read it back out of Chronicle, but then you're doing
wasteful decompress/compress operations. It would be better to access the
compressed bytes without running through the decompression code (e.g. using
reflection to grab the field directly) to avoid those wasteful operations.
Either way, make sure the message is still compressed when you send it
onwards if it was compressed when you received it...
Ah.. interesting. So I think you can control Chronicle compression /
decompression directly. It’s designed for this so that you can control
performance.

So it might be possible to read compressed data and write it over the
wire. The downside is that the client would need to be able to read the
chronicle data.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Tim Bain
2015-04-26 18:19:08 UTC
Permalink
I didn't follow your second paragraph. The goal with the Chronicle code
should be to put the message back in memory after the Chronicle read as it
was before the Chronicle write, right? So if the message body (only) was
compressed (using the compression algorithm used for ActiveMQ messages,
which isn't necessarily the one Chronicle uses), then it should be the same
when it comes back out of Chronicle. How does writing over the wire play
into that? Don't try to get Chronicle to redo the work ActiveMQ is already
doing (because then you have to stay in sync with changes to the
compression algorithm, plus it makes the code more complicated); just get
the bytes out of the byte array without invoking the getter that will
decompress them (and reflection should be able to do that easily).

Bonus points if you can tell Chronicle not to compress/decompress those
particular bytes (which saves some CPU cycles because those compressed
bytes aren't going to get much smaller and could easily get bigger with a
second compression), but even if it's double-compressed, that's better than
decompressing it, having Chronicle compress it, having Chronicle decompress
it, and then manually recompressing it before sending it on.
Post by Tim Bain
Post by Tim Bain
I don't think it's the network stack where that code works; I'm pretty
sure
Post by Tim Bain
the message itself does decompression when the body is accessed via the
getter. But when you read the message body to serialize it to Chronicle,
you're likely to invoke that decompression code and end up undoing the
compression of the message. So you could use a flag that'll tell you to
recompress it when you read it back out of Chronicle, but then you're
doing
Post by Tim Bain
wasteful decompress/compress operations. It would be better to access
the
Post by Tim Bain
compressed bytes without running through the decompression code (e.g.
using
Post by Tim Bain
reflection to grab the field directly) to avoid those wasteful
operations.
Post by Tim Bain
Either way, make sure the message is still compressed when you send it
onwards if it was compressed when you received it...
Ah.. interesting. So I think you can control Chronicle compression /
decompression directly. It’s designed for this so that you can control
performance.
So it might be possible to read compressed data and write it over the
wire. The downside is that the client would need to be able to read the
chronicle data.
--
Founder/CEO Spinn3r.com
Location: *San Francisco, CA*
blog: http://burtonator.wordpress.com

 or check out my Google+ profile
<https://plus.google.com/102718274791889610666/posts>
<http://spinn3r.com>
Loading...