Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial import of clustered tutorial example
  • Loading branch information
pmuir committed Feb 4, 2011
1 parent 7b674e8 commit f5d0900
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
tutorial/clustered/.gitignore
tutorial/clustered/.classpath
tutorial/clustered/.project
tutorial/clustered/.settings
tutorial/clustered/target

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Pete, rather than adding ignores for only tutorial/cluster, why not just list them without the folder name? That way you don't have to repeat them for each example.

Also, why do u wanna ignore .gitignore in tutorial/clustered?

This comment has been minimized.

Copy link
@pmuir

pmuir Feb 4, 2011

Author Owner

Hah, hangover from when I had this in another git repo.

1 change: 1 addition & 0 deletions tutorial/clustered/README.md
@@ -0,0 +1 @@
The code for the tutorial at http://community.jboss.org/wiki/SettingupanInfinispanCluster#43_A_complete_example
64 changes: 64 additions & 0 deletions tutorial/clustered/pom.xml
@@ -0,0 +1,64 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.infinispan.examples.tutorial.clustered</groupId>
<artifactId>infinispan-clustered-tutorial</artifactId>
<version>1.0.0-SNAPSHOT</version>

<properties>
<infinispan.version>4.2.0.FINAL</infinispan.version>
</properties>

<dependencies>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<version>${infinispan.version}</version>
</dependency>
</dependencies>


<profiles>
<profile>
<id>jboss-public-repository</id>
<activation>
<property>
<name>jboss-public-repository</name>
<value>!false</value>
</property>
</activation>
<repositories>
<repository>
<id>jboss-public-repository-group</id>
<name>JBoss Public Maven Repository Group</name>
<url>http://repository.jboss.org/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>jboss-public-repository-group</id>
<name>JBoss Public Maven Repository Group</name>
<url>http://repository.jboss.org/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>

</profiles>

</project>
@@ -0,0 +1,35 @@
package org.infinispan.examples.tutorial.clustered;

import java.util.logging.Logger;

import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;

/**
* An Infinispan listener that simply logs cache entries being created and
* removed
*
* @author Pete Muir
*
*/
@Listener
public class LoggingListener {

private transient Logger log = Logger.getLogger("tutorial");

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Why do you make the logger transient? The class is not Serializable, so isn't this clutter? Personally I prefer private static loggers since it avoids creating multiple loggers for each instance, but that's more of a personal preference.

This comment has been minimized.

Copy link
@pmuir

pmuir Feb 4, 2011

Author Owner

static loggers are a major cause of classloader related memory leaks, and modern logging frameworks like jboss logging can cope with reusing existing logger instances rather than creating a new one each time. But in this case the transient is not needed, I agree, just habit.

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Well, classloader issues came from poor loggers themselves such as Apache Commons Logging, but I get your point.


@CacheEntryCreated
public void observeAdd(CacheEntryCreatedEvent<?, ?> event) {
log.info("Cache entry with key " + event.getKey() + " added in cache "

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Since it's an example, maybe we wanna show log mesages using String.format() or use Infinispan's Log that can be used with %s like substitutions? I find it easier to read than concatenating strings.

This comment has been minimized.

Copy link
@pmuir

pmuir Feb 4, 2011

Author Owner

Thanks, I will change it.

+ event.getCache());
}

@CacheEntryRemoved
public void observeRemove(CacheEntryRemovedEvent<?, ?> event) {
log.info("Cache entry with key " + event.getKey() + " removed in cache "
+ event.getCache());
}

}
@@ -0,0 +1,34 @@
package org.infinispan.examples.tutorial.clustered;

import org.infinispan.Cache;
import org.infinispan.config.Configuration;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.examples.tutorial.clustered.util.ClusterValidation;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;


public class Node0 {

public static void main(String[] args) throws Exception {

// Create the configuration, and set to replication
GlobalConfiguration gc = GlobalConfiguration.getClusteredDefault();
Configuration c = new Configuration();
c.setCacheMode(Configuration.CacheMode.REPL_SYNC);

// Create the cache manager and get a handle to the cache we will use
EmbeddedCacheManager cm = new DefaultCacheManager(gc, c);
Cache<String, String> cache = cm.getCache("Demo");

// Add a listener so that we can see the put from Node1
cache.addListener(new LoggingListener());

// Wait for the cluster to form, erroring if it doesn't form after the
// timeout
if (!ClusterValidation.waitForClusterToForm(cm, 0, 2)) {
throw new IllegalStateException("Error forming cluster, check the log");
}
}

}
@@ -0,0 +1,34 @@
package org.infinispan.examples.tutorial.clustered;

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Hmmmm, Node1 contains pretty much the same code as Node0, why not create a common parent with the code and have Node1 and Node0 extend it? Avoids code repetition.

This comment has been minimized.

Copy link
@pmuir

pmuir Feb 4, 2011

Author Owner

Good idea, will do.


