Skip to content

Commit

Permalink
nfs: introduce PoolDeviceMap for pool to deviceid mapping
Browse files Browse the repository at this point in the history
Acked-by: Karsten Schwank
Target: master
Require-book: no
Require-notes: no
  • Loading branch information
kofemann committed Dec 13, 2013
1 parent d6b2d87 commit 09769a3
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 30 deletions.
Expand Up @@ -4,6 +4,7 @@
package org.dcache.chimera.nfsv41.door;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.glassfish.grizzly.Buffer;
import org.slf4j.Logger;
Expand All @@ -18,7 +19,6 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -104,11 +104,10 @@ public class NFSv41Door extends AbstractCellComponent implements

private static final Logger _log = LoggerFactory.getLogger(NFSv41Door.class);

/** dCache-friendly NFS device id to pool name mapping */
private final Map<String, PoolDS> _poolNameToIpMap = new HashMap<>();

/** All known devices */
private final Map<deviceid4, PoolDS> _deviceMap = new HashMap<>();
/**
* A mapping between pool name, nfs device id and pool's ip addresses.
*/
private final PoolDeviceMap _poolDeviceMap = new PoolDeviceMap();

/** next device id, 0 reserved for MDS */
private final AtomicInteger _nextDeviceID = new AtomicInteger(1);
Expand Down Expand Up @@ -289,26 +288,17 @@ public void messageArrived(PoolPassiveIoFileMessage<stateid4> message) {
_log.debug("NFS mover ready: {}", poolName);

InetSocketAddress[] poolAddress = message.socketAddresses();
PoolDS device = _poolNameToIpMap.get(poolName);
PoolDS device = _poolDeviceMap.getByPoolName(poolName);

if (device == null || !Arrays.equals(device.getInetSocketAddress(), poolAddress)) {
/* pool is unknown yet or has been restarted so create new device and device-id */
int id = this.nextDeviceID();

if (device != null) {
/*
* clean stale entry
*/
deviceid4 oldId = device.getDeviceId();
_deviceMap.remove(oldId);
}

deviceid4 deviceid = deviceidOf(id);
device = new PoolDS(deviceid, _stripingPattern, poolAddress);
final int id = this.nextDeviceID();
final deviceid4 deviceid = deviceidOf(id);
final PoolDS newDevice = new PoolDS(deviceid, _stripingPattern, poolAddress);

_poolNameToIpMap.put(poolName, device);
_deviceMap.put(deviceid, device);
_log.debug("new mapping: {}", device);
_log.debug("new mapping: {}", newDevice);
_poolDeviceMap.add(poolName, newDevice);
device = newDevice;
}

stateid4 stateid = message.challange();
Expand Down Expand Up @@ -357,7 +347,7 @@ public device_addr4 getDeviceInfo(CompoundContext context, deviceid4 deviceId) {
new InetSocketAddress[] { context.getRpcCall().getTransport().getLocalSocketAddress() } );
}

