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
ISPN-7780 multimap embedded #5193
Conversation
63c79fd
to
972262a
Compare
There was a problem hiding this 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 |
There was a problem hiding this comment.
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}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/matches/match/
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
public void testPut() { | ||
await( | ||
multimapCache.put("names", "julien").thenCompose(r1 -> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outdated link
There was a problem hiding this 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); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
There was a problem hiding this comment.
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> {} |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spacing
I am more than aware of that, hence my email before. But this doesn't stop
the Java client from performing a lambda on results from the server.
If we want to have separate, but similar, API for embedded and remote I am
fine with that. Then to me this API shouldn't exist in commons. We could
have a super set, such as BaseFeature that you mentioned in the email.
…On Tue, Jun 13, 2017, 4:12 AM Radim Vansa ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In commons/src/main/java/org/infinispan/multimap/api/MultimapCache.java
<#5193 (comment)>
:
> + * Returns true when the value has been removed from the key.
+ *
+ * @param key, to be removed
+ * @param value, to be removed
+ * @return boolean, remove result
+ */
+ CompletableFuture<Boolean> remove(K key, V value);
+
+ /**
+ * Removes all the values in every key that matches the given ***@***.*** Predicate}.
+ *
+ * For now, this method returns a completed future
+ *
+ * @param p, the predicate to be tested on every value in the multimap cache
+ */
+ CompletableFuture<Void> remove(Predicate<? super V> p);
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.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5193 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAnj7985fb4cpy8eVBCtrIwENCLQVv0Zks5sDjr8gaJpZM4N28Xq>
.
|
* @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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
0e3bdec
to
c318358
Compare
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 :/ |
@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
}); |
c318358
to
9c2026e
Compare
@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.
@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 |
@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 |
@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 maybe a stupid question, but they say it's always worth asking :) Looking at 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. |
@tsegismont Your understanding is correct. While the entries wouldn't be load-balanced perfectly, this is the easiest way to implement multimaps in Infinispan. |
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f88d668
to
8693df7
Compare
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) :( |
To note: commits for ISPN-7993 should be out of this PR. |
2a45116
to
85e13d1
Compare
@galderz I removed the rebase from ISPN-7993 and removed the tests OFF_HEAP storage type is working |
There was a problem hiding this 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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: followin
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created https://issues.jboss.org/browse/ISPN-8105 to handle that
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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<>(); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
second non owner?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these imports necessary?
There was a problem hiding this comment.
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
20afa03
to
315da88
Compare
Temporarily closing because of CI overload |
@tristantarrant you scared me there |
Needs rebasing :| |
e8c6482
to
692b0eb
Compare
a2b2ab9
to
6345514
Compare
@galderz properly rebased |
@@ -0,0 +1,155 @@ | |||
== Multimap Cache | |||
|
|||
Infinispan Multimap Cache is a new distributed and clustered collection type introduced in Infinispan 9. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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[] |
There was a problem hiding this comment.
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)} |
There was a problem hiding this comment.
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
6345514
to
39ca601
Compare
@tristantarrant I reported the changes you asked on documentation |
Merged to master |
https://issues.jboss.org/browse/ISPN-7780
Design doc PR :
infinispan/infinispan-designs#3