Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISPN-7780 multimap embedded #5193

Merged
merged 1 commit into from Sep 29, 2017

Conversation

karesti
Copy link
Contributor

@karesti karesti commented Jun 12, 2017

Copy link
Member

@rvansa rvansa left a comment

Choose a reason for hiding this comment

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

I don't see any documentation how the multimap cache will interact with expiration and eviction settings. A section in documentation would be welcome.


/**
* Removes a value on a key. In the case where duplicates are supported, all the values will be removed.
* After remove call, the remaining value on the key might be an Empty Collection. In this case, the key is removed
Copy link
Member

Choose a reason for hiding this comment

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

just say that the whole entry will be removed - no upper case needed for 'Empty Collection', btw

CompletableFuture<Boolean> remove(K key, V value);

/**
* Removes all the values in every key that matches the given {@link Predicate}.
Copy link
Member

Choose a reason for hiding this comment

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

s/matches/match/

Copy link
Member

Choose a reason for hiding this comment

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

@rvansa Actually matches is correct here :)

Copy link
Member

Choose a reason for hiding this comment

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

@danberindei it's not a the key that matches the predicate, since the predicate does not accept the key, therefore values ... match

Copy link
Member

Choose a reason for hiding this comment

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

Oops, I should have read the function declaration before replying :)

Then I think s/matches/match/ is not enough, it should be something like "Remove the values that match the given Predicate from all the keys".

/**
* Removes all the values in every key that matches the given {@link Predicate}.
*
* For now, this method returns a completed future
Copy link
Member

Choose a reason for hiding this comment

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

...the implementation of this method executes synchronously and returns a completed future. In future this might be changed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Does this behavior apply behavior apply to all methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tsegismont no, just the methods that are marked with the doc. But this might change soon. I have to speak to the team to see the best way to do it.

*
* @return long, the size
*/
CompletableFuture<Integer> size();
Copy link
Member

Choose a reason for hiding this comment

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

use rather CF<Long>, and update @return javadoc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I was thinking about that earlier too

Copy link
Member

Choose a reason for hiding this comment

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

👍