PoolDS ds = _deviceMap.get(deviceId);
PoolDS ds = _poolDeviceMap.getByDeviceId(deviceId);
if( ds == null) {
return null;
}
Expand Down Expand Up @@ -460,11 +450,7 @@ private PoolDS getPool(NfsTransfer transfer, NFS4ProtocolInfo protocolInfo, int

@Override
public List<deviceid4> getDeviceList(CompoundContext context) {
List<deviceid4> knownDevices = new ArrayList<>();

knownDevices.addAll(_deviceMap.keySet());

return knownDevices;
return Lists.newArrayList(_poolDeviceMap.getDeviceIds());
}


Expand Down Expand Up @@ -511,7 +497,7 @@ public void getInfo(PrintWriter pw) {
if (_nfs4 != null) {
pw.printf(" IO queue: %s\n", _ioQueue);
pw.println(" Known pools (DS):\n");
for (Map.Entry<String, PoolDS> ioDevice : _poolNameToIpMap.entrySet()) {
for (Map.Entry<String, PoolDS> ioDevice : _poolDeviceMap.getEntries()) {
pw.println(String.format(" %s : [%s]", ioDevice.getKey(), ioDevice.getValue()));
}

Expand Down Expand Up @@ -573,7 +559,7 @@ private static deviceid4 deviceidOf(int id) {
return new deviceid4(deviceidBytes);
}

private static class PoolDS {
static class PoolDS {

private final deviceid4 _deviceId;
private final InetSocketAddress[] _socketAddress;
Expand Down
@@ -0,0 +1,91 @@
package org.dcache.chimera.nfsv41.door;

import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.dcache.nfs.v4.xdr.deviceid4;
import org.dcache.chimera.nfsv41.door.NFSv41Door.PoolDS;

/**
* A mapping between pool name, nfs device id and pool's ip addresses.
*/
public class PoolDeviceMap {

/**
* dCache-friendly NFS device id to pool name mapping
*/
private final Map<String, PoolDS> _poolNameToIpMap = new HashMap<>();

/**
* All known devices
*/
private final Map<deviceid4, PoolDS> _deviceMap = new HashMap<>();

/*
* as there are mostly reads, use read-write locks to increase concurrency
*/
private final ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock _rlock = _lock.readLock();
private final ReentrantReadWriteLock.WriteLock _wlock = _lock.writeLock();

void add(String poolName, PoolDS device) {
_wlock.lock();
try {
PoolDS oldDevice = _poolNameToIpMap.put(poolName, device);
// remove dead entry
if (oldDevice != null) {
_deviceMap.remove(oldDevice.getDeviceId());
}
_deviceMap.put(device.getDeviceId(), device);
} finally {
_wlock.unlock();
}
}

Collection<PoolDS> getDevices() {
_rlock.lock();
try {
return ImmutableSet.copyOf(_poolNameToIpMap.values());
} finally {
_rlock.unlock();
}
}

Collection<deviceid4> getDeviceIds() {
_rlock.lock();
try {
return ImmutableSet.copyOf(_deviceMap.keySet());
} finally {
_rlock.unlock();
}
}

synchronized PoolDS getByPoolName(String name) {
_rlock.lock();
try {
return _poolNameToIpMap.get(name);
} finally {
_rlock.unlock();
}
}

synchronized PoolDS getByDeviceId(deviceid4 deviceId) {
_rlock.lock();
try {
return _deviceMap.get(deviceId);
} finally {
_rlock.unlock();
}
}

synchronized Collection<Map.Entry<String,PoolDS>> getEntries() {
_rlock.lock();
try {
return ImmutableSet.copyOf(_poolNameToIpMap.entrySet());
} finally {
_rlock.unlock();
}
}
}
@@ -0,0 +1,90 @@
package org.dcache.chimera.nfsv41.door;

import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.dcache.nfs.v4.RoundRobinStripingPattern;
import org.dcache.nfs.v4.StripingPattern;
import org.dcache.nfs.v4.xdr.deviceid4;
import org.dcache.nfs.v4.xdr.nfs4_prot;
import org.dcache.utils.Bytes;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;

/**
*
* @author tigran
*/
public class PoolDeviceMapTest {

private final StripingPattern<InetSocketAddress[]> _stripingPattern
= new RoundRobinStripingPattern<>();

private PoolDeviceMap _poolDeviceMap;

@Before
public void setUp() {
_poolDeviceMap = new PoolDeviceMap();
}

@Test
public void testGetMissingById() {
deviceid4 id = deviceidOf(1);
NFSv41Door.PoolDS ds = _poolDeviceMap.getByDeviceId(id);
assertNull("Got not existing", ds);
}

@Test
public void testGetMissingByName() {
String name = "somePool";
NFSv41Door.PoolDS ds = _poolDeviceMap.getByPoolName(name);
assertNull("Got not existing", ds);
}

@Test
public void testGetExisting() throws UnknownHostException {
String name = "somePool";
deviceid4 id1 = deviceidOf(1);
InetSocketAddress[] ip = new InetSocketAddress[]{new InetSocketAddress(0)};

NFSv41Door.PoolDS ds1 = new NFSv41Door.PoolDS(id1, _stripingPattern, ip);

_poolDeviceMap.add(name, ds1);
NFSv41Door.PoolDS ds;
ds = _poolDeviceMap.getByDeviceId(id1);
assertNotNull("Can't get existing by id", ds);

ds = _poolDeviceMap.getByPoolName(name);
assertNotNull("Can't get existing by name", ds);
}

@Test
public void testUpdateExisting() throws UnknownHostException {
String name = "somePool";
deviceid4 id1 = deviceidOf(1);
deviceid4 id2 = deviceidOf(2);
InetSocketAddress[] ip = new InetSocketAddress[]{new InetSocketAddress(0)};

NFSv41Door.PoolDS ds1 = new NFSv41Door.PoolDS(id1, _stripingPattern, ip);
NFSv41Door.PoolDS ds2 = new NFSv41Door.PoolDS(id2, _stripingPattern, ip);

_poolDeviceMap.add(name, ds1);
_poolDeviceMap.add(name, ds2);

NFSv41Door.PoolDS ds;
ds = _poolDeviceMap.getByDeviceId(id1);
assertNull("Update did not invalidate old id", ds);

ds = _poolDeviceMap.getByPoolName(name);
assertNotNull("Can't get updated by name", ds);
}

private static deviceid4 deviceidOf(int id) {
byte[] deviceidBytes = new byte[nfs4_prot.NFS4_DEVICEID4_SIZE];
Bytes.putInt(deviceidBytes, 0, id);

return new deviceid4(deviceidBytes);
}
}

0 comments on commit 09769a3

Please sign in to comment.