Initialize

Below describes the best practices using EVCache client.

Governated Server

You can do this in your StartServer.java (or the class that extends BaseServerLifecycleListener).

public class StartServer extends BaseServerLifecycleListener {
    protected void initialize(ServletContextEvent event) throws Exception {
        Injector injector = getInjector();
        injector.getInstance(com.netflix.evcache.EVCacheClientModule.class);
    }
}

Client Library

While initializing EVCacheClient Library we recommend you can create connections to EVCache servers your library needs. You can achieve this by doing the following

public class MyClientLibrary {
    @Inject
    public MyClientLibrary(EVCache.Builder evCacheBuilder, <Other Librariess your client needs>) {
        ....
        EVCache evCache = evCacheBuilder
            .setAppName(<EVCACHE App Name in upper case>)
            .setCachePrefix("cid") //Set the optional cache prefix
            .enableRetry() //Enable Retry
            .setDefaultTTL(3600) //Set TTL for 1 Hour
            .setTranscoder(...) //The transcoder to serialize and deserialize data stored in evcache
            .enableExceptionPropagation() //If exceptions need to be propagated else null is returned in case of exception
            .build();
        ....
    }
}

Client Module

If you have a client moudle you can initialize EVCache as below

public class MyClientModule extends AbstractModule {

    @Override
    protected void configure() {
        install(new EVCacheClientModule());
        //install other modules
    }

}

To obtain EVCache instance the first step is to import the necessary packages and create the cache. Specify the EVCache App Name, cache prefix (all keys will be prepended by this name so choose wisely) and the Default TTL and build the instance. Optionally you can also specify the transcoder and if your instance needs to do zone fallback.

public class MyClass {
    @Inject
    public MyClass(EVCache.Builder evCacheBuilder, <Other Librariess your client needs>) {
            ....
            EVCache evCache = evCacheBuilder
                .setAppName(<EVCACHE App Name in upper case>)
                .setCachePrefix("cid") //Set the optional cache prefix
                .enableRetry() //Enable Retry
                .setDefaultTTL(3600) //Set TTL for 1 Hour
                .setTranscoder(...) //The transcoder to serialize and deserialize data stored in evcache
                .enableExceptionPropagation() //If exceptions need to be propagated else null is returned in case of exception
                .build();
            .....
    }
}

Adding Data to EVCache

To set data you need to pass the key and value. You can also use the overloaded method and pass your custom transcoder and TTL(time to live) for you the data you are setting.

Note: key cannot contain space or new line character

Values are limited to 20MB maximum (minus serialization overhead)

EVCacheLatch latch = client.set("key", "value", Policy.ALL_MINUS_1)

After the latch is returned, you can wait on it for however long your SLA allows to ensure the write policy was met. Else the operation is unblocked but the write will proceed in the background. Waiting shorter than the write timeout will allow your code to continue but you will not know if the write was successful or not. You can inspect the EVCache write metrics to get status of such operations.

boolean status = latch.await(20, TimeUnit.MILLISECONDS) 
// wait for 20 ms or until the policy is fulfilled, whichever is comes first
//status boolean will be true if the operation was successful

There are several options for the policy, depending on your use case:

public static enum Policy {
    ONE, QUORUM, ALL_MINUS_1, ALL
}

It is generally not recommended to wait for all copies to confirm a write. If you do use the ALL policy, then it is possible for a bad box to back up your application servers because all of the latches will wait until the operation timeout or error.

Another approach is to use the non latch based set() method that return Future[]

Future<Boolean>[] status = myCache.set("key","value");

You can inspect the status futures to ensure if that the data you have set is successful or not. Doing so will block the operation until we can determine if the operation was successful or not.

for(Future<Boolean> operationStatus :  status) {

    /*
        wait until the operation is completed. Note this blocks until the operation is completed. 
    */
    operationStatus.get() 

    //To wait up to a specified duration you can pass the max block duration
    operationStatus.get(50, TimeUnit.MILLISECONDS) 
    /*
        the future is blocked for utmost 50 milliseconds. 
        If the operation is not completed by then the operation is cancelled the the thread is unblocked. 
    */
}

Retrieving data from EVCache

To retrieve the value associated with a key:

String value = myCache.<String>get("key");

Delete data from EVCache:

To delete the data from EVCache

Future<Boolean>[] status = myCache.delete("key");

Similar to set operation you can inspect the status flag to determine if the operation was successful or not.

Writing Test Cases

To Init EVCache when AutoBinding is on

//Make sure to turn on autobinding by setting the below property
//platform.NFLifecycleUnitTester.ignoringAllAutoBindClasses=false

//Init NFLifecycleUnitTester 
final NFLifecycleUnitTester unitTester = new NFLifecycleUnitTester(props);

//Get instance of EVCache.Builder
final EVCache.Builder evCacheBuilder = unitTester.getInjector().getInstance(EVCache.Builder.class);

//Get instance of EVCache. Now you can perform all operations on it
final EVCache evCache = evCacheBuilder.setAppName("EVCACHE_CINEPS").setCacheName("cid").enableZoneFallback().build();

To Init EVCache when AutoBinding is off
//Make sure to turn off autobinding by setting the below property
//platform.NFLifecycleUnitTester.ignoringAllAutoBindClasses=true

//Init NFLifecycleUnitTester 
final NFLifecycleUnitTester unitTester = new NFLifecycleUnitTester(props, new EVCacheClientModule());

//Get instance of EVCache.Builder
final EVCache.Builder evCacheBuilder = unitTester.getInjector().getInstance(EVCache.Builder.class);

//Get instance of EVCache. Now you can perform all operations on it
final EVCache evCache = evCacheBuilder.setAppName("EVCACHE_CINEPS").setCacheName("cid").enableZoneFallback().build();

If you are seeing the following exception.

 No implementation for com.netflix.evcache.connection.IConnectionFactoryProvider was bound.
  while locating com.google.inject.Provider<com.netflix.evcache.connection.IConnectionFactoryProvider>
    for parameter 2 at com.netflix.evcache.pool.EVCacheClientPoolManager.<init>(EVCacheClientPoolManager.java:86)
  while locating com.netflix.evcache.pool.EVCacheClientPoolManager
    for field at com.netflix.evcache.EVCache$Builder._poolManager(EVCache.java:952)
  while locating com.netflix.evcache.EVCache$Builder
    for parameter 0 at com.netflix.ab.client.cache.common.ABEVCache.<init>(ABEVCache.java:47)
  while locating com.netflix.ab.client.cache.common.ABEVCache
    for parameter 0 at com.netflix.ab.client.ABServiceClientManager.<init>(ABServiceClientManager.java:41)
  while locating com.netflix.ab.client.ABServiceClientManager
    for parameter 0 at com.netflix.membership.client.MembershipClientLibrary.setAbServiceClientManager(MembershipClientLibrary.java:82)
  at com.netflix.membership.client.MembershipClientLibrary.setAbServiceClientManager(MembershipClientLibrary.java:82)
  while locating com.netflix.membership.client.MembershipClientLibrary
    for parameter 0 at com.netflix.msl.userauth.SwitchProfileAuthenticationFactory.<init>(SwitchProfileAuthenticationFactory.java:49)

You can also try to create LifecycleInjectorBuilder add EVCacheClientModule module to it. This should init all the necessary modules.

LifecycleInjectorBuilder builder = LifecycleInjector.builder();
builder.withModules( new EVCacheClientModule());

You might need to set the following properties if you are not able to talk to eureka

-D@environment=test
-D@region=us-west-2