/** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest */ HashMap<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { HashMap<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for(Op op: multiRequest) { String path = op.getPath(); try { ChangeRecord cr = getRecordForPath(path); if (cr != null) { pendingChangeRecords.put(path, cr); } } catch (KeeperException.NoNodeException e) { // ignore this one } } return pendingChangeRecords; }
/** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest * @return a map that contains previously existed records that probably need to be * rolled back in any failure. */ HashMap<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { HashMap<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for (Op op : multiRequest) { String path = op.getPath(); ChangeRecord cr = getOutstandingChange(path); // only previously existing records need to be rolled back. if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } String parentPath = path.substring(0, lastSlash); ChangeRecord parentCr = getOutstandingChange(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } return pendingChangeRecords; }
/** * This test checks that a successful multi will change outstanding record * and failed multi shouldn't change outstanding record. */ @Test public void testMultiOutstandingChange() throws Exception { zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); process(Arrays.asList( Op.setData("/foo", new byte[0], -1))); ChangeRecord cr = zks.outstandingChangesForPath.get("/foo"); Assert.assertNotNull("Change record wasn't set", cr); Assert.assertEquals("Record zxid wasn't set correctly", 1, cr.zxid); process(Arrays.asList( Op.delete("/foo", -1))); cr = zks.outstandingChangesForPath.get("/foo"); Assert.assertEquals("Record zxid wasn't set correctly", 2, cr.zxid); // It should fail and shouldn't change outstanding record. process(Arrays.asList( Op.delete("/foo", -1))); cr = zks.outstandingChangesForPath.get("/foo"); // zxid should still be previous result because record's not changed. Assert.assertEquals("Record zxid wasn't set correctly", 2, cr.zxid); }
/** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest * @return a map that contains previously existed records that probably need to be * rolled back in any failure. */ private Map<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { Map<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for (Op op : multiRequest) { String path = op.getPath(); ChangeRecord cr = getOutstandingChange(path); // only previously existing records need to be rolled back. if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } String parentPath = path.substring(0, lastSlash); ChangeRecord parentCr = getOutstandingChange(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } return pendingChangeRecords; }
/** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest * @return a map that contains previously existed records that probably need to be * rolled back in any failure. */ private Map<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { HashMap<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for (Op op : multiRequest) { String path = op.getPath(); ChangeRecord cr = getOutstandingChange(path); // only previously existing records need to be rolled back. if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } String parentPath = path.substring(0, lastSlash); ChangeRecord parentCr = getOutstandingChange(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } return pendingChangeRecords; }
/** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest */ HashMap<String, ChangeRecord> getPendingChanges(MultiTransactionRecord multiRequest) { HashMap<String, ChangeRecord> pendingChangeRecords = new HashMap<String, ChangeRecord>(); for(Op op: multiRequest) { String path = op.getPath(); try { ChangeRecord cr = getRecordForPath(path); if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } String parentPath = path.substring(0, lastSlash); ChangeRecord parentCr = getRecordForPath(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } catch (KeeperException.NoNodeException e) { // ignore this one } } return pendingChangeRecords; }
private ChangeRecord getOutstandingChange(String path) { synchronized (zks.outstandingChanges) { return zks.outstandingChangesForPath.get(path); } }
void addChangeRecord(ChangeRecord c) { synchronized (zks.outstandingChanges) { zks.outstandingChanges.add(c); zks.outstandingChangesForPath.put(c.path, c); } }
private void addChangeRecord(ChangeRecord c) { synchronized (zks.outstandingChanges) { zks.outstandingChanges.add(c); zks.outstandingChangesForPath.put(c.path, c); } }
private void pRequest2TxnCreate(int type, Request request, Record record, boolean deserialize) throws IOException, KeeperException { if (deserialize) { ByteBufferInputStream.byteBuffer2Record(request.request, record); } int flags; String path; List<ACL> acl; byte[] data; long ttl; if (type == OpCode.createTTL) { CreateTTLRequest createTtlRequest = (CreateTTLRequest)record; flags = createTtlRequest.getFlags(); path = createTtlRequest.getPath(); acl = createTtlRequest.getAcl(); data = createTtlRequest.getData(); ttl = createTtlRequest.getTtl(); } else { CreateRequest createRequest = (CreateRequest)record; flags = createRequest.getFlags(); path = createRequest.getPath(); acl = createRequest.getAcl(); data = createRequest.getData(); ttl = -1; } CreateMode createMode = CreateMode.fromFlag(flags); validateCreateRequest(path, createMode, request, ttl); String parentPath = validatePathForCreate(path, request.sessionId); List<ACL> listACL = fixupACL(path, request.authInfo, acl); ChangeRecord parentRecord = getRecordForPath(parentPath); checkACL(zks, request.cnxn, parentRecord.acl, ZooDefs.Perms.CREATE, request.authInfo, path, listACL); int parentCVersion = parentRecord.stat.getCversion(); if (createMode.isSequential()) { path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion); } validatePath(path, request.sessionId); try { if (getRecordForPath(path) != null) { throw new KeeperException.NodeExistsException(path); } } catch (KeeperException.NoNodeException e) { // ignore this one } boolean ephemeralParent = EphemeralType.get(parentRecord.stat.getEphemeralOwner()) == EphemeralType.NORMAL; if (ephemeralParent) { throw new KeeperException.NoChildrenForEphemeralsException(path); } int newCversion = parentRecord.stat.getCversion()+1; if (type == OpCode.createContainer) { request.setTxn(new CreateContainerTxn(path, data, listACL, newCversion)); } else if (type == OpCode.createTTL) { request.setTxn(new CreateTTLTxn(path, data, listACL, newCversion, ttl)); } else { request.setTxn(new CreateTxn(path, data, listACL, createMode.isEphemeral(), newCversion)); } StatPersisted s = new StatPersisted(); if (createMode.isEphemeral()) { s.setEphemeralOwner(request.sessionId); } parentRecord = parentRecord.duplicate(request.getHdr().getZxid()); parentRecord.childCount++; parentRecord.stat.setCversion(newCversion); addChangeRecord(parentRecord); addChangeRecord(new ChangeRecord(request.getHdr().getZxid(), path, s, 0, listACL)); }