@Override
public CompletableFuture<Collection<V>> get(K key) {
SerializableBiFunction<? super Collection<V>, Throwable, Collection<V>> emptyCollectionWhenNull = (vs, throwable) -> vs == null ? new HashSet<>() : vs;
return cache.getAsync(key).handle(emptyCollectionWhenNull);
Copy link
Member

Choose a reason for hiding this comment

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

emptyCollectionWhenNull does not need to be serializable... Use thenApply instead of handle and return rather Collections.emptySet()

Copy link
Member

Choose a reason for hiding this comment

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

We also need to make sure this is a copy of the collection, just in case. Otherwise if we are in a clustered cache data would get in an inconsistent state if user updates this collection on nodes that own the data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@wburns good point. Changing implementation

Copy link
Member

Choose a reason for hiding this comment

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

@wburns @karesti I don't agree - the collection stored in cache should be immutable, so we should not create a copy but rather wrap it in Collections.unmodifiableCollection.


public void testPut() {
await(
multimapCache.put("names", "julien").thenCompose(r1 ->
Copy link
Member

Choose a reason for hiding this comment

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

You're chaining it wrong, should be:

 multimapCache.put("names", "julien")
   .thenCompose(r -> multimapCache.put("names", "oihana"))
   .thenCompose(r -> multimapCache.put("names", "julien"))
   .thenCompose(r -> multimapCache.get("names"))

That applies to the rest of chains as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok ;)

}

public void testPut() {
await(
Copy link
Member

Choose a reason for hiding this comment

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

unless you need the timeout, use CF.join() instead of await

Copy link
Member

Choose a reason for hiding this comment

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

@karesti please do use a timeout in tests, even if you think you don't need it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using await

* @since 9.1
*/
@Test(groups = "functional", testName = "multimap.TxEmbeddedMultimapCacheTest")
public class TxEmbeddedMultimapCacheTest extends EmbeddedMultimapCacheTest {
Copy link
Member

Choose a reason for hiding this comment

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

You should probably test with both pessimistic and optimistic transactions, and pessimistic with both RC and RR isolation levels. Also multi-node tests can reveal more issues than single-node ones, I think (testing both owner and non-owner variant).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea

Copy link
Member

Choose a reason for hiding this comment

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

Also if you can using the factory or @factory annotation would be preferred over using different classes. Unless there are a lot of differences between how the tests run.

* MultimapCache provides the common building block for the two different types of multimap caches that Infinispan
* provides: embedded and remote.
* <p>
* Please see the <a href="http://www.jboss.org/infinispan/docs">Infinispan documentation</a> and/or the <a
Copy link
Member

Choose a reason for hiding this comment

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

These links are outdated and don't say anything about multimap cache.

* <p/>
*
* @author Katia Aresti, karesti@redhat.com
* @see <a href="http://www.jboss.org/infinispan/docs">Infinispan documentation</a>
Copy link
Member

Choose a reason for hiding this comment

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

outdated link

Copy link
Member

@wburns wburns left a comment

Choose a reason for hiding this comment

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

I haven't had a chance to look over tests, but put some comments on docs and impl.

*/

public static <K, V> MultimapCache<K, V> get(String name, EmbeddedCacheManager cacheManager, Configuration configuration) {
cacheManager.defineConfiguration("test", configuration);
Copy link
Member

Choose a reason for hiding this comment

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

Think you meant to do something else with the configuration here.

*
* Returns duplicates are supported or not in this multimap cache
*
* @return true or false
Copy link
Member

Choose a reason for hiding this comment

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

that is implied by the defined return type. "@return true if this multicache supports duplicate values for a given key" or something like that.

/**
* Removes a value on a key. In the case where duplicates are supported, all the values will be removed.
* After remove call, the remaining value on the key might be an Empty Collection. In this case, the key is removed
* fro this multimap cache.
Copy link
Member

Choose a reason for hiding this comment

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

typo

*
* @param key, to be removed
* @param value, to be removed
* @return boolean, remove result
Copy link
Member

Choose a reason for hiding this comment

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

"@return true if the value has been removed, including if the entire key was removed due to the value being the last one" something like this.

CompletableFuture<Boolean> remove(K key);

/**
* Removes a value on a key. In the case where duplicates are supported, all the values will be removed.
Copy link
Member

Choose a reason for hiding this comment

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

"all of the duplicate values will be removed"

@Override
public CompletableFuture<Collection<V>> get(K key) {
SerializableBiFunction<? super Collection<V>, Throwable, Collection<V>> emptyCollectionWhenNull = (vs, throwable) -> vs == null ? new HashSet<>() : vs;
return cache.getAsync(key).handle(emptyCollectionWhenNull);
Copy link
Member

Choose a reason for hiding this comment

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

We also need to make sure this is a copy of the collection, just in case. Otherwise if we are in a clustered cache data would get in an inconsistent state if user updates this collection on nodes that own the data.

Collection<V> values = (Collection<V>) o1;
Collection<V> newValues = new HashSet<>();

for (V v : values) {
Copy link
Member

@wburns wburns Jun 12, 2017

Choose a reason for hiding this comment

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

Seems we could just use the iterator on the collection and just call remove if it doesn't pass the predicate.

Copy link
Member

Choose a reason for hiding this comment

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

nope, as above

import org.kohsuke.MetaInfServices;

/**
* Used to add exterlizers
Copy link
Member

Choose a reason for hiding this comment

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

Typo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

* @author Katia Aresti, karesti@redhat.com
* @since 9.1
*/
abstract class BaseFunction<K, V, R> implements SerializableFunction<EntryView.ReadWriteEntryView<K, Collection<V>>, R> {}
Copy link
Member

Choose a reason for hiding this comment

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

This can just be an interface


try {
// Raises NoSuchElementException if the value does not exist
Collection<V> values = entryView.get();
Copy link
Member

@wburns wburns Jun 12, 2017

Choose a reason for hiding this comment

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

Hrmm if the entry exists can't we just add the new value to it and set it back in the view? Otherwise the update will get progressively slower.

Collection<V> newValues = entryView.find().map(HashSet::new).orElseGet(HashSet::new);
newValues.add(value)
entryView.set(newValues)

*
* @param p, the predicate to be tested on every value in the multimap cache
*/
CompletableFuture<Void> remove(Predicate<? super V> p);
Copy link
Member

Choose a reason for hiding this comment

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

Oh also we probably want a SerializablePredicate override for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@wburns Yep. I wanted to add it but this class is in commons and SerializablePredicate is in core ... This joins the pb Radim asked about on a mail last friday.

Copy link
Member

Choose a reason for hiding this comment

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

Same thing as I posted on there, we should just add an additional interface in commons for this. We can worry about refactoring core in a major version. At least my 2cents.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, predicate-based methods won't fly in client-server - Hot Rod is language neutral and therefore can't carry lambdas. That's why the SerializableFunction et all are in core and not in commons.
For client-server you'll need to register the filter on server-side, and refer to it by name. Alternatively you could do the filtering using Ickle query.

// Raises NoSuchElementException if the key/value does not exist
Collection<V> values = entryView.get();

if(value == null) {
Copy link
Member

Choose a reason for hiding this comment

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

Spacing

@wburns
Copy link
Member

wburns commented Jun 13, 2017 via email

* @return the collection found in the key when it's present,
* empty Collection when the key is not present
*/
CompletableFuture<Collection<V>> get(K key);
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this be a copy or a view of the values in the cluster?

Copy link
Contributor Author

@karesti karesti Jun 16, 2017

Choose a reason for hiding this comment

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

@tsegismont This will be a copy. Collections.unmodifiableCollection(v)
Changes are not allowed, and if the user needs to modify the collection directly, it should create a new one from the answer.
What do you think ?

/**
* Removes all the values in every key that matches the given {@link Predicate}.
*
* For now, this method returns a completed future
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this behavior apply behavior apply to all methods?

@wburns
Copy link
Member

wburns commented Jun 14, 2017

Talking to @karesti about the async executor, I guess we can't use this in a tx context as there is no way to get the tx context into the spawned thread. And I don't know if I like having to have a method to inject an InvocationContext or Transaction although that would be possible as part of AdvancedCache API / DecoratedCache. Guess I am fine with it blocking for now :/

@rvansa
Copy link
Member

rvansa commented Jun 15, 2017

@wburns Why can't you suspend and resume the transaction? You can even call

Transaction tx = transactionManager.getTransaction();
executor.execute(() -> {
    transactionManager.resume(tx);
    // do something else
});

@wburns
Copy link
Member

wburns commented Jun 15, 2017

@wburns Why can't you suspend and resume the transaction?

@rvansa That was my initial thought as well, but we can't do this as the calling thread has to keep the transaction on it.

You can even call <code>

@rvansa Afaik you can't share the transaction between multiple threads. The old async operations never resumed the transaction in the async thread when an explicit tx was present, they just updated the InvocationContext. Are you sure that we can have more than 1 thread associated with the same transaction? You probably know more about the finer details of the xa spec than I do (the javadoc don't seem to detail this case). Would all the various TransactionManagers support this?

@rvansa
Copy link
Member

rvansa commented Jun 15, 2017

@wburns As you've said, JTA is not clear in this part so I've asked Narayana guys and they've confirmed that there's no issue with it in JBoss TM. The first thread that calls tm.commit()/tm.rollback() ends the transaction, and TM prevents multiple invocations. However, if you have an ongoing transaction and other thread commits it while you're doing sth. with that, problems may arise - it's up to the app to not call commit until all participants are ready.
It seems that it was not same with Atomikos but things may have changes since 2009. Other report that Bitronix does not support that.

@wburns
Copy link
Member

wburns commented Jun 15, 2017

@rvansa So I am on the fence either way, whichever people prefer. The one thing I do like is that according to that stack overflow post that supposedly JTA spec does allow multiple threads to be associated with the same transaction. I double checked and that line is there at http://download.oracle.com/otndocs/jcp/jta-1.1-spec-oth-JSpec/

@karesti
Copy link
Contributor Author

karesti commented Jun 15, 2017

@wburns @rvansa concerning the explicit tx, I'm blocking whenever a explicit tx is ongoing for the methods that fail if I don't don't block. I've documented it on the interface.
I'm going to continue with other stuff on this multimap, and I will come to tx later if you don't mind.

@tsegismont
Copy link
Contributor

@karesti maybe a stupid question, but they say it's always worth asking :)

Looking at EmbeddedMultimapCacheManagerFactory, I see that a multicache is created from a backing "standard" cache. Does that mean that a multicache can be either distributed or replicated?

And since distribution happens on keys, primary and backups will have the whole collection for a key. Is that correct?

I'm just asking to understand, not saying if it's right or wrong.

@rvansa
Copy link
Member

rvansa commented Jun 16, 2017

@tsegismont Your understanding is correct. While the entries wouldn't be load-balanced perfectly, this is the easiest way to implement multimaps in Infinispan.

@tsegismont
Copy link
Contributor

Thanks @rvansa !

* @return the collection found in the key when it's present,
* empty Collection when the key is not present
*/
CompletableFuture<Collection<V>> get(K key);
Copy link
Contributor

Choose a reason for hiding this comment

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

@karesti karesti force-pushed the ISPN-7780-cache-mutimap branch 6 times, most recently from f88d668 to 8693df7 Compare June 21, 2017 09:46
@karesti karesti changed the title ISPN-7780 multimap embedded first shot - dist mode fails with eval ISPN-7780 multimap embedded Jun 21, 2017
@galderz
Copy link
Member

galderz commented Jul 26, 2017

What are we waiting for to integrate this in? It was open early June :\

IMO, this should have included in 9.1.0.Final with follow up JIRAs for missing orthogonal features (transaction support, encoding support...etc) :(

@galderz
Copy link
Member

galderz commented Jul 26, 2017

To note: commits for ISPN-7993 should be out of this PR.

@karesti karesti force-pushed the ISPN-7780-cache-mutimap branch 2 times, most recently from 2a45116 to 85e13d1 Compare July 26, 2017 08:59
@karesti
Copy link
Contributor Author

karesti commented Jul 26, 2017

@galderz I removed the rebase from ISPN-7993 and removed the tests OFF_HEAP storage type is working

Copy link
Member

@rvansa rvansa left a comment

Choose a reason for hiding this comment

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

Sorry, I've lost this from the radar. Looks mostly OK, just the keys in functions are redundant. And I would consider having a null-valued Contains and Remove static instances.

* <p>
* <h2>Transactions</h2> MultimapCache supports implicit transactions without blocking. The following methods block when
* they are called in a explicit transaction context. This limitation could be improved in the followin versions if
* technically possible. <ul> <li>{@link MultimapCache#size()}</li> <li>{@link MultimapCache#containsEntry(Object,
Copy link
Member

Choose a reason for hiding this comment

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

Please one method per line. And btw., in javadoc it is preferred to omit the closing </li> as it improves readability in source.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

didn't know for the closing li. Correcting

*
* @since 9.2
*/
CompletableFuture<Optional<CacheEntry<K, Collection<V>>>> getEntry(K key);
Copy link
Member

Choose a reason for hiding this comment

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

Why is there the Optional? Javadoc does not match

Copy link
Contributor Author

@karesti karesti Jul 26, 2017

Choose a reason for hiding this comment

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

I copied the java doc on cache and changed after to optional, didn't realize, thanks for pointing

* Future implementations might evolve to support duplicated values.
* <p>
* <h2>Transactions</h2> MultimapCache supports implicit transactions without blocking. The following methods block when
* they are called in a explicit transaction context. This limitation could be improved in the followin versions if
Copy link
Member

Choose a reason for hiding this comment

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

typo: followin

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep

* nothing will be done.</li>
* </ul>
*
* @param key, the key to be put
Copy link
Member

Choose a reason for hiding this comment

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

why do you have the comma after param name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why not, comma are pretty 💃
TBH, I don't know why or when I took the idea from, and I repeated the , in my javadoc. Removing them

/**
* Defines a named multimap cache's configuration by using the provided configuration
* If this cache was already configured either declaritively or programmatically this method will throw a
* {@link org.infinispan.commons.CacheConfigurationException}.
Copy link
Member

Choose a reason for hiding this comment

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

Please define the relationship with regular cache names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I don't think that such checks are necessary, I just want to document that multimap "x" is just a different view to cache "x".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

@Test(groups = "functional", testName = "distribution.DistributedMultimapCacheTest")
public class DistributedMultimapCacheTest extends BaseDistFunctionalTest<String, Collection<User>> {

protected Map<Address, MultimapCache<String, User>> multimapCacheCluster = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

I would rather store them in a list so that you can call multimap(0) in the same way as you do cache(0) in core testsuite.

}

protected MultimapCache getMultimapCacheSecondOwner(String key) {
Cache<String, Collection<User>> cache = getSecondNonOwner(key);
Copy link
Member

Choose a reason for hiding this comment

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

second non owner?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not used method anymore, I removed

import org.testng.annotations.Test;

/**
* Single Multimap Cache Test
Copy link
Member

Choose a reason for hiding this comment

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

And do we need both single-node and multinode test? Can be the code shared in any way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well, configuration is different, I like having the simple test too, I'm going to rework it to avoid unnecessary duplication, I agree on that

import org.testng.annotations.Test;
import org.junit.Ignore;

//TODO: Integrate https://issues.jboss.org/browse/ISPN-7993 to make it pass
Copy link
Member

Choose a reason for hiding this comment

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

let's move this test out of the PR if 7993 does not get in before that

@@ -10,6 +10,8 @@
import javax.persistence.spi.PersistenceProvider;

import org.infinispan.commons.util.Util;
import org.infinispan.factories.scopes.Scope;
Copy link
Member

Choose a reason for hiding this comment

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

are these imports necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, this has been added by error and not removed on amend

@karesti karesti force-pushed the ISPN-7780-cache-mutimap branch 2 times, most recently from 20afa03 to 315da88 Compare July 30, 2017 10:00
@tristantarrant
Copy link
Member

Temporarily closing because of CI overload

@karesti
Copy link
Contributor Author

karesti commented Aug 2, 2017

@tristantarrant you scared me there

@karesti karesti reopened this Sep 27, 2017
@galderz
Copy link
Member

galderz commented Sep 27, 2017

Needs rebasing :|

@karesti karesti force-pushed the ISPN-7780-cache-mutimap branch 2 times, most recently from a2b2ab9 to 6345514 Compare September 28, 2017 12:20
@karesti
Copy link
Contributor Author

karesti commented Sep 28, 2017

@galderz properly rebased

@@ -0,0 +1,155 @@
== Multimap Cache

Infinispan Multimap Cache is a new distributed and clustered collection type introduced in Infinispan 9.
Copy link
Member

Choose a reason for hiding this comment

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

9.2


=== Creating a Multimap Cache

In version 9.2, the MultimapCache is configured as a regular cache. This can be done either by code or XML configuration.
Copy link
Member

Choose a reason for hiding this comment

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

Better not use version numbers, just say "Currently".


=== Limitations

In almost every case the Multimap Cache will behave as a regular Cache, but some limitations exist in the first 9.2 version.
Copy link
Member

Choose a reason for hiding this comment

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

again, avoid version numbers.

@@ -36,3 +36,4 @@ include::rolling_upgrades.adoc[]
include::extending.adoc[]
include::architecture.adoc[]
include::counters.adoc[]
include::multimapcache.adoc[]
Copy link
Member

Choose a reason for hiding this comment

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

I think we should move both counters and multimapcache docs further up, after query, before CDI.

* </pre>
* <p>
* <p>
* <h2>Duplicates</h2> Today's implementation does not support duplicate values on keys. {@link Object#equals(Object)}
Copy link
Member

Choose a reason for hiding this comment

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

s/Today's/The current/

* The API is asynchronous
* Mutimap cache is based on regular cache
* There is a limitation on tx caches, some mehods will block
* Added storage type test, attending ISPN-7993
* Added getEntry method for hotrod
* Eenhancements in MultiMap tests
@karesti
Copy link
Contributor Author

karesti commented Sep 28, 2017

@tristantarrant I reported the changes you asked on documentation

@tristantarrant tristantarrant merged commit cb025da into infinispan:master Sep 29, 2017
@tristantarrant
Copy link
Member

Merged to master

@karesti karesti deleted the ISPN-7780-cache-mutimap branch February 6, 2019 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants