@Test
  public void testPrimaryOwnerGoesDownAfterSendingEvent()
      throws InterruptedException, ExecutionException, TimeoutException {
    final Cache<Object, String> cache0 = cache(0, CACHE_NAME);
    Cache<Object, String> cache1 = cache(1, CACHE_NAME);
    Cache<Object, String> cache2 = cache(2, CACHE_NAME);

    ClusterListener clusterListener = listener();
    cache0.addListener(clusterListener);

    CheckPoint checkPoint = new CheckPoint();
    waitUntilNotificationRaised(cache1, checkPoint);
    checkPoint.triggerForever("pre_raise_notification_release");

    final MagicKey key = new MagicKey(cache1, cache2);
    Future<String> future =
        fork(
            new Callable<String>() {
              @Override
              public String call() throws Exception {
                return cache0.put(key, FIRST_VALUE);
              }
            });

    checkPoint.awaitStrict("post_raise_notification_invoked", 10, TimeUnit.SECONDS);

    // Kill the cache now - note this will automatically unblock the fork thread
    TestingUtil.killCacheManagers(cache1.getCacheManager());

    future.get(10, TimeUnit.SECONDS);

    // We should have received 2 events
    assertEquals(clusterListener.events.size(), 2);
    CacheEntryEvent<Object, String> event = clusterListener.events.get(0);
    assertEquals(event.getType(), Event.Type.CACHE_ENTRY_CREATED);
    CacheEntryCreatedEvent<Object, String> createEvent =
        (CacheEntryCreatedEvent<Object, String>) event;
    assertFalse(createEvent.isCommandRetried());
    assertEquals(createEvent.getKey(), key);
    assertEquals(createEvent.getValue(), FIRST_VALUE);

    event = clusterListener.events.get(1);
    // Since it was a retry but the backup got the write the event isn't a CREATE!!
    assertEquals(event.getType(), Event.Type.CACHE_ENTRY_MODIFIED);
    CacheEntryModifiedEvent<Object, String> modEvent =
        (CacheEntryModifiedEvent<Object, String>) event;
    assertTrue(modEvent.isCommandRetried());
    assertEquals(modEvent.getKey(), key);
    assertEquals(modEvent.getValue(), FIRST_VALUE);
  }
  public void testPrimaryOwnerGoesDownBeforeBackupRaisesEvent()
      throws InterruptedException, TimeoutException, ExecutionException, BrokenBarrierException {
    final Cache<Object, String> cache0 = cache(0, CACHE_NAME);
    Cache<Object, String> cache1 = cache(1, CACHE_NAME);
    Cache<Object, String> cache2 = cache(2, CACHE_NAME);

    ClusterListener clusterListener = new ClusterListener();
    cache0.addListener(clusterListener);

    // Now we want to block the outgoing put to the backup owner
    RpcManager rpcManager = TestingUtil.extractComponent(cache1, RpcManager.class);
    ControlledRpcManager controlledRpcManager = new ControlledRpcManager(rpcManager);
    controlledRpcManager.blockBefore(PutKeyValueCommand.class);
    TestingUtil.replaceComponent(cache1, RpcManager.class, controlledRpcManager, true);

    final MagicKey key = new MagicKey(cache1, cache2);
    Future<String> future =
        fork(
            new Callable<String>() {
              @Override
              public String call() throws Exception {
                return cache0.put(key, FIRST_VALUE);
              }
            });

    // Wait until the primary owner has sent the put command successfully to  backup
    controlledRpcManager.waitForCommandToBlock(10, TimeUnit.SECONDS);

    // Kill the cache now
    TestingUtil.killCacheManagers(cache1.getCacheManager());

    // This should return null normally, but since it was retried it returns it's own value :(
    // Maybe some day this can work properly
    assertEquals(null, future.get(10, TimeUnit.SECONDS));

    // We should have received an event that was marked as retried
    assertEquals(clusterListener.events.size(), 1);
    CacheEntryEvent<Object, String> event = clusterListener.events.get(0);
    // Since it was a retry but the backup got the write the event isn't a CREATE!!
    assertEquals(event.getType(), Event.Type.CACHE_ENTRY_CREATED);
    CacheEntryCreatedEvent<Object, String> modEvent =
        (CacheEntryCreatedEvent<Object, String>) event;
    assertTrue(modEvent.isCommandRetried());
    assertEquals(modEvent.getKey(), key);
    assertEquals(modEvent.getValue(), FIRST_VALUE);
  }
예제 #3
0
 @Override
 public void notifyClusterListeners(
     Collection<? extends CacheEntryEvent<K, V>> events, UUID uuid) {
   for (CacheEntryEvent<K, V> event : events) {
     if (event.isPre()) {
       throw new IllegalArgumentException(
           "Events for cluster listener should never be pre change");
     }
     switch (event.getType()) {
       case CACHE_ENTRY_MODIFIED:
         for (CacheEntryListenerInvocation<K, V> listener : cacheEntryModifiedListeners) {
           if (listener.isClustered() && uuid.equals(listener.getIdentifier())) {
             // We force invocation, since it means the owning node passed filters already and they
             // already converted so don't run converter either
             listener.invokeNoChecks(event, false, true);
           }
         }
         break;
       case CACHE_ENTRY_CREATED:
         for (CacheEntryListenerInvocation<K, V> listener : cacheEntryCreatedListeners) {
           if (listener.isClustered() && uuid.equals(listener.getIdentifier())) {
             // We force invocation, since it means the owning node passed filters already and they
             // already converted so don't run converter either
             listener.invokeNoChecks(event, false, true);
           }
         }
         break;
       case CACHE_ENTRY_REMOVED:
         for (CacheEntryListenerInvocation<K, V> listener : cacheEntryRemovedListeners) {
           if (listener.isClustered() && uuid.equals(listener.getIdentifier())) {
             // We force invocation, since it means the owning node passed filters already and they
             // already converted so don't run converter either
             listener.invokeNoChecks(event, false, true);
           }
         }
         break;
       default:
         throw new IllegalArgumentException("Unexpected event type encountered!");
     }
   }
 }
  private void verifyEvents(
      boolean isClustered, StateListener<String, String> listener, Map<String, String> expected) {
    assertEquals(listener.events.size(), isClustered ? expected.size() : expected.size() * 2);
    boolean isPost = true;
    for (CacheEntryEvent<String, String> event : listener.events) {
      // Even checks means it will be post and have a value - note we force every check to be
      // even for clustered since those should always be post
      if (!isClustered) {
        isPost = !isPost;
      }

      assertEquals(event.getType(), Event.Type.CACHE_ENTRY_CREATED);
      assertTrue(expected.containsKey(event.getKey()));
      assertEquals(event.isPre(), !isPost);
      if (isPost) {
        assertEquals(event.getValue(), expected.get(event.getKey()));
      } else {
        assertNull(event.getValue());
      }
    }
  }
    @Override
    public void onFilterResult(
        Object userContext,
        Object eventType,
        Object instance,
        Object[] projection,
        Comparable[] sortProjection) {
      CacheEntryEvent<K, V> event = (CacheEntryEvent<K, V>) userContext;
      if (event.isPre() && isClustered
          || isPrimaryOnly && !clusteringDependentLogic.localNodeIsPrimaryOwner(event.getKey())) {
        return;
      }

      DelegatingCacheEntryListenerInvocation<K, V>[] invocations;
      switch (event.getType()) {
        case CACHE_ENTRY_ACTIVATED:
          invocations = activated_invocations;
          break;
        case CACHE_ENTRY_CREATED:
          invocations = created_invocations;
          break;
        case CACHE_ENTRY_INVALIDATED:
          invocations = invalidated_invocations;
          break;
        case CACHE_ENTRY_LOADED:
          invocations = loaded_invocations;
          break;
        case CACHE_ENTRY_MODIFIED:
          invocations = modified_invocations;
          break;
        case CACHE_ENTRY_PASSIVATED:
          invocations = passivated_invocations;
          break;
        case CACHE_ENTRY_REMOVED:
          invocations = removed_invocations;
          break;
        case CACHE_ENTRY_VISITED:
          invocations = visited_invocations;
          break;
        case CACHE_ENTRY_EVICTED:
          invocations = evicted_invocations;
          break;
        case CACHE_ENTRY_EXPIRED:
          invocations = expired_invocations;
          break;
        default:
          return;
      }

      boolean conversionDone = false;
      for (DelegatingCacheEntryListenerInvocation<K, V> invocation : invocations) {
        if (invocation.getObservation().shouldInvoke(event.isPre())) {
          if (!conversionDone) {
            if (filterAndConvert
                && event
                    instanceof
                    EventImpl) { // todo [anistor] can it not be an EventImpl? can it not be
                                 // filterAndConvert?
              EventImpl<K, V> eventImpl = (EventImpl<K, V>) event;
              EventImpl<K, V> clone = eventImpl.clone();
              clone.setValue(
                  (V)
                      makeFilterResult(
                          userContext,
                          eventType,
                          event.getKey(),
                          projection == null ? instance : null,
                          projection,
                          sortProjection));
              event = clone;
            }
            conversionDone = true;
          }

          invocation.invokeNoChecks(
              new EventWrapper<>(event.getKey(), event), false, filterAndConvert);
        }
      }
    }
 private void injectFailure(CacheEntryEvent event) {
   if (injectFailure) {
     if (isInjectInPre && event.isPre()) throwSuspectException();
     else if (!isInjectInPre && !event.isPre()) throwSuspectException();
   }
 }
  protected void testIterationBeganAndSegmentNotComplete(
      final StateListener<String, String> listener,
      Operation operation,
      boolean shouldBePrimaryOwner)
      throws TimeoutException, InterruptedException, ExecutionException {
    final Map<String, String> expectedValues = new HashMap<String, String>(10);
    final Cache<String, String> cache = cache(0, CACHE_NAME);
    for (int i = 0; i < 10; i++) {
      String key = "key-" + i;
      String value = "value-" + i;
      expectedValues.put(key, value);
      cache.put(key, value);
    }

    String value;
    String keyToChange =
        findKeyBasedOnOwnership(
            expectedValues.keySet(),
            cache.getAdvancedCache().getDistributionManager().getConsistentHash(),
            shouldBePrimaryOwner,
            cache.getCacheManager().getAddress());

    switch (operation) {
      case CREATE:
        keyToChange = "new-key";
        value = "new-value";
        break;
      case PUT:
        value = cache.get(keyToChange) + "-changed";
        break;
      case REMOVE:
        value = null;
        break;
      default:
        throw new IllegalArgumentException("Unsupported Operation provided " + operation);
    }

    CheckPoint checkPoint = new CheckPoint();
    int segmentToUse =
        cache
            .getAdvancedCache()
            .getDistributionManager()
            .getConsistentHash()
            .getSegment(keyToChange);

    // do the operation, which should put it in the queue.
    ClusterCacheNotifier notifier = waitUntilClosingSegment(cache, segmentToUse, checkPoint);

    Future<Void> future =
        fork(
            new Callable<Void>() {

              @Override
              public Void call() throws Exception {
                cache.addListener(listener);
                return null;
              }
            });

    try {
      checkPoint.awaitStrict("pre_complete_segment_invoked", 10, TimeUnit.SECONDS);
      Object oldValue = operation.perform(cache, keyToChange, value);

      // Now let the iteration complete
      checkPoint.triggerForever("pre_complete_segment_released");

      future.get(10, TimeUnit.SECONDS);

      boolean isClustered = isClustered(listener);

      // We should have 1 or 2 (local) events due to the modification coming after we iterated on
      // it.  Note the value
      // isn't brought up until the iteration is done
      assertEquals(
          listener.events.size(),
          isClustered ? expectedValues.size() + 1 : (expectedValues.size() + 1) * 2);

      // If it is clustered, then the modify can occur in the middle.  In non clustered we have to
      // block all events
      // just in case of tx event listeners (ie. tx start/tx end would have to wrap all events) and
      // such so we can't
      // release them early.  The cluster listeners aren't affected by transaction since it those
      // are not currently
      // supported
      if (isClustered) {

        CacheEntryEvent event = null;
        boolean foundEarlierCreate = false;
        // We iterate backwards so we only have to do it once
        for (int i = listener.events.size() - 1; i >= 0; --i) {
          CacheEntryEvent currentEvent = listener.events.get(i);
          if (currentEvent.getKey().equals(keyToChange)
              && operation.getType() == currentEvent.getType()) {
            if (event == null) {
              event = currentEvent;
              // We can remove safely since we are doing backwards counter as well
              listener.events.remove(i);

              // If it is a create there is no previous create
              if (operation.getType() == Event.Type.CACHE_ENTRY_CREATED) {
                foundEarlierCreate = true;
                break;
              }
            } else {
              fail("There should only be a single event in the event queue!");
            }
          } else if (event != null
              && (foundEarlierCreate = event.getKey().equals(currentEvent.getKey()))) {
            break;
          }
        }
        // This should have been set
        assertTrue(
            foundEarlierCreate, "There was no matching create event for key " + event.getKey());

        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), false);
        assertEquals(event.getValue(), value);
      }

      // Assert the first 10/20 since they should all be from iteration - this may not work since
      // segments complete earlier..
      boolean isPost = true;
      int position = 0;
      for (;
          position < (isClustered ? expectedValues.size() : expectedValues.size() * 2);
          ++position) {
        // Even checks means it will be post and have a value - note we force every check to be
        // even for clustered since those should always be post
        if (!isClustered) {
          isPost = !isPost;
        }

        CacheEntryEvent event = listener.events.get(position);

        assertEquals(event.getType(), Event.Type.CACHE_ENTRY_CREATED);
        assertTrue(expectedValues.containsKey(event.getKey()));
        assertEquals(event.isPre(), !isPost);
        if (isPost) {
          assertEquals(event.getValue(), expectedValues.get(event.getKey()));
        } else {
          assertNull(event.getValue());
        }
      }

      // We should have 2 extra events at the end which are our modifications
      if (!isClustered) {
        CacheEntryEvent<String, String> event = listener.events.get(position);
        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), true);
        assertEquals(event.getKey(), keyToChange);
        assertEquals(event.getValue(), oldValue);

        event = listener.events.get(position + 1);
        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), false);
        assertEquals(event.getKey(), keyToChange);
        assertEquals(event.getValue(), value);
      }
    } finally {
      TestingUtil.replaceComponent(cache, CacheNotifier.class, notifier, true);
      TestingUtil.replaceComponent(cache, ClusterCacheNotifier.class, notifier, true);
      cache.removeListener(listener);
    }
  }
  /** This test is to verify that the modification event is sent after the creation event is done */
  private void testModificationAfterIterationBeganAndCompletedSegmentValueOwner(
      final StateListener<String, String> listener,
      Operation operation,
      boolean shouldBePrimaryOwner)
      throws IOException, InterruptedException, TimeoutException, BrokenBarrierException,
          ExecutionException {
    final Map<String, String> expectedValues = new HashMap<String, String>(10);
    final Cache<String, String> cache = cache(0, CACHE_NAME);
    for (int i = 0; i < 10; i++) {
      String key = "key-" + i;
      String value = "value-" + i;
      expectedValues.put(key, value);
      cache.put(key, value);
    }

    CheckPoint checkPoint = new CheckPoint();

    InterceptorChain chain =
        mockStream(
            cache,
            (mock, real, additional) -> {
              doAnswer(
                      i -> {
                        // Wait for main thread to sync up
                        checkPoint.trigger("pre_close_iter_invoked");
                        // Now wait until main thread lets us through
                        checkPoint.awaitStrict("pre_close_iter_released", 10, TimeUnit.SECONDS);
                        return i.getMethod().invoke(real, i.getArguments());
                      })
                  .when(mock)
                  .close();

              doAnswer(i -> i.getMethod().invoke(real, i.getArguments())).when(mock).iterator();
            });

    try {
      Future<Void> future =
          fork(
              () -> {
                cache.addListener(listener);
                return null;
              });

      checkPoint.awaitStrict("pre_close_iter_invoked", 10, TimeUnit.SECONDS);

      String value;
      String keyToChange =
          findKeyBasedOnOwnership(
              expectedValues.keySet(),
              cache.getAdvancedCache().getDistributionManager().getConsistentHash(),
              shouldBePrimaryOwner,
              cache.getCacheManager().getAddress());

      switch (operation) {
        case CREATE:
          keyToChange = "new-key";
          value = "new-value";
          break;
        case PUT:
          value = cache.get(keyToChange) + "-changed";
          break;
        case REMOVE:
          value = null;
          break;
        default:
          throw new IllegalArgumentException("Unsupported Operation provided " + operation);
      }

      Object oldValue = operation.perform(cache, keyToChange, value);

      // Now let the iteration complete
      checkPoint.triggerForever("pre_close_iter_released");

      future.get(10, TimeUnit.SECONDS);

      boolean isClustered = isClustered(listener);

      // We should have 1 or 2 (local) events due to the modification coming after we iterated on
      // it.  Note the value
      // isn't brought up until the iteration is done
      assertEquals(
          listener.events.size(),
          isClustered ? expectedValues.size() + 1 : (expectedValues.size() + 1) * 2);

      // Assert the first 10/20 since they should all be from iteration - this may not work since
      // segments complete earlier..
      boolean isPost = true;
      int position = 0;
      for (;
          position < (isClustered ? expectedValues.size() : expectedValues.size() * 2);
          ++position) {
        // Even checks means it will be post and have a value - note we force every check to be
        // even for clustered since those should always be post
        if (!isClustered) {
          isPost = !isPost;
        }

        CacheEntryEvent event = listener.events.get(position);

        assertEquals(event.getType(), Event.Type.CACHE_ENTRY_CREATED);
        assertTrue(expectedValues.containsKey(event.getKey()));
        assertEquals(event.isPre(), !isPost);
        if (isPost) {
          assertEquals(event.getValue(), expectedValues.get(event.getKey()));
        } else {
          assertNull(event.getValue());
        }
      }

      // We should have 2 extra events at the end which are our modifications
      if (isClustered) {
        CacheEntryEvent<String, String> event = listener.events.get(position);
        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), false);
        assertEquals(event.getKey(), keyToChange);
        assertEquals(event.getValue(), value);
      } else {
        CacheEntryEvent<String, String> event = listener.events.get(position);
        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), true);
        assertEquals(event.getKey(), keyToChange);
        assertEquals(event.getValue(), oldValue);

        event = listener.events.get(position + 1);
        assertEquals(event.getType(), operation.getType());
        assertEquals(event.isPre(), false);
        assertEquals(event.getKey(), keyToChange);
        assertEquals(event.getValue(), value);
      }
    } finally {
      TestingUtil.replaceComponent(cache, InterceptorChain.class, chain, true);
      cache.removeListener(listener);
    }
  }
 @CacheEntryActivated
 public void handleActivated(CacheEntryEvent e) {
   if (e.isPre()) activated.add(e.getKey());
 }
 @CacheEntryLoaded
 public void handleLoaded(CacheEntryEvent e) {
   if (e.isPre()) loaded.add(e.getKey());
 }