import org.infinispan.Cache;
import org.infinispan.config.Configuration;
import org.infinispan.config.GlobalConfiguration;
import org.infinispan.examples.tutorial.clustered.util.ClusterValidation;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;


public class Node1 {

public static void main(String[] args) throws Exception {

// Create the configuration, and set to replication
GlobalConfiguration gc = GlobalConfiguration.getClusteredDefault();
Configuration c = new Configuration();
c.setCacheMode(Configuration.CacheMode.REPL_SYNC);

// Create the cache manager and get a handle to the cache we will use
EmbeddedCacheManager cm = new DefaultCacheManager(gc, c);
Cache<String, String> cache = cm.getCache("Demo");

// Wait for the cluster to form, erroring if it doesn't form after the timeout
if (!ClusterValidation.waitForClusterToForm(cm, 1, 2))
{
throw new IllegalStateException("Error forming cluster, check the log");
}

// Put some information in the cache that we can display on the other node
cache.put("key", "value");
}

}
@@ -0,0 +1,127 @@
package org.infinispan.examples.tutorial.clustered.util;

import org.infinispan.Cache;
import org.infinispan.manager.EmbeddedCacheManager;

/**
* Utility class that knows how to wait until a cluster forms
*
* @author Pete Muir
*
*/
public class ClusterValidation {

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

Good stuff creating this class. Maybe we should include it as part of infinispan-core (org.infinispan.util ?) to make it easier for people to verify cluster formation in their code?

This comment has been minimized.

Copy link
@galderz

galderz Feb 4, 2011

In fact, this could be taken further by making it an interceptor? For example: "I know my cluster must be formed of N nodes, so hold any invocations until I have N members in the cluster".

UPDATE: For this class to be convertable to an interceptor, the impl would likely need to vary and not rely on a cache operation itself, so consider the concept here more than the implementation.

This comment has been minimized.

Copy link
@pmuir

pmuir Feb 4, 2011

Author Owner

Makes sense to me. If it were an interceptor, I would suggest having this class provide the utility, and then an interceptor which uses it.

I'll also replace the System.out here with log statements, no idea why I didn't do that in the first place!


private static int REPLICATION_TRY_COUNT = 60;
private static int REPLICATION_TIME_SLEEP = 2000;

/**
* The time to wait for the cluster to form
*/
public static int TIMEOUT = REPLICATION_TIME_SLEEP * REPLICATION_TIME_SLEEP;

private static final String KEY = ClusterValidation.class.getName();

/**
* Waits for the cluster to form. You must call this method on every node in
* the cluster.
*
* @param cacheManager
* the {@link EmbeddedCacheManager} to use to check if the cluster
* has formed
* @param nodeId
* the id of the current node
* @param clusterSize
* the number of nodes in the cluster
* @return true if the cluster formed within the time out, otherwise false
*/
public static boolean waitForClusterToForm(
EmbeddedCacheManager cacheManager, int nodeId, int clusterSize) {
return new ClusterValidation(cacheManager.getCache(KEY), nodeId,
clusterSize).checkReplicationSeveralTimes() > 0;
}

private final Cache<Object, Object> cache;
private final int clusterSize;
private final int nodeId;

private ClusterValidation(Cache<Object, Object> cache, int nodeId,
int clusterSize) {
this.cache = cache;
this.clusterSize = clusterSize;
this.nodeId = nodeId;
}

private int checkReplicationSeveralTimes() {

for (int i = 0; i < REPLICATION_TRY_COUNT; i++) {
// Try to put our key into the cache
tryToPut();
// Wait for a bit to allow replication and wait for the other node to
// start
try {
Thread.sleep(REPLICATION_TIME_SLEEP);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
int replCount = replicationCount(clusterSize);
if (replCount == clusterSize - 1) {
// The cluster has formed if there are n-1 replicas in the cluster
System.out.println("Cluster formed successfully!");
// We try to put one last time to ensure that *our* key is now in
// the cluster so that other nodes see it!
tryToPut();
return replCount;
}

}
// Give up
System.out.println("Cluster failed to form!");
return -1;
}

private void tryToPut() {
int tryCount = 0;
while (tryCount < 5) {
try {
cache.put(key(nodeId), "true");
return;
} catch (Throwable e) {
tryCount++;
}
}
throw new IllegalStateException(
"Couldn't accomplish addition before replication!");
}

private int replicationCount(int clusterSize) {
int replicaCount = 0;
for (int i = 0; i < clusterSize; i++) {
if (i == nodeId) {
continue;
}
Object data = tryGet(i);
if (data == null || !"true".equals(data)) {
} else {
replicaCount++;
}
}
return replicaCount;
}

private Object tryGet(int i) {
int tryCont = 0;
while (tryCont < 5) {
try {
return cache.get(key(i));
} catch (Throwable e) {
tryCont++;
}
}
return null;
}

private String key(int slaveIndex) {
return KEY + slaveIndex;
}

}

0 comments on commit f5d0900

Please sign in to comment.