[ 
https://issues.apache.org/jira/browse/IGNITE-29276?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Reza Taroosheh updated IGNITE-29276:
------------------------------------
    Summary: An interrupted ATOMIC put whose interceptor returns a new 
Externalizable class is reported as a corrupted B+Tree and stops the node  
(was: An interrupted ATOMIC update is reported as a corrupted B+Tree if the 
interceptor returns a value of a new Externalizable class)

> An interrupted ATOMIC put whose interceptor returns a new Externalizable 
> class is reported as a corrupted B+Tree and stops the node
> -----------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: IGNITE-29276
>                 URL: https://issues.apache.org/jira/browse/IGNITE-29276
>             Project: Ignite
>          Issue Type: Bug
>            Reporter: Reza Taroosheh
>            Assignee: Reza Taroosheh
>            Priority: Major
>             Fix For: 2.20
>
>
> h3. Problem
> In an ATOMIC cache, {{CacheInterceptor.onBeforePut()}} is called inside 
> {{BPlusTree.invoke()}}, from 
> {{GridCacheMapEntry.AtomicCacheUpdateClosure.update()}}. If it returns a 
> value of an {{Externalizable}} class that isn't registered yet, the value is 
> marshaled there by {{prepareForCache()}}, and the thread waits for the class 
> registration inside the tree. An interrupt ends the wait with a 
> {{BinaryObjectException}}, {{BPlusTree.invoke()}} reports it as 
> {{CorruptedTreeException}}, and the failure handler stops the node, although 
> nothing is corrupted.
> IGNITE-29241 fixes the same problem for {{put}}, {{putAll}}, {{invoke}} and 
> {{invokeAll}} by making the bytes of the new value before the tree. It 
> doesn't cover the interceptor, because on this path the interceptor's value 
> only exists inside the tree.
> h3. Reproducer
> In {{AtomicPutInterruptTest}} (added by IGNITE-29241):
> {code:java}
> /** The interceptor replaces the new value with an Externalizable value 
> inside the cache update. */
> @Test
> public void 
> testInterruptedPutWithInterceptorReturningUnregisteredExternalizableClass() 
> throws Exception {
>     IgniteCache<Integer, Object> cache = grid(0).createCache(new 
> CacheConfiguration<Integer, Object>("interceptor")
>         .setAtomicityMode(ATOMIC)
>         .setInterceptor(new ExternalizableInterceptor()));
>     checkInterruptedUpdateFails(() -> cache.put(1, 1));
>     cache.put(1, 2);
>     assertEquals(2, ((ExternalizableValue)cache.get(1)).val);
> }
> /** Replaces the new value with an Externalizable value, so the value is 
> created inside the cache update. */
> private static class ExternalizableInterceptor extends 
> CacheInterceptorAdapter<Integer, Object> {
>     /** {@inheritDoc} */
>     @Override public Object onBeforePut(Cache.Entry<Integer, Object> entry, 
> Object newVal) {
>         return new ExternalizableValue((Integer)newVal);
>     }
> }
> {code}
> Expected: the put fails, and the failure handler isn't called.
> Actual: the failure handler is called with {{CorruptedTreeException}}, caused 
> by {{BinaryObjectException: Failed to register class}} from 
> {{AtomicCacheUpdateClosure.update()}} -> {{prepareForCache()}}, caused by 
> {{IgniteInterruptedCheckedException}}.
> h3. Notes
> * Moving {{onBeforePut()}} before the tree isn't a simple change: inside the 
> tree it runs after the checks that can still reject the update (version 
> check, filter, conflict resolution, expiry), so calling it earlier would also 
> call it for updates that are rejected later.
> * In {{GridDhtAtomicCache.updateWithBatch()}} (multi-key update with a 
> write-through store), the interceptor already runs before the tree, so the 
> bytes of its value can be made right after it, as IGNITE-29241 does for the 
> entry processor's value.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to