Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.apache.kafka.streams.state.internals;

import java.util.List;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.apache.kafka.common.utils.Bytes;
import org.apache.kafka.streams.KeyValue;
import org.apache.kafka.streams.processor.ProcessorContext;
Expand All @@ -26,18 +28,16 @@

import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;

public class InMemoryKeyValueStore implements KeyValueStore<Bytes, byte[]> {
private final String name;
private final NavigableMap<Bytes, byte[]> map;
private final ConcurrentNavigableMap<Bytes, byte[]> map;
private volatile boolean open = false;

public InMemoryKeyValueStore(final String name) {
this.name = name;

this.map = new TreeMap<>();
this.map = new ConcurrentSkipListMap<>();
}

@Override
Expand All @@ -46,7 +46,6 @@ public String name() {
}

@Override
@SuppressWarnings("unchecked")
public void init(final ProcessorContext context,
final StateStore root) {

Expand Down Expand Up @@ -76,12 +75,12 @@ public boolean isOpen() {
}

@Override
public synchronized byte[] get(final Bytes key) {
public byte[] get(final Bytes key) {
return this.map.get(key);
}

@Override
public synchronized void put(final Bytes key, final byte[] value) {
public void put(final Bytes key, final byte[] value) {
if (value == null) {
this.map.remove(key);
} else {
Expand All @@ -90,7 +89,7 @@ public synchronized void put(final Bytes key, final byte[] value) {
}

@Override
public synchronized byte[] putIfAbsent(final Bytes key, final byte[] value) {
public byte[] putIfAbsent(final Bytes key, final byte[] value) {
final byte[] originalValue = get(key);
if (originalValue == null) {
put(key, value);
Expand All @@ -99,29 +98,29 @@ public synchronized byte[] putIfAbsent(final Bytes key, final byte[] value) {
}

@Override
public synchronized void putAll(final List<KeyValue<Bytes, byte[]>> entries) {
public void putAll(final List<KeyValue<Bytes, byte[]>> entries) {
for (final KeyValue<Bytes, byte[]> entry : entries) {
put(entry.key, entry.value);
}
}

@Override
public synchronized byte[] delete(final Bytes key) {
public byte[] delete(final Bytes key) {
return this.map.remove(key);
}

@Override
public synchronized KeyValueIterator<Bytes, byte[]> range(final Bytes from,
final Bytes to) {
public KeyValueIterator<Bytes, byte[]> range(final Bytes from, final Bytes to) {
return new DelegatingPeekingKeyValueIterator<>(
name,
new InMemoryKeyValueIterator(this.map.subMap(from, true, to, true).entrySet().iterator()));
}

@Override
public synchronized KeyValueIterator<Bytes, byte[]> all() {
final TreeMap<Bytes, byte[]> copy = new TreeMap<>(this.map);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to copy the entire underlying store, just return an iterator on the existing map

return new DelegatingPeekingKeyValueIterator<>(name, new InMemoryKeyValueIterator(copy.entrySet().iterator()));
public KeyValueIterator<Bytes, byte[]> all() {
return new DelegatingPeekingKeyValueIterator<>(
name,
new InMemoryKeyValueIterator(this.map.entrySet().iterator()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,15 @@ public void shouldDeleteFromStore() {
store.delete(2);
assertNull(store.get(2));
}

@Test
public void shouldNotThrowConcurrentModificationException() {
store.put(0, "zero");

final KeyValueIterator<Integer, String> results = store.range(0, 2);

store.put(1, "one");

assertEquals(new KeyValue<>(0, "zero"), results.next());
}
}