Skip to content

KAFKA-7912: Support concurrent access in InMemoryKeyValueStore#6336

Merged
guozhangwang merged 1 commit intoapache:trunkfrom
ableegoldman:ConcurrentInMemoryKeyValueStore
Feb 28, 2019
Merged

KAFKA-7912: Support concurrent access in InMemoryKeyValueStore#6336
guozhangwang merged 1 commit intoapache:trunkfrom
ableegoldman:ConcurrentInMemoryKeyValueStore

Conversation

@ableegoldman
Copy link
Copy Markdown
Member

Previously the InMemoryKeyValue store would throw a ConcurrentModificationException if the store was modified beneath an open iterator. The TreeMap implementation was swapped with a ConcurrentSkipListMap for similar performance while supporting concurrent access.

Added one test to AbstractKeyValueStoreTest, no existing tests caught this.

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)


@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

@ableegoldman
Copy link
Copy Markdown
Member Author

@bbejeck
Copy link
Copy Markdown
Member

bbejeck commented Feb 27, 2019

\cc @vvcephei for review

@bbejeck
Copy link
Copy Markdown
Member

bbejeck commented Feb 27, 2019

Java 8 failed kafka.server.ListOffsetsRequestTest.testResponseIncludesLeaderEpoch
Java 11 failed

kafka.api.SaslSslAdminClientIntegrationTest.testMinimumRequestTimeouts
org.apache.kafka.streams.integration.RegexSourceIntegrationTest.testRegexMatchesTopicsAWhenDeleted

retest this please

Copy link
Copy Markdown
Member

@bbejeck bbejeck left a comment

Choose a reason for hiding this comment

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

Thanks, @ableegoldman good catch.

Overall it looks good to me, but this got me thinking.

Since we only have a store per task and a StreamThread only operates on one task at a time (even if it is assigned multiple tasks) could we hit this point in a streams application under regular circumstances? I guess the risk comes from the fact that someone could open an iterator and hold on to it without closing and then during further processing the map is updated and since the iterator is a view back to the original map, using the iterator again results in the exception

I'm not suggesting we don't merge this PR, as IMHO it's an improvement, it's got me thinking about our store access patterns.

@ableegoldman
Copy link
Copy Markdown
Member Author

It does seem like we're adding "some" overhead here to deal with edge cases of weird behavior for IQ only, since as you say we shouldn't hit this problem under normal operation. Might it be worth implementing two versions of the underlying stores, an optimized one for normal Streams and a "safe" one for IQ?

I'll try to investigate how much of an overhead we're really introducing here, I suspect it's not terrible for the key-value store but might be less tolerable for the window store (or future session store) since I believe SkipList performance would degrade as we continually delete from the beginning as things expire..?

@bbejeck
Copy link
Copy Markdown
Member

bbejeck commented Feb 28, 2019

Might it be worth implementing two versions of the underlying stores, an optimized one for normal Streams and a "safe" one for IQ?

I'm leaning towards no, if there is any additional overhead I don't think it would be that much and returning thread-safe iterators for IQ would outweigh the cost of maintaining two separate types.

Additionally, we have ReadOnlyXXXStore interfaces that need to provide thread-safe access, so I think what you have here will fit in well.

@bbejeck
Copy link
Copy Markdown
Member

bbejeck commented Feb 28, 2019

Java 11 failed with

kafka.api.DelegationTokenEndToEndAuthorizationTest.testNoConsumeWithoutDescribeAclViaSubscribe
kafka.server.DynamicBrokerReconfigurationTest.testDefaultTopicConfig
kafka.server.RequestQuotaTest.testUnauthorizedThrottle

Java 8 failed with kafka.api.AdminClientIntegrationTest.testMinimumRequestTimeouts

retest this please

@guozhangwang
Copy link
Copy Markdown
Contributor

Good point about performance degradation of SkipList if most of the modifications are at the top -- well depending on how clever J8's SkipList implementation is, as they can go as fancy as this: https://arxiv.org/pdf/1805.04794.pdf (related works introduced skiplist).

But I think for now we can still afford for the overhead, even for windowed in-memory store: if we realized later in production that it is really bad we can come back to it as well.

@guozhangwang
Copy link
Copy Markdown
Contributor

LGTM (java8 will not succeed so we can ignore atm).

Copy link
Copy Markdown
Member

@bbejeck bbejeck left a comment

Choose a reason for hiding this comment

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

LGTM

@guozhangwang guozhangwang merged commit 0913623 into apache:trunk Feb 28, 2019
@guozhangwang
Copy link
Copy Markdown
Contributor

Also confirmed that the added test will fail without the other change.

Merged to trunk, thanks @ableegoldman !!

jarekr pushed a commit to confluentinc/kafka that referenced this pull request Apr 18, 2019
* apache/trunk:
  KAFKA-7880:Naming worker thread by task id (apache#6275)
  improve some logging statements (apache#6078)
  KAFKA-7312: Change broker port used in testMinimumRequestTimeouts and testForceClose
  KAFKA-7997: Use automatic RPC generation in SaslAuthenticate
  KAFKA-8002; Log dir reassignment stalls if future replica has different segment base offset (apache#6346)
  KAFKA-3522: Add TimestampedKeyValueStore builder/runtime classes (apache#6152)
  HOTFIX: add igore import to streams_upgrade_test
  MINOR: ConsumerNetworkClient does not need to send the remaining requests when the node is not ready (apache#6264)
  KAFKA-7922: Return authorized operations in describe consumer group responses (KIP-430 Part-1)
  KAFKA-7918: Inline generic parameters Pt. III: in-memory window store (apache#6328)
  KAFKA-8012; Ensure partitionStates have not been removed before truncating. (apache#6333)
  KAFKA-8011: Fix for race condition causing concurrent modification exception (apache#6338)
  KAFKA-7912: Support concurrent access in InMemoryKeyValueStore (apache#6336)
  MINOR: Skip quota check when replica is in sync (apache#6344)
  HOTFIX: Change header back to http instead of https to path license header test (apache#6347)
  MINOR: fix release.py script (apache#6317)
  MINOR: Remove types from caching stores (apache#6331)
  MINOR: Improve logging for alter log dirs (apache#6302)
  MINOR: state.cleanup.delay.ms default is 600,000 ms (10 minutes). (apache#6345)
  MINOR: disable Streams system test for broker upgrade/downgrade (apache#6341)
pengxiaolong pushed a commit to pengxiaolong/kafka that referenced this pull request Jun 14, 2019
…e#6336)

Previously the InMemoryKeyValue store would throw a ConcurrentModificationException if the store was modified beneath an open iterator. The TreeMap implementation was swapped with a ConcurrentSkipListMap for similar performance while supporting concurrent access.

Added one test to AbstractKeyValueStoreTest, no existing tests caught this.

Reviewers: Bill Bejeck <bill@confluent.io>, Guozhang Wang <wangguoz@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants