1 Reply Latest reply: Dec 4, 2012 7:57 AM by user123799 RSS

    Sticky Eviction Trigger - Issue with no expiry deco on new binary.

    user123799
      Hi fellow forumers,

      I'm attempting to write a 'StickyEvictionTrigger' which, when attached to a cache, causes eviction settings of an entry to stick even if it is later updated without an eviction, i.e.
      cache.put(1,1, 1000);   // put entry into cache with an explicit eviction
      cache.put(1,2);            // update the entry without eviction - this normally clears the eviction, but not with StickyEvictionTrigger.
      Here's the trigger's WIP/test code:
      public class StickyEvictionTrigger implements MapTrigger, PortableObject {
          @Override
          public void process(Entry entry) {
              final BinaryEntry be = (BinaryEntry) entry;
      
              if (be.getOriginalBinaryValue() == null ||
                  be.getBinaryValue() == null) {
                  // Insert or delete - nothing to do:
                  return;
              }
      
              // Update:
              handleUpdate(be);
          }
      
          private void handleUpdate(BinaryEntry entry) {
              try {
                  // extract the relative eviction time in millisecs from the old value:
                  Eviction oldEviction = new Eviction();
                  if (!extractEviction(entry.getOriginalBinaryValue(), oldEviction)) {
                      return; // No old eviction set
                  }
      
                  if (extractEviction(entry.getBinaryValue(), null)) {
                      return; // New value has eviction explicitly set, leave it alone.
                  }
      
                  // Stick the eviction on the new value:
                  entry.expire(oldEviction.value);
      
              } catch (IOException e) {
                  throw new IllegalStateException("Failed to extract ttl from Binary");
              }
          }
      
          private boolean extractEviction(Binary binary, Eviction out) throws IOException {
              // extract the relative eviction time in millisecs from the old value:
              final Binary binEviction = ExternalizableHelper.getDecoration(binary, ExternalizableHelper.DECO_EXPIRY);
              if (binEviction == null) {
                  return false; // No eviction set
              }
      
              long ttl = binEviction.getBufferInput().readLong();
      
              if (ttl == CacheMap.EXPIRY_DEFAULT) {
                  return false;  // No eviction set:
              }
      
              if (out != null) {
                  if (ttl != CacheMap.EXPIRY_NEVER) {
                      // Make relative:
                      ttl -= System.currentTimeMillis();
                  }
      
                  out.value = ttl;
              }
              return true;
          }
      
          private static class Eviction {
              public long value;
          }
      
         ...
      }
      For the basic case of 'expiry is set on an entry and then an update comes in with no expiry' shown above this works well - the previously set expiry is maintained and the entry eventually expires - nice!

      However, allowing clients to set a new eviction ttl in the new value doesn't work e.g.
      cache.put(1,1, 1000);   // put entry into cache with an explicit eviction
      cache.put(1,1, 100);     // Actually, I want to change the eviction. - Unfortunately, this doesn't work with StickyEvictionTrigger
      The reason the above code doesn't work is that the new binary doesn't have an expiry decoration, even though the update is coming from a cache.put(key, value, expiry) call i.e. 'ExternalizableHelper.getDecoration(binaryEntry.getBinaryValue(), ExternalizableHelper.DECO_EXPIRY)' returns null. It appears that this decoration is added post-listener calls!

      Does anyone know if this is the case and I've not just missed something?
      Does anyone know if this is intentional or a bug (I'm using 3.7.1.6 - haven't tried 3.7.1.7 yet) - it doesn't feel like a bug.
      Does anyone know another way of skinning this cat?

      TBH not supporting the ability to set a new eviction is not a problem right now for me as I don't need this functionality - but I think this makes this trigger most generic / useful / reusable i.e. another tool in the tool box.

      Thanks,

      Andy
        • 1. Re: Sticky Eviction Trigger - Issue with no expiry deco on new binary.
          user123799
          Don't you just hate it when you realise your mistake just after clicking post? OK, so with more thought - this isn't an issue, why? Because the very fact that Coherence seems to apply the expiry after the call to the trigger means that calls that explicitly set the expiry still work as expected i.e. set a new expiry. This simplifies my code to that given below (in case this is of use to others).

          One thing I did notice while testing this is that BinaryEntry.expire(CacheMap.EXPIRY_NEVER) immediately evicts the item, even on a scheme using a local-scheme backing map - which isn't want the documentation says. Does anyone know if this is a bug or me missing something? (I'll leave this as an open question)

          StickyEvictionTrigger code (note: not fully tested):
          public class StickyEvictionTrigger implements MapTrigger, PortableObject {
          
              @Override
              public void process(Entry entry) {
          
                  final BinaryEntry be = (BinaryEntry) entry;
                  if (be.getOriginalBinaryValue() == null ||
                      be.getBinaryValue() == null) {
                      // Insert or delete - nothing to do:
                      return;
                  }
          
                  // Update:
                  handleUpdate(be);
              }
          
              private void handleUpdate(BinaryEntry entry) {
                  try {
                      // extract the relative eviction time in millisecs from the old value:
                      final Binary binEviction = ExternalizableHelper.getDecoration(entry.getOriginalBinaryValue(), ExternalizableHelper.DECO_EXPIRY);
                      if (binEviction == null) {
                          return; // No eviction set
                      }
          
                      long ttl = binEviction.getBufferInput().readLong();
                      if (ttl == CacheMap.EXPIRY_DEFAULT) {
                          return;  // No eviction set:
                      }
          
                      if (ttl != CacheMap.EXPIRY_NEVER) {    // Note: Current bug/feature means EXPIRY_NEVER doesn't stick - item is evicted!
                          // Make relative:
                          ttl -= System.currentTimeMillis();
                      }
          
                      // Stick the eviction on the new value (will be overwritten if new value explicitly sets eviction):
                      entry.expire(ttl);
          
                  } catch (IOException e) {
                      throw new IllegalStateException("Failed to extract ttl from Binary");
                  }
              }
          
              public static MapListener getListener() {
                  return new MapTriggerListener(new StickyEvictionTrigger());
              }
          
              @Override
              public void readExternal(PofReader pofReader) throws IOException {
              }
          
              @Override
              public void writeExternal(PofWriter pofWriter) throws IOException {
              }
          }
          Edited by: BigAndy on 04-Dec-2012 05:56