Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - aitusoftware/recall: Single-threaded off-heap object store · GitHub
Skip to content

Repository files navigation

Build Status

Language grade: Java

Recall Icon

Recall is an off-heap, allocation-free object store for the JVM.

Usage

Recall is designed for use in allocation-free or low-garbage systems. Objects are expected to be mutable in order to reduce allocation costs. For this reason, domain objects should have mutator methods for any fields that need to be serialised.

Dependency

Gradle

dependencies {
compile group: 'com.aitusoftware', name: 'recall-store', version: '0.2.0'
}

Maven

<dependency>
<groupId>com.aitusoftware</groupId>
<artifactId>recall-store</artifactId>
<version>0.2.0</version>
</dependency>

Recall can use either a standard JDK ByteBuffer or an AgronaUnsafeBuffer for storage of objects outside of the Java heap.

To use the Recall object store, implement the Encoder, Decoder, and IdAccessor interface for a given object and buffer type:

publicclassOrder {
privatelongid;
privatedoublequantity;
privatedoubleprice;
// constructor omitted// getters and setters omitted
}

Implement Encoder

publicclassOrderEncoderimplementsEncoder<ByteBuffer, Order> {
publicvoidstore(ByteBufferbuffer, intoffset, Orderorder) {
buffer.putLong(offset, order.getId());
buffer.putDouble(offset + Long.BYTES, order.getQuantity());
buffer.putDouble(offset + Long.BYTES + Double.BYTES, order.getPrice());
}
}

Implement Decoder

publicclassOrderDecoderimplementsDecoder<ByteBuffer, Order> {
publicvoidload(ByteBufferbuffer, intoffset, Ordertarget) {
target.setId(buffer.getLong(offset));
target.setQuantity(buffer.getDouble(offset + Long.BYTES));
target.setPrice(buffer.getDouble(offset + Long.BYTES + Double.BYTES));
}
}

Implement IdAccessor

publicclassOrderIdAccessorimplementsIdAccessor<Order> {
publiclonggetId(Orderorder) {
returnorder.getId();
}
}

Store

Create a Store:

BufferStore<ByteBuffer> store =
newBufferStore<>(24, 100, ByteBuffer::allocateDirect, newByteBufferOps());

Optionally wrap it in a SingleTypeStore (if only one type is going to be stored):

SingleTypeStore<ByteBuffer, Order> typeStore =
newSingleTypeStore<>(store, newOrderDecoder(), newOrderEncoder(),
newOrderIdAccessor());

Storage and Retrieval

Domain objects can be serialised to off-heap storage, and retrieved at a later time:

longorderId = 42L;
OrdertestOrder = newOrder(orderId, 12.34D, 56.78D);
typeStore.store(testOrder);
Ordercontainer = newOrder(-1, -1, -1);
asserttypeStore.load(orderId, container);
assertcontainer.getQuantity() == 12.34D;

SBE integration

Recall is able to provide efficient off-heap storage of SBE-encoded messages.

This example uses the canonical Car example from SBE.

SBE codecs

SBE objects must be generated with:

-Dsbe.java.generate.interfaces=true

this causes the Decoder to implement MessageDecoderFlyweight.

Implement IdAccessor

It is necessary to implement the IdAccessor interface for the SBE Decoder type:

publicclassCarIdAccessorimplementsIdAccessor<CarDecoder> {
publiclonggetId(CarDecoderdecoder) {
returndecoder.id();
}
}

SBE Message Store

Create a SingleTypeStore for the type of the Decoder:

SingleTypeStore<UnsafeBuffer, CarDecoder> messageStore =
SbeMessageStoreFactory.forSbeMessage(newCarDecoder(),
MAX_RECORD_LENGTH, 100,
len -> newUnsafeBuffer(ByteBuffer.allocateDirect(len)),
newCarIdAccessor());

Note: it is up to the application developer to determine the maximum length of any given SBE message (even in the case of variable-length fields).

If an encoded value exceeds the specified maximum record length, then the store method will throw an IllegalArgumentException.

SBE messages can now be stored for later retrieval:

Storage

publicvoidreceiveCar(ReadableByteChannelchannel) {
CarDecoderdecoder = newCarDecoder();
UnsafeBufferbuffer = newUnsafeBuffer();
ByteBufferinputData = ByteBuffer.allocateDirect(MAX_RECORD_LENGTH);
channel.read(inputData);
inputData.flip();
buffer.wrap(inputData);
decoder.wrap(buffer, 0, BLOCK_LENGTH, VERSION);
dispatchCarReceivedEvent(decoder);
messageStore.store(decoder);
}

Retrieval

publicvoidnotifyCarSold(longcarId) {
CarDecoderdecoder = newCarDecoder();
messageStore.load(carId, decoder);
dispatchCarSoldEvent(decoder);
}

Non-integer keys

Since it is sometimes useful to be able to store and retrieve objects by something other than an integer key, Recall also provides the ability to create mappings based on variable-length keys based on either strings, or byte-sequences.

CharSequenceMap

CharSequenceMap is an open-addressed hash map with that can be used to store a CharSequence against an integer identifier.

Example usage:

privatefinalOrderByteBufferTranscodertranscoder =
newOrderByteBufferTranscoder();
privatefinalSingleTypeStore<ByteBuffer, Order> store =
newSingleTypeStore<>(
newBufferStore<>(MAX_RECORD_LENGTH, INITIAL_SIZE,
ByteBuffer::allocateDirect, newByteBufferOps()),
transcoder, transcoder, Order::getId);
privatefinalCharSequenceMaporderBySymbol =
newCharSequenceMap(MAX_KEY_LENGTH, INITIAL_SIZE, Long.MIN_VALUE);
privatevoidexecute()
{
finalString[] symbols = newString[INITIAL_SIZE];
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalOrderorder = Order.of(i);
store.store(order);
orderBySymbol.insert(order.getSymbol(), order.getId());
symbols[i] = order.getSymbol().toString();
}
finalOrdercontainer = Order.of(-1L);
for (inti = 0; i < INITIAL_SIZE; i++)
{
finalStringsearchTerm = symbols[i];
finallongid = orderBySymbol.search(searchTerm);
assertThat(store.load(id, container)).isTrue();
System.out.printf("Order with symbol %s has id %d%n", searchTerm, id);
}
}

ByteSequenceMap

ByteSequenceMap is an open-addressed hash map with that can be used to store a ByteBuffer against an integer identifier.

Releases

Packages

Used by

Contributors

Languages