/** * Creates the serialized value of the object and stores this in ZooKeeper under the path. It * updates the lastStatusVersion. It does not set a watcher for the path. */ private void updateCoordinateData() throws CoordinateMissingException, CloudnameException { if (!started.get()) { throw new IllegalStateException("Not started."); } if (!zkClient.isConnected()) { throw new CloudnameException("No proper connection with zookeeper."); } synchronized (lastStatusVersionMonitor) { try { Stat stat = zkClient .getZookeeper() .setData( path, zkCoordinateData.snapshot().serialize().getBytes(Util.CHARSET_NAME), lastStatusVersion); LOG.fine("Updated coordinate, latest version is " + stat.getVersion()); lastStatusVersion = stat.getVersion(); } catch (KeeperException.NoNodeException e) { throw new CoordinateMissingException("Coordinate does not exist " + path); } catch (KeeperException e) { throw new CloudnameException( "ZooKeeper errror in updateCoordinateData: " + e.getMessage(), e); } catch (UnsupportedEncodingException e) { throw new CloudnameException(e); } catch (InterruptedException e) { throw new CloudnameException(e); } catch (IOException e) { throw new CloudnameException(e); } } }
private static boolean checkStale(SolrZkClient zkClient, String zkPath, int currentVersion) { if (zkPath == null) return false; try { Stat stat = zkClient.exists(zkPath, null, true); if (stat == null) { if (currentVersion > -1) return true; return false; } if (stat.getVersion() > currentVersion) { log.info( zkPath + " is stale will need an update from {} to {}", currentVersion, stat.getVersion()); return true; } return false; } catch (KeeperException.NoNodeException nne) { // no problem } catch (KeeperException e) { log.error("error refreshing solrconfig ", e); } catch (InterruptedException e) { Thread.currentThread().isInterrupted(); } return false; }
/** * Deletes an existing unassigned node that is in the specified state for the specified region. * * <p>If a node does not already exist for this region, a {@link NoNodeException} will be thrown. * * <p>No watcher is set whether this succeeds or not. * * <p>Returns false if the node was not in the proper state but did exist. * * <p>This method is used during table disables when a region finishes successfully closing. This * is the Master acknowledging completion of the specified regions transition to being closed. * * @param zkw zk reference * @param regionName region to be deleted from zk * @param expectedState state region must be in for delete to complete * @param expectedVersion of the znode that is to be deleted. If expectedVersion need not be * compared while deleting the znode pass -1 * @throws KeeperException if unexpected zookeeper exception * @throws KeeperException.NoNodeException if node does not exist */ public static boolean deleteNode( ZooKeeperWatcher zkw, String regionName, EventType expectedState, int expectedVersion) throws KeeperException, KeeperException.NoNodeException { LOG.debug( zkw.prefix( "Deleting existing unassigned " + "node for " + regionName + " that is in expected state " + expectedState)); String node = getNodeName(zkw, regionName); zkw.sync(node); Stat stat = new Stat(); byte[] bytes = ZKUtil.getDataNoWatch(zkw, node, stat); if (bytes == null) { // If it came back null, node does not exist. throw KeeperException.create(Code.NONODE); } RegionTransitionData data = RegionTransitionData.fromBytes(bytes); if (!data.getEventType().equals(expectedState)) { LOG.warn( zkw.prefix( "Attempting to delete unassigned " + "node " + regionName + " in " + expectedState + " state but node is in " + data.getEventType() + " state")); return false; } if (expectedVersion != -1 && stat.getVersion() != expectedVersion) { LOG.warn( "The node we are trying to delete is not the expected one. " + "Got a version mismatch"); return false; } synchronized (zkw.getNodes()) { // TODO: Does this go here or only if we successfully delete node? zkw.getNodes().remove(node); if (!ZKUtil.deleteNode(zkw, node, stat.getVersion())) { LOG.warn( zkw.prefix( "Attempting to delete " + "unassigned node in " + expectedState + " state but " + "after verifying it was in OPENED state, we got a version mismatch")); return false; } LOG.debug( zkw.prefix( "Successfully deleted unassigned node for region " + regionName + " in expected state " + expectedState)); return true; } }
@Test public void testSyncGet() { String className = TestHelper.getTestClassName(); String methodName = TestHelper.getTestMethodName(); String testName = className + "_" + methodName; System.out.println("START " + testName + " at " + new Date(System.currentTimeMillis())); String path = String.format("/%s/%s", testName, "msg_0"); ZNRecord record = new ZNRecord("msg_0"); ZkBaseDataAccessor<ZNRecord> accessor = new ZkBaseDataAccessor<ZNRecord>(_gZkClient); Stat stat = new Stat(); ZNRecord getRecord = accessor.get(path, stat, 0); Assert.assertNull(getRecord); try { accessor.get(path, stat, AccessOption.THROW_EXCEPTION_IFNOTEXIST); Assert.fail("Should throw exception if not exist"); } catch (Exception e) { // OK } boolean success = accessor.create(path, record, AccessOption.PERSISTENT); Assert.assertTrue(success); getRecord = accessor.get(path, stat, 0); Assert.assertNotNull(getRecord); Assert.assertEquals(getRecord.getId(), "msg_0"); Assert.assertEquals(stat.getVersion(), 0); record.setSimpleField("key0", "value0"); success = accessor.set(path, record, AccessOption.PERSISTENT); Assert.assertTrue(success); getRecord = accessor.get(path, stat, 0); Assert.assertNotNull(getRecord); Assert.assertEquals(record.getSimpleFields().size(), 1); Assert.assertNotNull(getRecord.getSimpleField("key0")); Assert.assertEquals(getRecord.getSimpleField("key0"), "value0"); Assert.assertEquals(stat.getVersion(), 1); ZNRecord newRecord = new ZNRecord("msg_0"); newRecord.setSimpleField("key1", "value1"); success = accessor.update(path, new ZNRecordUpdater(newRecord), AccessOption.PERSISTENT); Assert.assertTrue(success); getRecord = accessor.get(path, stat, 0); Assert.assertNotNull(getRecord); Assert.assertEquals(getRecord.getSimpleFields().size(), 2); Assert.assertNotNull(getRecord.getSimpleField("key0")); Assert.assertEquals(getRecord.getSimpleField("key0"), "value0"); Assert.assertNotNull(getRecord.getSimpleField("key1")); Assert.assertEquals(getRecord.getSimpleField("key1"), "value1"); Assert.assertEquals(stat.getVersion(), 2); System.out.println("END " + testName + " at " + new Date(System.currentTimeMillis())); }
/** * Changes the shared value only if its value has not changed since this client last read it. If * the value has changed, the value is not set and this client's view of the value is updated. * i.e. if the value is not successful you can get the updated value by calling {@link * #getValue()}. * * @param newValue the new value to attempt * @return true if the change attempt was successful, false if not. If the change was not * successful, {@link #getValue()} will return the updated value * @throws Exception ZK errors, interruptions, etc. */ public boolean trySetValue(byte[] newValue) throws Exception { Preconditions.checkState(state.get() == State.STARTED, "not started"); try { client.setData().withVersion(stat.getVersion()).forPath(path, newValue); stat.setVersion(stat.getVersion() + 1); value = Arrays.copyOf(newValue, newValue.length); return true; } catch (KeeperException.BadVersionException ignore) { // ignore } readValue(); return false; }
@Test public void testCheckVersion() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/foo"); Stat stat = client.setData().forPath("/foo", "new".getBytes()); // up the version try { client .inTransaction() .check() .withVersion(stat.getVersion() + 1) .forPath("/foo") // force a bad version .and() .create() .forPath("/bar") .and() .commit(); Assert.fail(); } catch (KeeperException.BadVersionException correct) { // correct } Assert.assertNull(client.checkExists().forPath("/bar")); } finally { client.close(); } }
@Test public void testSyncGetStat() { String className = TestHelper.getTestClassName(); String methodName = TestHelper.getTestMethodName(); String testName = className + "_" + methodName; System.out.println("START " + testName + " at " + new Date(System.currentTimeMillis())); String path = String.format("/%s/%s", testName, "msg_0"); ZNRecord record = new ZNRecord("msg_0"); ZkBaseDataAccessor<ZNRecord> accessor = new ZkBaseDataAccessor<ZNRecord>(_gZkClient); Stat stat = accessor.getStat(path, 0); Assert.assertNull(stat); boolean success = accessor.create(path, record, AccessOption.EPHEMERAL); Assert.assertTrue(success); stat = accessor.getStat(path, 0); Assert.assertNotNull(stat); Assert.assertEquals(stat.getVersion(), 0); Assert.assertNotSame(stat.getEphemeralOwner(), 0); System.out.println("END " + testName + " at " + new Date(System.currentTimeMillis())); }
/** * Change the shared value value irrespective of its previous state * * @param newValue new value * @throws Exception ZK errors, interruptions, etc. */ public void setValue(byte[] newValue) throws Exception { Preconditions.checkState(state.get() == State.STARTED, "not started"); client.setData().forPath(path, newValue); stat.setVersion(stat.getVersion() + 1); value = Arrays.copyOf(newValue, newValue.length); }
public void updateRecord(MetaRecord rec) { try { long ts = System.currentTimeMillis(); putRecord(rec, ts); String node = ZKUtil.joinZNode(H2MetaTableTracker.NODE_NAME, Integer.toString(rec.getId())); // setData会异步触发所有机器(包括本机)上的H2MetaTableTracker.nodeDataChanged // 然后触发下列调用: // =>org.h2.engine.Database.updateDatabaseObject(int) // =>org.h2.engine.Database.update(Session, DbObject) // =>org.h2.engine.Database.addMeta0(Session, DbObject, boolean) // =>又到此方法 // 所以会造成循环 synchronized (this) { // 避免setData后立刻触发nodeDataChanged,此时IdVersion还未更新 ZKUtil.setData(watcher, node, Bytes.toBytes(ts)); // setData后watch不见了,所以要继续watch,监听其他人对此node的修改 // ZKUtil.watchAndCheckExists(watcher, node); Stat stat = new Stat(); ZKUtil.getDataAndWatch(watcher, node, stat); // 这里记录下id的最新版本,触发nodeDataChanged时再检查一下是否版本一样, // 如果不大于这里的版本那么就不再执行updateDatabaseObject操作 tracker.updateIdVersion(rec.getId(), stat.getVersion()); } } catch (Exception e) { throw new RuntimeException(e); } }
/** * Try to atomically update a node in ZooKeeper, creating it if it doesn't exist. This is meant to * be used within an optimistic concurrency model. * * @param pathSuffix suffix to use to build path in ZooKeeper. * @param f function used to initialize the node, or transform the data already there. * @return true if node was created/updated, false if a concurrent modification occurred and * succeeded while trying to update/create the node. * @throws Exception */ private boolean tryAtomicUpdate(final String pathSuffix, final NodeFunction f) throws Exception { final String path = buildZookeeperPath(pathSuffix); final Stat stat = zk.checkExists().forPath(path); if (stat == null) { try { zk.create() .creatingParentsIfNeeded() .withMode(CreateMode.PERSISTENT) .forPath(path, f.initialize()); } catch (KeeperException.NodeExistsException e) { LOG.debug("Concurrent creation of " + path + ", retrying", e); return false; } } else { Mod<byte[]> newVal = f.apply(zk.getData().forPath(path)); if (newVal.hasModification()) { try { zk.setData().withVersion(stat.getVersion()).forPath(path, newVal.get()); } catch (KeeperException.BadVersionException e) { LOG.debug("Concurrent update to " + path + ", retrying.", e); return false; } } } return true; }
void trigeWatcher() { try { Stat s = zk.exists("/root", false); // 此处不设置watcher zk.setData("/root", "a".getBytes(), s.getVersion()); // 修改数据时需要提供version,version设为-1表示强制修改 } catch (Exception e) { e.printStackTrace(); } }
protected Stat setData(String path, byte[] data, Stat stat) { try { stat = zooKeeper.setData(path, data, stat.getVersion()); } catch (Exception e) { System.out.println("Exception caught in zk.setData(): " + e); } return stat; }
/** * sync update * * @return: updatedData on success, or null on fail */ public T update( String path, DataUpdater<T> updater, List<String> createPaths, Stat stat, int options) { CreateMode mode = AccessOption.getMode(options); if (mode == null) { LOG.error("Invalid update mode. options: " + options); return null; } boolean retry; T updatedData = null; do { retry = false; try { Stat readStat = new Stat(); T oldData = (T) _zkClient.readData(path, readStat); T newData = updater.update(oldData); Stat setStat = _zkClient.writeDataGetStat(path, newData, readStat.getVersion()); if (stat != null) { DataTree.copyStat(setStat, stat); } updatedData = newData; } catch (ZkBadVersionException e) { retry = true; } catch (ZkNoNodeException e) { // node not exist, try create try { T newData = updater.update(null); RetCode rc = create(path, newData, createPaths, options); switch (rc) { case OK: updatedData = newData; break; case NODE_EXISTS: retry = true; break; default: LOG.error("Fail to update path by creating: " + path); return null; } } catch (Exception e1) { LOG.error("Exception while updating path by creating: " + path, e1); return null; } } catch (Exception e) { LOG.error("Exception while updating path: " + path, e); return null; } } while (retry); return updatedData; }
@PUT @Path("blueprint/{name}") public Response updateBlueprint( @Context UriInfo uri, @PathParam("name") String oldName, ConfigManifest blueprint) { Response res; try { ZooKeeper zk = Controller.getInstance().getZKInstance(); String newName = oldName; if (blueprint.getUrl() != null) { newName = ZookeeperUtil.getBaseURL(blueprint.getUrl().toString()); } else { blueprint.setUrl(uri.getAbsolutePath().toURL()); } byte[] data = JAXBUtil.write(blueprint); Stat stat = zk.exists( CommonConfigurationKeys.ZOOKEEPER_CONFIG_BLUEPRINT_PATH_DEFAULT + '/' + newName, false); if (stat != null && oldName.equals(newName)) { // Update existing blueprint String path = CommonConfigurationKeys.ZOOKEEPER_CONFIG_BLUEPRINT_PATH_DEFAULT + '/' + oldName; zk.delete(path, stat.getVersion()); zk.create( CommonConfigurationKeys.ZOOKEEPER_CONFIG_BLUEPRINT_PATH_DEFAULT + '/' + newName, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } else if (stat != null) { // Conflict in name change throw new WebApplicationException(409); } else { // Create new blueprint try { zk.create( CommonConfigurationKeys.ZOOKEEPER_CONFIG_BLUEPRINT_PATH_DEFAULT + '/' + newName, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException e) { throw new WebApplicationException(409); } } res = Response.noContent().build(); return res; } catch (WebApplicationException e) { throw e; } catch (Exception e) { LOG.error(ExceptionUtil.getStackTrace(e)); throw new WebApplicationException(500); } }
public static void copyStat(Stat from, Stat to) { to.setAversion(from.getAversion()); to.setCtime(from.getCtime()); to.setCversion(from.getCversion()); to.setCzxid(from.getCzxid()); to.setMtime(from.getMtime()); to.setMzxid(from.getMzxid()); to.setPzxid(from.getPzxid()); to.setVersion(from.getVersion()); to.setEphemeralOwner(from.getEphemeralOwner()); to.setDataLength(from.getDataLength()); to.setNumChildren(from.getNumChildren()); }
public Integer getVersion(CuratorFramework zk, String path, boolean watch) throws Exception { String npath = PathUtils.normalize_path(path); Stat stat = null; if (existsNode(zk, npath, watch)) { if (watch) { stat = zk.checkExists().watched().forPath(PathUtils.normalize_path(path)); } else { stat = zk.checkExists().forPath(PathUtils.normalize_path(path)); } return Integer.valueOf(stat.getVersion()); } return null; }
@DELETE @Path("blueprint/{name}") public Response deleteStack(@PathParam("name") String name) { ZooKeeper zk = Controller.getInstance().getZKInstance(); try { String path = ZookeeperUtil.getConfigManifestPath(name); Stat current = zk.exists(path, false); zk.delete(path, current.getVersion()); } catch (Exception e) { LOG.error(ExceptionUtil.getStackTrace(e)); throw new WebApplicationException(500); } Response res = Response.noContent().build(); return res; }
@Test(timeout = 60000) public void testBadVersionOnTwoAllocators() throws Exception { String allocationPath = "/allocation-bad-version"; zkc.get() .create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = new Stat(); byte[] data = zkc.get().getData(allocationPath, false, stat); Versioned<byte[]> allocationData = new Versioned<byte[]>(data, new ZkVersion(stat.getVersion())); SimpleLedgerAllocator allocator1 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); SimpleLedgerAllocator allocator2 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); allocator1.allocate(); // wait until allocated ZKTransaction txn1 = newTxn(); LedgerHandle lh = FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER)); allocator2.allocate(); ZKTransaction txn2 = newTxn(); try { FutureUtils.result(allocator2.tryObtain(txn2, NULL_LISTENER)); fail( "Should fail allocating on second allocator as allocator1 is starting allocating something."); } catch (ZKException zke) { assertEquals(KeeperException.Code.BADVERSION, zke.getKeeperExceptionCode()); } FutureUtils.result(txn1.execute()); Utils.close(allocator1); Utils.close(allocator2); long eid = lh.addEntry("hello world".getBytes()); lh.close(); LedgerHandle readLh = bkc.get() .openLedger(lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes()); Enumeration<LedgerEntry> entries = readLh.readEntries(eid, eid); int i = 0; while (entries.hasMoreElements()) { LedgerEntry entry = entries.nextElement(); assertEquals("hello world", new String(entry.getEntry(), UTF_8)); ++i; } assertEquals(1, i); }
/** * Atomically sets the value to the given updated value if the current value {@code ==} the * expected value. Remember to always check {@link AtomicValue#succeeded()}. * * @param expectedValue the expected value * @param newValue the new value * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue<byte[]> compareAndSet(byte[] expectedValue, byte[] newValue) throws Exception { Stat stat = new Stat(); MutableAtomicValue<byte[]> result = new MutableAtomicValue<byte[]>(null, null, false); boolean createIt = getCurrentValue(result, stat); if (!createIt && Arrays.equals(expectedValue, result.preValue)) { try { client.setData().withVersion(stat.getVersion()).forPath(path, newValue); result.succeeded = true; result.postValue = newValue; } catch (KeeperException.BadVersionException dummy) { result.succeeded = false; } catch (KeeperException.NoNodeException dummy) { result.succeeded = false; } } else { result.succeeded = false; } return result; }
/** * Try to own the task by transitioning the zk node data from UNASSIGNED to OWNED. * * <p>This method is also used to periodically heartbeat the task progress by transitioning the * node from OWNED to OWNED. * * <p> * * @param isFirstTime shows whther it's the first attempt. * @param zkw zk wathcer * @param server name * @param task to own * @param taskZKVersion version of the task in zk * @return non-negative integer value when task can be owned by current region server otherwise -1 */ protected static int attemptToOwnTask( boolean isFirstTime, ZooKeeperWatcher zkw, ServerName server, String task, RecoveryMode mode, int taskZKVersion) { int latestZKVersion = FAILED_TO_OWN_TASK; try { SplitLogTask slt = new SplitLogTask.Owned(server, mode); Stat stat = zkw.getRecoverableZooKeeper().setData(task, slt.toByteArray(), taskZKVersion); if (stat == null) { LOG.warn("zk.setData() returned null for path " + task); SplitLogCounters.tot_wkr_task_heartbeat_failed.incrementAndGet(); return FAILED_TO_OWN_TASK; } latestZKVersion = stat.getVersion(); SplitLogCounters.tot_wkr_task_heartbeat.incrementAndGet(); return latestZKVersion; } catch (KeeperException e) { if (!isFirstTime) { if (e.code().equals(KeeperException.Code.NONODE)) { LOG.warn("NONODE failed to assert ownership for " + task, e); } else if (e.code().equals(KeeperException.Code.BADVERSION)) { LOG.warn("BADVERSION failed to assert ownership for " + task, e); } else { LOG.warn("failed to assert ownership for " + task, e); } } } catch (InterruptedException e1) { LOG.warn( "Interrupted while trying to assert ownership of " + task + " " + StringUtils.stringifyException(e1)); Thread.currentThread().interrupt(); } SplitLogCounters.tot_wkr_task_heartbeat_failed.incrementAndGet(); return FAILED_TO_OWN_TASK; }
public void write(String path, String value) throws InterruptedException, KeeperException { int retries = 0; while (true) { try { Stat stat = zk.exists(path, false); if (stat == null) { zk.create(path, value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } else { zk.setData(path, value.getBytes(CHARSET), stat.getVersion()); } return; } catch (KeeperException.SessionExpiredException e) { throw e; } catch (KeeperException e) { if (retries++ == MAX_RETRIES) { throw e; } // sleep then retry TimeUnit.SECONDS.sleep(RETRY_PERIOD_SECONDS); } } }
/** @see org.storevm.toolkits.session.zookeeper.handler.GetDataHandler#handle() */ @SuppressWarnings("unchecked") @Override public <T> T handle() throws Exception { if (zookeeper != null) { String path = GROUP_NAME + NODE_SEP + id; // 检查指定的Session节点是否存在 Stat stat = zookeeper.exists(path, false); // 如果节点存在则删除之 if (stat != null) { // 查找数据节点是否存在,不存在就创建一个 String dataPath = path + NODE_SEP + key; stat = zookeeper.exists(dataPath, false); if (stat == null) { // 创建数据节点 zookeeper.create(dataPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); if (LOGGER.isInfoEnabled()) { LOGGER.info("创建数据节点完成[" + dataPath + "]"); } } // 在节点上设置数据,所有数据必须可序列化 if (data instanceof Serializable) { int dataNodeVer = -1; if (stat != null) { // 记录数据节点的版本 dataNodeVer = stat.getVersion(); } byte[] arrData = SerializationUtils.serialize(data); stat = zookeeper.setData(dataPath, arrData, dataNodeVer); if (LOGGER.isInfoEnabled()) { LOGGER.info("更新数据节点数据完成[" + dataPath + "][" + data + "]"); } return (T) Boolean.TRUE; } } } return (T) Boolean.FALSE; }
private boolean tryOnce(MutableAtomicValue<byte[]> result, MakeValue makeValue) throws Exception { Stat stat = new Stat(); boolean createIt = getCurrentValue(result, stat); boolean success = false; try { byte[] newValue = makeValue.makeFrom(result.preValue); if (createIt) { client.create().forPath(path, newValue); } else { client.setData().withVersion(stat.getVersion()).forPath(path, newValue); } result.postValue = Arrays.copyOf(newValue, newValue.length); success = true; } catch (KeeperException.NodeExistsException e) { // do Retry } catch (KeeperException.BadVersionException e) { // do Retry } catch (KeeperException.NoNodeException e) { // do Retry } return success; }
public void consume() { while (true) { if (zooKeeper != null) { synchronized (mutex) { try { List<String> children = zooKeeper.getChildren(root, true); if (children.size() == 0) { mutex.wait(); } else { Stat stat = new Stat(); String minElement = root + "/" + min(children); String value = new String(zooKeeper.getData(minElement, false, stat)); System.out.println(value); zooKeeper.delete(minElement, stat.getVersion()); } } catch (KeeperException | InterruptedException e) { e.printStackTrace(); } } } } }
@Test public void testBasicFunctionality() throws Exception { // Check ZNode for timestamp exists (storage instantiation should create it) Stat zNodeStats = zkClient.checkExists().forPath(TIMESTAMP_ZNODE); assertEquals(zNodeStats.getVersion(), 0); // Initial checks assertEquals(storage.getMaxTimestamp(), INITIAL_MAX_TS_VALUE); byte[] data = zkClient.getData().forPath(TIMESTAMP_ZNODE); assertEquals(data.length, BYTES_IN_LONG); // Check new timestamp does not allow negative values... try { storage.updateMaxTimestamp(INITIAL_MAX_TS_VALUE, NEGATIVE_TS); fail(); } catch (IllegalArgumentException e) { LOG.info("Expected exception", e); } // ...nor is less than previous timestamp try { storage.updateMaxTimestamp(1, 0); fail(); } catch (IllegalArgumentException e) { LOG.info("Expected exception", e); } // Check that the original version is still there zNodeStats = zkClient.checkExists().forPath(TIMESTAMP_ZNODE); assertEquals(zNodeStats.getVersion(), 0); // Iterate updating the timestamp and check the final value long previousMaxTimestamp = INITIAL_MAX_TS_VALUE; for (int i = 0; i < ITERATION_COUNT; i++) { long newMaxTimestamp = previousMaxTimestamp + 1_000_000; storage.updateMaxTimestamp(previousMaxTimestamp, newMaxTimestamp); previousMaxTimestamp = newMaxTimestamp; } assertEquals(storage.getMaxTimestamp(), 1_000_000 * ITERATION_COUNT); // Check the znode version has changed accordingly zNodeStats = zkClient.checkExists().forPath(TIMESTAMP_ZNODE); assertEquals(zNodeStats.getVersion(), ITERATION_COUNT); // Check exceptions doThrow(new RuntimeException()).when(storageInternalZKClient).getData(); try { storage.getMaxTimestamp(); fail(); } catch (IOException e) { LOG.info("Expected exception", e); } doThrow(new RuntimeException()).when(storageInternalZKClient).setData(); try { storage.updateMaxTimestamp(INITIAL_MAX_TS_VALUE, INITIAL_MAX_TS_VALUE + 1_000_000); fail(); } catch (IOException e) { LOG.info("Expected exception", e); } // Reset the mock and double-check last result Mockito.reset(storageInternalZKClient); assertEquals(storage.getMaxTimestamp(), 1_000_000 * ITERATION_COUNT); // Finally check the znode version is still the same zNodeStats = zkClient.checkExists().forPath(TIMESTAMP_ZNODE); assertEquals(zNodeStats.getVersion(), ITERATION_COUNT); }
@Test public void testShouldNotCompeleteOpenedEntityGroupSuccessfullyIfVersionMismatches() throws Exception { EntityGroup entityGroup = null; try { int testIndex = 0; TEST_UTIL.getHBaseTestingUtility().startMiniZKCluster(); TEST_UTIL .getConfiguration() .set( FConstants.ZOOKEEPER_QUORUM, TEST_UTIL.getConfiguration().get(HConstants.ZOOKEEPER_QUORUM)); TEST_UTIL .getConfiguration() .set( FConstants.ZOOKEEPER_CLIENT_PORT, TEST_UTIL.getConfiguration().get(HConstants.ZOOKEEPER_CLIENT_PORT)); final Server server = new MockServer(TEST_UTIL); FTable htd = FMetaTestUtil.makeTable( "testShouldNotCompeleteOpenedEntityGroupSuccessfullyIfVersionMismatches"); EntityGroupInfo egi = new EntityGroupInfo( Bytes.toBytes(htd.getTableName()), Bytes.toBytes(testIndex), Bytes.toBytes(testIndex + 1)); entityGroup = EntityGroup.createEntityGroup(egi, TEST_UTIL.getConfiguration(), htd, null); assertNotNull(entityGroup); AssignmentManager am = Mockito.mock(AssignmentManager.class); EntityGroupStates rsm = Mockito.mock(EntityGroupStates.class); Mockito.doReturn(rsm).when(am).getEntityGroupStates(); when(rsm.isEntityGroupInTransition(egi)).thenReturn(false); when(rsm.getEntityGroupState(egi)) .thenReturn( new EntityGroupState( entityGroup.getEntityGroupInfo(), EntityGroupState.State.OPEN, System.currentTimeMillis(), server.getServerName())); // create a node with OPENED state zkw = WaspTestingUtility.createAndForceNodeToOpenedState( TEST_UTIL, entityGroup, server.getServerName()); when(am.getZKTable()).thenReturn(new ZKTable(zkw)); Stat stat = new Stat(); String nodeName = ZKAssign.getNodeName(zkw, entityGroup.getEntityGroupInfo().getEncodedName()); ZKUtil.getDataAndWatch(zkw, nodeName, stat); // use the version for the OpenedEntityGroupHandler OpenedEntityGroupHandler handler = new OpenedEntityGroupHandler( server, am, entityGroup.getEntityGroupInfo(), server.getServerName(), stat.getVersion()); // Once again overwrite the same znode so that the version changes. ZKAssign.transitionNode( zkw, entityGroup.getEntityGroupInfo(), server.getServerName(), EventType.FSERVER_ZK_ENTITYGROUP_OPENED, EventType.FSERVER_ZK_ENTITYGROUP_OPENED, stat.getVersion()); // Should not invoke assignmentmanager.entityGroupOnline. If it is // invoked as per current mocking it will throw null pointer exception. boolean expectedException = false; try { handler.process(); } catch (Exception e) { expectedException = true; } assertFalse("The process method should not throw any exception.", expectedException); List<String> znodes = ZKUtil.listChildrenAndWatchForNewChildren(zkw, zkw.assignmentZNode); String entityGroupName = znodes.get(0); assertEquals( "The entityGroup should not be opened successfully.", entityGroupName, entityGroup.getEntityGroupInfo().getEncodedName()); } finally { EntityGroup.closeEntityGroup(entityGroup); TEST_UTIL.getHBaseTestingUtility().shutdownMiniZKCluster(); } }
/** * try to grab a 'lock' on the task zk node to own and execute the task. * * <p> * * @param path zk node for the task */ private void grabTask(String path) { Stat stat = new Stat(); byte[] data; synchronized (grabTaskLock) { currentTask = path; workerInGrabTask = true; if (Thread.interrupted()) { return; } } try { try { if ((data = ZKUtil.getDataNoWatch(watcher, path, stat)) == null) { SplitLogCounters.tot_wkr_failed_to_grab_task_no_data.incrementAndGet(); return; } } catch (KeeperException e) { LOG.warn("Failed to get data for znode " + path, e); SplitLogCounters.tot_wkr_failed_to_grab_task_exception.incrementAndGet(); return; } SplitLogTask slt; try { slt = SplitLogTask.parseFrom(data); } catch (DeserializationException e) { LOG.warn("Failed parse data for znode " + path, e); SplitLogCounters.tot_wkr_failed_to_grab_task_exception.incrementAndGet(); return; } if (!slt.isUnassigned()) { SplitLogCounters.tot_wkr_failed_to_grab_task_owned.incrementAndGet(); return; } currentVersion = attemptToOwnTask( true, watcher, server.getServerName(), path, slt.getMode(), stat.getVersion()); if (currentVersion < 0) { SplitLogCounters.tot_wkr_failed_to_grab_task_lost_race.incrementAndGet(); return; } if (ZKSplitLog.isRescanNode(watcher, currentTask)) { ZkSplitLogWorkerCoordination.ZkSplitTaskDetails splitTaskDetails = new ZkSplitLogWorkerCoordination.ZkSplitTaskDetails(); splitTaskDetails.setTaskNode(currentTask); splitTaskDetails.setCurTaskZKVersion(new MutableInt(currentVersion)); endTask( new SplitLogTask.Done(server.getServerName(), slt.getMode()), SplitLogCounters.tot_wkr_task_acquired_rescan, splitTaskDetails); return; } LOG.info("worker " + server.getServerName() + " acquired task " + path); SplitLogCounters.tot_wkr_task_acquired.incrementAndGet(); getDataSetWatchAsync(); submitTask(path, slt.getMode(), currentVersion, reportPeriod); // after a successful submit, sleep a little bit to allow other RSs to grab the rest tasks try { int sleepTime = RandomUtils.nextInt(500) + 500; Thread.sleep(sleepTime); } catch (InterruptedException e) { LOG.warn("Interrupted while yielding for other region servers", e); Thread.currentThread().interrupt(); } } finally { synchronized (grabTaskLock) { workerInGrabTask = false; // clear the interrupt from stopTask() otherwise the next task will // suffer Thread.interrupted(); } } }
/** * Private method that actually performs unassigned node transitions. * * <p>Attempts to transition the unassigned node for the specified region from the expected state * to the state in the specified transition data. * * <p>Method first reads existing data and verifies it is in the expected state. If the node does * not exist or the node is not in the expected state, the method returns -1. If the transition is * successful, the version number of the node following the transition is returned. * * <p>If the read state is what is expected, it attempts to write the new state and data into the * node. When doing this, it includes the expected version (determined when the existing state was * verified) to ensure that only one transition is successful. If there is a version mismatch, the * method returns -1. * * <p>If the write is successful, no watch is set and the method returns true. * * @param zkw zk reference * @param region region to be transitioned to opened * @param serverName server event originates from * @param endState state to transition node to if all checks pass * @param beginState state the node must currently be in to do transition * @param expectedVersion expected version of data before modification, or -1 * @return version of node after transition, -1 if unsuccessful transition * @throws KeeperException if unexpected zookeeper exception */ public static int transitionNode( ZooKeeperWatcher zkw, HRegionInfo region, String serverName, EventType beginState, EventType endState, int expectedVersion) throws KeeperException { String encoded = region.getEncodedName(); if (LOG.isDebugEnabled()) { LOG.debug( zkw.prefix( "Attempting to transition node " + HRegionInfo.prettyPrint(encoded) + " from " + beginState.toString() + " to " + endState.toString())); } String node = getNodeName(zkw, encoded); zkw.sync(node); // Read existing data of the node Stat stat = new Stat(); byte[] existingBytes = ZKUtil.getDataNoWatch(zkw, node, stat); if (existingBytes == null) { // Node no longer exists. Return -1. It means unsuccessful transition. return -1; } RegionTransitionData existingData = RegionTransitionData.fromBytes(existingBytes); // Verify it is the expected version if (expectedVersion != -1 && stat.getVersion() != expectedVersion) { LOG.warn( zkw.prefix( "Attempt to transition the " + "unassigned node for " + encoded + " from " + beginState + " to " + endState + " failed, " + "the node existed but was version " + stat.getVersion() + " not the expected version " + expectedVersion)); return -1; } // Verify it is in expected state if (!existingData.getEventType().equals(beginState)) { LOG.warn( zkw.prefix( "Attempt to transition the " + "unassigned node for " + encoded + " from " + beginState + " to " + endState + " failed, " + "the node existed but was in the state " + existingData.getEventType() + " set by the server " + existingData.getServerName())); return -1; } // Write new data, ensuring data has not changed since we last read it try { RegionTransitionData data = new RegionTransitionData(endState, region.getRegionName(), serverName); if (!ZKUtil.setData(zkw, node, data.getBytes(), stat.getVersion())) { LOG.warn( zkw.prefix( "Attempt to transition the " + "unassigned node for " + encoded + " from " + beginState + " to " + endState + " failed, " + "the node existed and was in the expected state but then when " + "setting data we got a version mismatch")); return -1; } if (LOG.isDebugEnabled()) { LOG.debug( zkw.prefix( "Successfully transitioned node " + encoded + " from " + beginState + " to " + endState)); } return stat.getVersion() + 1; } catch (KeeperException.NoNodeException nne) { LOG.warn( zkw.prefix( "Attempt to transition the " + "unassigned node for " + encoded + " from " + beginState + " to " + endState + " failed, " + "the node existed and was in the expected state but then when " + "setting data it no longer existed")); return -1; } }
/** * Allows you to do * * <pre><code>node.data = "some data: ${var}"</code></pre> * * in Groovy code. * * @param data * @throws InterruptedException * @throws KeeperException */ public void setData(Object data) throws InterruptedException, KeeperException { zookeeper.setData(path, serialize(data), stat.getVersion()); }
@Test(timeout = 60000) public void testSuccessAllocatorShouldDeleteUnusedledger() throws Exception { String allocationPath = "/allocation-delete-unused-ledger"; zkc.get() .create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = new Stat(); byte[] data = zkc.get().getData(allocationPath, false, stat); Versioned<byte[]> allocationData = new Versioned<byte[]>(data, new ZkVersion(stat.getVersion())); SimpleLedgerAllocator allocator1 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); allocator1.allocate(); // wait until allocated ZKTransaction txn1 = newTxn(); LedgerHandle lh1 = FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER)); // Second allocator kicks in stat = new Stat(); data = zkc.get().getData(allocationPath, false, stat); allocationData = new Versioned<byte[]>(data, new ZkVersion(stat.getVersion())); SimpleLedgerAllocator allocator2 = new SimpleLedgerAllocator( allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc); allocator2.allocate(); // wait until allocated ZKTransaction txn2 = newTxn(); LedgerHandle lh2 = FutureUtils.result(allocator2.tryObtain(txn2, NULL_LISTENER)); // should fail to commit txn1 as version is changed by second allocator try { FutureUtils.result(txn1.execute()); fail( "Should fail commit obtaining ledger handle from first allocator as allocator is modified by second allocator."); } catch (ZKException ke) { // as expected } FutureUtils.result(txn2.execute()); Utils.close(allocator1); Utils.close(allocator2); // ledger handle should be deleted try { lh1.close(); fail("LedgerHandle allocated by allocator1 should be deleted."); } catch (BKException bke) { // as expected } try { bkc.get() .openLedger(lh1.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes()); fail("LedgerHandle allocated by allocator1 should be deleted."); } catch (BKException.BKNoSuchLedgerExistsException nslee) { // as expected } long eid = lh2.addEntry("hello world".getBytes()); lh2.close(); LedgerHandle readLh = bkc.get() .openLedger( lh2.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes()); Enumeration<LedgerEntry> entries = readLh.readEntries(eid, eid); int i = 0; while (entries.hasMoreElements()) { LedgerEntry entry = entries.nextElement(); assertEquals("hello world", new String(entry.getEntry(), UTF_8)); ++i; } assertEquals(1, i); }