use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.
the class HelixAccountServiceTest method testReadBadZNRecordCase5.
/**
* Tests reading {@link ZNRecord} from {@link HelixPropertyStore}, where the {@link ZNRecord} has a map field
* ({@link LegacyMetadataStore#ACCOUNT_METADATA_MAP_KEY}: accountMap), and accountMap contains
* ("accountId": badAccountJsonString). This is a NOT good {@link ZNRecord} format that should fail fetch or update.
* @throws Exception Any unexpected exception.
*/
@Test
public void testReadBadZNRecordCase5() throws Exception {
Map<String, String> mapValue = new HashMap<>();
mapValue.put(String.valueOf(refAccount.getId()), BAD_ACCOUNT_METADATA_STRING);
ZNRecord zNRecord = null;
if (useNewZNodePath) {
String blobID = RouterStore.writeAccountMapToRouter(mapValue, mockRouter);
List<String> list = Collections.singletonList(new RouterStore.BlobIDAndVersion(blobID, 1).toJson());
zNRecord = makeZNRecordWithListField(null, RouterStore.ACCOUNT_METADATA_BLOB_IDS_LIST_KEY, list);
} else {
zNRecord = makeZNRecordWithMapField(null, LegacyMetadataStore.ACCOUNT_METADATA_MAP_KEY, mapValue);
}
updateAndWriteZNRecord(zNRecord, false);
}
use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.
the class HelixAccountServiceTest method testAddContainer.
/**
* Test adding container to an existing account.
*/
@Test
public void testAddContainer() throws Exception {
assumeTrue(!useNewZNodePath);
accountService = mockHelixAccountServiceFactory.getAccountService();
assertEquals("The number of account in HelixAccountService is incorrect", 0, accountService.getAllAccounts().size());
// create three containers for testing
Container activeContainer = new ContainerBuilder((short) (refContainer.getId() + 1), "active", Container.ContainerStatus.ACTIVE, "", refAccount.getId()).build();
Container inactiveContainer = new ContainerBuilder((short) (refContainer.getId() + 2), "inactive", Container.ContainerStatus.INACTIVE, "", refAccount.getId()).build();
Container deleteInProgressContainer1 = new ContainerBuilder((short) (refContainer.getId() + 3), "within-retention-period", ContainerStatus.DELETE_IN_PROGRESS, "", refAccount.getId()).setDeleteTriggerTime(System.currentTimeMillis()).build();
Container deleteInProgressContainer2 = new ContainerBuilder((short) (refContainer.getId() + 4), "past-retention-period", ContainerStatus.DELETE_IN_PROGRESS, "", refAccount.getId()).setDeleteTriggerTime(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(15)).build();
refAccount.updateContainerMap(Arrays.asList(activeContainer, inactiveContainer, deleteInProgressContainer1, deleteInProgressContainer2));
// add an account with single container
accountService.updateAccounts(Collections.singletonList(refAccount));
Container brandNewContainer = new ContainerBuilder(activeContainer).setId(UNKNOWN_CONTAINER_ID).build();
// 1. test invalid input
try {
accountService.updateContainers("", null);
fail("should fail because input is invalid");
} catch (AccountServiceException e) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.BadRequest, e.getErrorCode());
}
// 2. test account is not found
String fakeAccountName = refAccountName + "fake";
Container containerToAdd1 = new ContainerBuilder(UNKNOWN_CONTAINER_ID, "newContainer", ContainerStatus.ACTIVE, "description", refParentAccountId).build();
try {
accountService.updateContainers(fakeAccountName, Collections.singleton(containerToAdd1));
fail("should fail because account is not found");
} catch (AccountServiceException e) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.NotFound, e.getErrorCode());
}
// 3. test containers already exist with INACTIVE and DELETE_IN_PROGRESS state
Container duplicateInactiveContainer = new ContainerBuilder(inactiveContainer).setId(UNKNOWN_CONTAINER_ID).build();
Container duplicateDeleteInProgressContainer1 = new ContainerBuilder(deleteInProgressContainer1).setId(UNKNOWN_CONTAINER_ID).build();
Container duplicateDeleteInProgressContainer2 = new ContainerBuilder(deleteInProgressContainer2).setId(UNKNOWN_CONTAINER_ID).build();
try {
accountService.updateContainers(refAccountName, Collections.singleton(duplicateInactiveContainer));
fail("Should fail because the existing container has been marked INACTIVE.");
} catch (AccountServiceException ase) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.ResourceHasGone, ase.getErrorCode());
}
try {
accountService.updateContainers(refAccountName, Collections.singleton(duplicateDeleteInProgressContainer1));
fail("Should fail because the existing container has been marked DELETE_IN_PROGRESS.");
} catch (AccountServiceException ase) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.MethodNotAllowed, ase.getErrorCode());
}
try {
accountService.updateContainers(refAccountName, Collections.singleton(duplicateDeleteInProgressContainer2));
fail("Should fail because the existing container has been marked DELETE_IN_PROGRESS and past retention time");
} catch (AccountServiceException ase) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.ResourceHasGone, ase.getErrorCode());
}
// 4. test conflict container (new container with existing name but different attributes)
Container conflictContainer = new ContainerBuilder(brandNewContainer).setBackupEnabled(true).build();
try {
accountService.updateContainers(refAccountName, Collections.singleton(conflictContainer));
fail("should fail because there is a conflicting container");
} catch (AccountServiceException e) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.ResourceConflict, e.getErrorCode());
}
// 5. test adding same container twice, should be no-op and return result should include container with id
Collection<Container> addedContainers = accountService.updateContainers(refAccountName, Collections.singleton(brandNewContainer));
assertEquals("Mismatch in return count", 1, addedContainers.size());
for (Container container : addedContainers) {
assertEquals("Mismatch in account id", refAccountId, container.getParentAccountId());
assertEquals("Mismatch in container id", activeContainer.getId(), container.getId());
assertEquals("Mismatch in container name", activeContainer.getName(), container.getName());
}
// 6. test adding a different container (failure case due to ZK update failure)
MockHelixPropertyStore<ZNRecord> mockHelixStore = mockHelixAccountServiceFactory.getHelixStore(ZK_CONNECT_STRING, storeConfig);
mockHelixStore.setExceptionDuringUpdater(true);
try {
accountService.updateContainers(refAccountName, Collections.singleton(containerToAdd1));
fail("should fail because exception occurs when updating ZK");
} catch (AccountServiceException e) {
assertEquals("Mismatch in error code", AccountServiceErrorCode.InternalError, e.getErrorCode());
}
mockHelixStore.setExceptionDuringUpdater(false);
// 7. test adding 2 different containers (success case)
Container containerToAdd2 = new ContainerBuilder(UNKNOWN_CONTAINER_ID, "newContainer2", ContainerStatus.ACTIVE, "description", refParentAccountId).build();
addedContainers = accountService.updateContainers(refAccountName, Arrays.asList(containerToAdd1, containerToAdd2));
for (Container container : addedContainers) {
assertEquals("Mismatch in account id", refAccountId, container.getParentAccountId());
}
}
use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.
the class HelixAccountServiceTest method testReadBadZNRecordCase6.
/**
* Tests reading {@link ZNRecord} from {@link HelixPropertyStore}, where the {@link ZNRecord} has an invalid account
* record and a valid account record. This is a NOT good {@link ZNRecord} format and it should fail fetch or update
* operations, with none of the record should be read.
* @throws Exception Any unexpected exception.
*/
@Test
public void testReadBadZNRecordCase6() throws Exception {
ZNRecord zNRecord = new ZNRecord(String.valueOf(System.currentTimeMillis()));
Map<String, String> accountMap = new HashMap<>();
accountMap.put(String.valueOf(refAccount.getId()), objectMapper.writeValueAsString(new AccountBuilder(refAccount).snapshotVersion(refAccount.getSnapshotVersion() + 1).build()));
accountMap.put(String.valueOf(refAccount.getId() + 1), BAD_ACCOUNT_METADATA_STRING);
if (useNewZNodePath) {
String blobID = RouterStore.writeAccountMapToRouter(accountMap, mockRouter);
List<String> list = Collections.singletonList(new RouterStore.BlobIDAndVersion(blobID, 1).toJson());
zNRecord.setListField(RouterStore.ACCOUNT_METADATA_BLOB_IDS_LIST_KEY, list);
} else {
zNRecord.setMapField(LegacyMetadataStore.ACCOUNT_METADATA_MAP_KEY, accountMap);
}
updateAndWriteZNRecord(zNRecord, false);
}
use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.
the class HelixHealthReportAggregationTaskTest method testAggregationTask.
/**
* Test {@link HelixHealthReportAggregatorTask#run()} method.
* @throws Exception
*/
@Test
public void testAggregationTask() throws Exception {
int port = 10000;
int numNode = 3;
for (StatsReportType type : StatsReportType.values()) {
initializeNodeReports(type, numNode, port);
String healthReportName = type == StatsReportType.ACCOUNT_REPORT ? HEALTH_REPORT_NAME_ACCOUNT : HEALTH_REPORT_NAME_PARTITION;
String statsFieldName = type == StatsReportType.ACCOUNT_REPORT ? STATS_FIELD_NAME_ACCOUNT : STATS_FIELD_NAME_PARTITION;
task = new HelixHealthReportAggregatorTask(mockHelixManager, RELEVANT_PERIOD_IN_MINUTES, healthReportName, statsFieldName, type, null, clusterMapConfig, mockTime);
task.run();
// Verify the targeted znode has value, don't worry about the correctness of the value, it's verified by other tests.
Stat stat = new Stat();
ZNRecord record = mockHelixManager.getHelixPropertyStore().get(String.format("/%s%s", HelixHealthReportAggregatorTask.AGGREGATED_REPORT_PREFIX, healthReportName), stat, AccessOption.PERSISTENT);
Assert.assertNotNull(record);
Assert.assertNotNull(record.getSimpleField(HelixHealthReportAggregatorTask.VALID_SIZE_FIELD_NAME));
Assert.assertNotNull(record.getSimpleField(HelixHealthReportAggregatorTask.RAW_VALID_SIZE_FIELD_NAME));
}
}
use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.
the class HelixBootstrapUpgradeToolTest method testGeneratePartitionOverrideMapFromStaticFile.
/**
* Test that bootstrap tool is able to generate partition override map from static file (if adminConfigFilePath is not
* null). It tests both success and failure cases.
* @throws Exception
*/
@Test
public void testGeneratePartitionOverrideMapFromStaticFile() throws Exception {
List<PartitionId> writablePartitions = new ArrayList<>(testPartitionLayout.getPartitionLayout().getWritablePartitions(null));
int partitionCount = testPartitionLayout.getPartitionCount();
Utils.writeJsonObjectToFile(zkJson, zkLayoutPath);
Utils.writeJsonObjectToFile(testHardwareLayout.getHardwareLayout().toJSONObject(), hardwareLayoutPath);
Utils.writeJsonObjectToFile(testPartitionLayout.getPartitionLayout().toJSONObject(), partitionLayoutPath);
Utils.writeStringToFile(String.valueOf(partitionCount + 1), adminConfigFilePath);
// failure case 1: partition id is out of valid range
try {
HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath, CLUSTER_NAME_PREFIX, dcStr, false, new String[] { ClusterMapUtils.PARTITION_OVERRIDE_STR }, adminConfigFilePath);
fail("should fail because input partition id is out of valid range");
} catch (IllegalArgumentException e) {
// expected
}
// failure case 2: partition id is non-numeric
Utils.writeStringToFile("non-numeric", adminConfigFilePath);
try {
HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath, CLUSTER_NAME_PREFIX, dcStr, false, new String[] { ClusterMapUtils.PARTITION_OVERRIDE_STR }, adminConfigFilePath);
fail("should fail because input partition id is not numeric");
} catch (NumberFormatException e) {
// expected
}
// success case: mark 1/10 partitions as RO
Collections.shuffle(writablePartitions);
int readOnlyCount = writablePartitions.size() / 10;
StringBuilder sb = new StringBuilder();
Set<String> readOnlyInFile = new HashSet<>();
for (int i = 0; i < readOnlyCount; ++i) {
sb.append(writablePartitions.get(i).toPathString()).append(",");
readOnlyInFile.add(writablePartitions.get(i).toPathString());
}
Utils.writeStringToFile(sb.toString(), adminConfigFilePath);
HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath, CLUSTER_NAME_PREFIX, dcStr, false, new String[] { ClusterMapUtils.PARTITION_OVERRIDE_STR }, adminConfigFilePath);
// verify overridden partitions in Helix
for (ZkInfo zkInfo : dcsToZkInfo.values()) {
HelixPropertyStore<ZNRecord> propertyStore = CommonUtils.createHelixPropertyStore("localhost:" + zkInfo.getPort(), propertyStoreConfig, Collections.singletonList(propertyStoreConfig.rootPath));
ZNRecord zNRecord = propertyStore.get(ClusterMapUtils.PARTITION_OVERRIDE_ZNODE_PATH, null, AccessOption.PERSISTENT);
if (!activeDcSet.contains(zkInfo.getDcName())) {
assertNull(zNRecord);
} else {
assertNotNull(zNRecord);
Map<String, Map<String, String>> overridePartition = zNRecord.getMapFields();
Set<String> readOnlyInDc = new HashSet<>();
for (Map.Entry<String, Map<String, String>> entry : overridePartition.entrySet()) {
if (entry.getValue().get(ClusterMapUtils.PARTITION_STATE).equals(ClusterMapUtils.READ_ONLY_STR)) {
readOnlyInDc.add(entry.getKey());
}
}
// Verify ReadOnly partitions in DC match that in static file
assertEquals("Mismatch in ReadOnly partitions for static file and propertyStore", readOnlyInFile, readOnlyInDc);
}
}
}
Aggregations